123 lines
5.1 KiB
C#
123 lines
5.1 KiB
C#
using System.Collections.Immutable;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
using NuGet.Converters;
|
|
using NuGet.Frameworks;
|
|
using NuGet.Models;
|
|
using NuGet.Versioning;
|
|
|
|
namespace NuGet.Deps;
|
|
|
|
public record DependenciesManifest(RuntimeTarget RuntimeTarget,
|
|
ImmutableDictionary<NuGetFramework, string> CompilationOptions,
|
|
ImmutableDictionary<NuGetFramework, ImmutableDictionary<ManifestPackageKey, DependencyTarget>> Targets,
|
|
ImmutableDictionary<ManifestPackageKey, DependencyLibrary> Libraries);
|
|
|
|
public record DependencyLibrary(
|
|
LibraryType Type,
|
|
string Sha512 = "",
|
|
bool Serviceable = false,
|
|
ManifestPackageKey? Path = null,
|
|
string? HashPath = null);
|
|
|
|
[JsonConverter(typeof(JsonStringEnumConverter<LibraryType>))]
|
|
public enum LibraryType
|
|
{
|
|
Project,
|
|
Package
|
|
}
|
|
|
|
public record DependencyTarget(ImmutableDictionary<string, NuGetVersion>? Dependencies,
|
|
// key is file path relative to package root
|
|
ImmutableDictionary<string, RuntimeDependency>? Runtime,
|
|
// key is file path relative to package root
|
|
ImmutableDictionary<string, Dependency>? Native);
|
|
|
|
public record Dependency(Version? FileVersion = null);
|
|
|
|
public record RuntimeDependency(Version? AssemblyVersion = null, Version? FileVersion = null) : Dependency(FileVersion);
|
|
|
|
public record RuntimeTarget([property: JsonPropertyName("name")] NuGetFramework Framework, string Signature = "");
|
|
|
|
[JsonConverter(typeof(ManifestPackageKeyJsonConverter))]
|
|
public record ManifestPackageKey(string Id, NuGetVersion Version)
|
|
{
|
|
public static ManifestPackageKey Parse(string str)
|
|
{
|
|
var index = str.IndexOf('/');
|
|
if (index < 0)
|
|
throw new FormatException("Invalid package key: " + str);
|
|
|
|
return new ManifestPackageKey(str[..index], NuGetVersion.Parse(str[(index + 1)..]));
|
|
}
|
|
|
|
public override string ToString() => $"{Id}/{Version}";
|
|
}
|
|
|
|
public static class DependencyManifestUtility
|
|
{
|
|
private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
|
|
{
|
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
|
WriteIndented = true,
|
|
Converters =
|
|
{
|
|
new FrameworkJsonConverter(FrameworkNameFormat.FrameworkName),
|
|
new VersionJsonConverter()
|
|
}
|
|
};
|
|
|
|
public static async ValueTask WriteDependencyManifestAsync(Stream stream, CatalogEntry catalogEntry, NuGetFramework targetFramework,
|
|
PackageSourceMapping packageSources, Func<Models.Dependency, NuGetVersion> versionResolver)
|
|
{
|
|
var runtimeTarget = new RuntimeTarget(targetFramework);
|
|
|
|
var targets = ImmutableDictionary<ManifestPackageKey, DependencyTarget>.Empty.ToBuilder();
|
|
|
|
await MapCatalogEntry(catalogEntry, targetFramework, packageSources, versionResolver, targets);
|
|
|
|
var manifest = new DependenciesManifest(runtimeTarget, ImmutableDictionary<NuGetFramework, string>.Empty,
|
|
ImmutableDictionary<NuGetFramework, ImmutableDictionary<ManifestPackageKey, DependencyTarget>>.Empty
|
|
.Add(targetFramework, targets.ToImmutable()),
|
|
ImmutableDictionary<ManifestPackageKey, DependencyLibrary>.Empty);
|
|
|
|
await JsonSerializer.SerializeAsync(stream, manifest, SerializerOptions);
|
|
}
|
|
|
|
private static async Task MapCatalogEntry(CatalogEntry catalogEntry, NuGetFramework targetFramework,
|
|
PackageSourceMapping packageSources, Func<Models.Dependency, NuGetVersion> versionResolver, ImmutableDictionary<ManifestPackageKey, DependencyTarget>.Builder targets)
|
|
{
|
|
if (targets.ContainsKey(new(catalogEntry.Id, catalogEntry.Version)))
|
|
return;
|
|
|
|
var nearest = NuGetFrameworkUtility.GetNearest(catalogEntry.DependencyGroups, targetFramework,
|
|
group => group.TargetFramework);
|
|
|
|
if (nearest is null)
|
|
return;
|
|
|
|
targets.Add(new(catalogEntry.Id, catalogEntry.Version),
|
|
MapEntry(catalogEntry, nearest, targetFramework, versionResolver));
|
|
|
|
foreach (var dependency in nearest.Dependencies)
|
|
{
|
|
var client = await packageSources.GetClientAsync(dependency.Id);
|
|
var (url, entry) = await client.GetPackageRegistrationAsync(dependency.Id, versionResolver(dependency));
|
|
|
|
entry ??= await client.GetPackageCatalogEntryAsync(url);
|
|
|
|
await MapCatalogEntry(entry, targetFramework, packageSources, versionResolver, targets);
|
|
}
|
|
}
|
|
|
|
private static DependencyTarget MapEntry(CatalogEntry entry, DependencyGroup group, NuGetFramework targetFramework, Func<Models.Dependency, NuGetVersion> versionResolver)
|
|
{
|
|
return new(group.Dependencies.ToImmutableDictionary(b => b.Id, versionResolver),
|
|
entry.PackageEntries.Where(b => b.FullName.StartsWith($"lib/{targetFramework.GetShortFolderName()}/"))
|
|
.ToImmutableDictionary(b => b.FullName, _ => new RuntimeDependency()),
|
|
entry.PackageEntries.Where(b =>
|
|
b.FullName.StartsWith($"runtimes/{RuntimeInformation.RuntimeIdentifier}/native/"))
|
|
.ToImmutableDictionary(b => b.FullName, _ => new Dependency()));
|
|
}
|
|
} |