|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.ObjectModel;
using Microsoft.DotNet.ApiCompatibility.Logging;
using Microsoft.DotNet.ApiCompatibility.Runner;
using NuGet.Client;
using NuGet.ContentModel;
using NuGet.Frameworks;
namespace Microsoft.DotNet.PackageValidation.Validators
{
/// <summary>
/// Validates the api surface between compatible frameworks.
/// </summary>
public class CompatibleFrameworkInPackageValidator(ISuppressibleLog log,
IApiCompatRunner apiCompatRunner) : IPackageValidator
{
/// <summary>
/// Validates that the compatible frameworks have compatible surface area.
/// </summary>
/// <param name="options"><see cref="PackageValidatorOption"/> to configure the compatible framework in package validation.</param>
public void Validate(PackageValidatorOption options)
{
ApiCompatRunnerOptions apiCompatOptions = new(options.EnableStrictMode);
// The runtime graph doesn't need to be passed in as compile time assets can't be rid specific.
ManagedCodeConventions conventions = new(null);
PatternSet patternSet = options.Package.RefAssets.Any() ?
conventions.Patterns.CompileRefAssemblies :
conventions.Patterns.CompileLibAssemblies;
// If the package doesn't contain at least two frameworks, then there's nothing to compare.
if (options.Package.FrameworksInPackage.Count < 2)
{
return;
}
Queue<(NuGetFramework, IReadOnlyList<ContentItem>)> compileAssetsQueue = new();
foreach (NuGetFramework framework in options.Package.FrameworksInPackage.OrderByDescending(f => f.Version))
{
IReadOnlyList<ContentItem>? compileTimeAsset = options.Package.FindBestCompileAssetForFramework(framework);
if (compileTimeAsset != null)
compileAssetsQueue.Enqueue((framework, compileTimeAsset));
}
while (compileAssetsQueue.Count > 0)
{
(NuGetFramework framework, IReadOnlyList<ContentItem> compileTimeAsset) = compileAssetsQueue.Dequeue();
// If no assets are available for comparison, stop the iteration.
if (compileAssetsQueue.Count == 0) break;
SelectionCriteria managedCriteria = conventions.Criteria.ForFramework(framework);
ContentItemCollection contentItemCollection = new();
// The collection won't contain the current compile time asset as it is already dequeued.
contentItemCollection.Load(compileAssetsQueue.SelectMany(a => a.Item2).Select(a => a.Path));
// Search for a compatible compile time asset and compare it.
IList<ContentItem>? compatibleFrameworkAsset = contentItemCollection.FindBestItemGroup(managedCriteria, patternSet)?.Items;
if (compatibleFrameworkAsset != null)
{
apiCompatRunner.QueueApiCompatFromContentItem(log,
new ReadOnlyCollection<ContentItem>(compatibleFrameworkAsset),
compileTimeAsset,
apiCompatOptions,
options.Package);
}
}
if (options.ExecuteApiCompatWorkItems)
{
apiCompatRunner.ExecuteWorkItems();
}
}
}
}
|