|
|
|
@ -304,44 +304,95 @@ public static class ApiDiffHelper |
|
|
|
{ |
|
|
|
using var packageReader = new PackageArchiveReader(currentArchive); |
|
|
|
packageId = packageReader.NuspecReader.GetId(); |
|
|
|
|
|
|
|
baselineFolderPath = outputFolderPath / "baseline" / packageId; |
|
|
|
Directory.CreateDirectory(baselineFolderPath); |
|
|
|
|
|
|
|
currentFolderPath = outputFolderPath / "current" / packageId; |
|
|
|
Directory.CreateDirectory(currentFolderPath); |
|
|
|
|
|
|
|
currentFolderNames = ExtractDiffableAssembliesFromPackage(currentArchive, currentFolderPath); |
|
|
|
} |
|
|
|
|
|
|
|
// Download baseline package
|
|
|
|
memoryStream.Position = 0L; |
|
|
|
memoryStream.SetLength(0L); |
|
|
|
await DownloadBaselinePackageAsync(memoryStream, downloadContext, packageId, baselineVersion); |
|
|
|
memoryStream.Position = 0L; |
|
|
|
var packageExists = await downloadContext.FindPackageByIdResource.DoesPackageExistAsync( |
|
|
|
packageId, |
|
|
|
baselineVersion, |
|
|
|
downloadContext.CacheContext, |
|
|
|
NullLogger.Instance, |
|
|
|
CancellationToken.None); |
|
|
|
|
|
|
|
// Extract baseline package
|
|
|
|
using (var baselineArchive = new ZipArchive(memoryStream, ZipArchiveMode.Read, leaveOpen: true)) |
|
|
|
if (packageExists) |
|
|
|
{ |
|
|
|
baselineFolderPath = outputFolderPath / "baseline" / packageId; |
|
|
|
// Download baseline package
|
|
|
|
memoryStream.Position = 0L; |
|
|
|
memoryStream.SetLength(0L); |
|
|
|
await DownloadBaselinePackageAsync(memoryStream, downloadContext, packageId, baselineVersion); |
|
|
|
memoryStream.Position = 0L; |
|
|
|
|
|
|
|
// Extract baseline package
|
|
|
|
using var baselineArchive = new ZipArchive(memoryStream, ZipArchiveMode.Read, leaveOpen: true); |
|
|
|
baselineFolderNames = ExtractDiffableAssembliesFromPackage(baselineArchive, baselineFolderPath); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
Information("Baseline package {Id} {Version} does not exist. Assuming new package.", packageId, baselineVersion); |
|
|
|
baselineFolderNames = []; |
|
|
|
} |
|
|
|
|
|
|
|
if (currentFolderNames.Count == 0 && baselineFolderNames.Count == 0) |
|
|
|
continue; |
|
|
|
|
|
|
|
var frameworkDiffs = new List<FrameworkDiffInfo>(); |
|
|
|
|
|
|
|
// Match frameworks
|
|
|
|
foreach (var (framework, currentFolderName) in currentFolderNames) |
|
|
|
{ |
|
|
|
// Ignore new frameworks that didn't exist in the baseline package. Empty folders make the ApiDiff tool crash.
|
|
|
|
if (!baselineFolderNames.TryGetValue(framework, out var baselineFolderName)) |
|
|
|
continue; |
|
|
|
baselineFolderName = currentFolderName; |
|
|
|
|
|
|
|
frameworkDiffs.Add(new FrameworkDiffInfo( |
|
|
|
var frameworkDiff = new FrameworkDiffInfo( |
|
|
|
framework, |
|
|
|
baselineFolderPath / FolderLib / baselineFolderName, |
|
|
|
currentFolderPath / FolderLib / currentFolderName)); |
|
|
|
currentFolderPath / FolderLib / currentFolderName); |
|
|
|
|
|
|
|
EnsureAssemblies(frameworkDiff); |
|
|
|
|
|
|
|
frameworkDiffs.Add(frameworkDiff); |
|
|
|
} |
|
|
|
|
|
|
|
packageDiffs.Add(new PackageDiffInfo(packageId, [..frameworkDiffs])); |
|
|
|
} |
|
|
|
|
|
|
|
return new GlobalDiffInfo(baselineVersion, currentVersion, packageDiffs.DrainToImmutable()); |
|
|
|
|
|
|
|
// Ensure that both sides of a framework diff have matching assemblies.
|
|
|
|
// For any missing, generate an empty assembly to diff against.
|
|
|
|
// (The API diff tool supports added and removed assemblies in theory but actually throws if one side doesn't have any.)
|
|
|
|
static void EnsureAssemblies(FrameworkDiffInfo frameworkDiff) |
|
|
|
{ |
|
|
|
Directory.CreateDirectory(frameworkDiff.BaselineFolderPath); |
|
|
|
Directory.CreateDirectory(frameworkDiff.CurrentFolderPath); |
|
|
|
|
|
|
|
var baselineFileNames = GetFileNames(frameworkDiff.BaselineFolderPath); |
|
|
|
var currentFileNames = GetFileNames(frameworkDiff.CurrentFolderPath); |
|
|
|
|
|
|
|
GenerateMissingAssemblies(currentFileNames.Except(baselineFileNames), frameworkDiff.BaselineFolderPath); |
|
|
|
GenerateMissingAssemblies(baselineFileNames.Except(currentFileNames), frameworkDiff.CurrentFolderPath); |
|
|
|
|
|
|
|
static string[] GetFileNames(string folderPath) |
|
|
|
=> Directory.EnumerateFiles(folderPath, "*.dll").Select(Path.GetFileName)!.ToArray<string>(); |
|
|
|
|
|
|
|
void GenerateMissingAssemblies(IEnumerable<string> missingFileNames, string folderPath) |
|
|
|
{ |
|
|
|
foreach (var missingFileName in missingFileNames) |
|
|
|
{ |
|
|
|
GenerateEmptyAssembly( |
|
|
|
Path.GetFileNameWithoutExtension(missingFileName), |
|
|
|
frameworkDiff.Framework.GetShortFolderName(), |
|
|
|
Path.Join(folderPath, missingFileName)); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
static async Task<NuGetDownloadContext> CreateNuGetDownloadContextAsync() |
|
|
|
@ -453,6 +504,44 @@ public static class ApiDiffHelper |
|
|
|
value; |
|
|
|
} |
|
|
|
|
|
|
|
static void GenerateEmptyAssembly(string name, string framework, string outputFilePath) |
|
|
|
{ |
|
|
|
var projectContents = |
|
|
|
$"""
|
|
|
|
<Project Sdk="Microsoft.NET.Sdk"> |
|
|
|
<PropertyGroup> |
|
|
|
<TargetFramework>{framework}</TargetFramework> |
|
|
|
<Configuration>Release</Configuration> |
|
|
|
<DebugType>None</DebugType> |
|
|
|
</PropertyGroup> |
|
|
|
</Project> |
|
|
|
""";
|
|
|
|
|
|
|
|
var tempDirPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); |
|
|
|
var projectFilePath = Path.Join(tempDirPath, $"{name}.csproj"); |
|
|
|
|
|
|
|
Directory.CreateDirectory(tempDirPath); |
|
|
|
|
|
|
|
try |
|
|
|
{ |
|
|
|
File.WriteAllText(projectFilePath, projectContents); |
|
|
|
|
|
|
|
using var process = ProcessTasks.StartProcess( |
|
|
|
"dotnet", |
|
|
|
$"build \"{projectFilePath}\" --output \"{tempDirPath}\"", |
|
|
|
tempDirPath); |
|
|
|
|
|
|
|
process.AssertZeroExitCode(); |
|
|
|
|
|
|
|
File.Copy(Path.Join(tempDirPath, $"{name}.dll"), outputFilePath); |
|
|
|
} |
|
|
|
finally |
|
|
|
{ |
|
|
|
if (Directory.Exists(tempDirPath)) |
|
|
|
Directory.Delete(tempDirPath, true); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public sealed class GlobalDiffInfo( |
|
|
|
NuGetVersion baselineVersion, |
|
|
|
NuGetVersion currentVersion, |
|
|
|
|