| File: Validators\BaselinePackageValidator.cs | Web Access |
| Project: src\sdk\src\Compatibility\ApiCompat\Microsoft.DotNet.PackageValidation\Microsoft.DotNet.PackageValidation.csproj (Microsoft.DotNet.PackageValidation) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.DotNet.ApiCompatibility.Logging; using Microsoft.DotNet.ApiCompatibility.Runner; using NuGet.ContentModel; using NuGet.Frameworks; namespace Microsoft.DotNet.PackageValidation.Validators { /// <summary> /// Validates that no target framework / rid support is dropped in the latest package. /// Reports all the breaking changes in the latest package. /// </summary> public sealed class BaselinePackageValidator(ISuppressibleLog log, IApiCompatRunner apiCompatRunner) : IPackageValidator { /// <summary> /// Validates the latest NuGet package doesn't drop any target framework/rid and does not introduce any breaking changes. /// </summary> /// <param name="options"><see cref="PackageValidatorOption"/> to configure the baseline package validation.</param> public void Validate(PackageValidatorOption options) { if (options.BaselinePackage is null) { throw new ArgumentNullException(nameof(options.BaselinePackage)); } ApiCompatRunnerOptions apiCompatOptions = new(options.EnableStrictMode, isBaselineComparison: true); foreach (NuGetFramework baselineTargetFramework in options.BaselinePackage.FrameworksInPackage) { // Skip target frameworks excluded from the baseline package. if (options.BaselinePackageFrameworkFilter is not null && options.BaselinePackageFrameworkFilter.IsExcluded(baselineTargetFramework)) { continue; } // Retrieve the compile time assets from the baseline package IReadOnlyList<ContentItem>? baselineCompileAssets = options.BaselinePackage.FindBestCompileAssetForFramework(baselineTargetFramework); if (baselineCompileAssets != null) { // Search for compatible compile time assets in the latest package. IReadOnlyList<ContentItem>? latestCompileAssets = options.Package.FindBestCompileAssetForFramework(baselineTargetFramework); if (latestCompileAssets == null) { log.LogError(new Suppression(DiagnosticIds.TargetFrameworkDropped) { Target = baselineTargetFramework.ToString() }, DiagnosticIds.TargetFrameworkDropped, string.Format(Resources.MissingTargetFramework, baselineTargetFramework)); } else if (options.EnqueueApiCompatWorkItems) { apiCompatRunner.QueueApiCompatFromContentItem(log, baselineCompileAssets, latestCompileAssets, apiCompatOptions, options.BaselinePackage, options.Package); } } // Retrieve runtime baseline assets and searches for compatible runtime assets in the latest package. IReadOnlyList<ContentItem>? baselineRuntimeAssets = options.BaselinePackage.FindBestRuntimeAssetForFramework(baselineTargetFramework); if (baselineRuntimeAssets != null) { // Search for compatible runtime assets in the latest package. IReadOnlyList<ContentItem>? latestRuntimeAssets = options.Package.FindBestRuntimeAssetForFramework(baselineTargetFramework); if (latestRuntimeAssets == null) { log.LogError(new Suppression(DiagnosticIds.TargetFrameworkDropped) { Target = baselineTargetFramework.ToString() }, DiagnosticIds.TargetFrameworkDropped, string.Format(Resources.MissingTargetFramework, baselineTargetFramework)); } else if (options.EnqueueApiCompatWorkItems) { apiCompatRunner.QueueApiCompatFromContentItem(log, baselineRuntimeAssets, latestRuntimeAssets, apiCompatOptions, options.BaselinePackage, options.Package); } } // Retrieve runtime specific baseline assets and searches for compatible runtime specific assets in the latest package. IReadOnlyList<ContentItem>? baselineRuntimeSpecificAssets = options.BaselinePackage.FindBestRuntimeSpecificAssetForFramework(baselineTargetFramework); if (baselineRuntimeSpecificAssets != null && baselineRuntimeSpecificAssets.Count > 0) { IEnumerable<IGrouping<string, ContentItem>> baselineRuntimeSpecificAssetsRidGroupedPerRid = baselineRuntimeSpecificAssets .Where(t => t.Path.StartsWith("runtimes")) .GroupBy(t => (string)t.Properties["rid"]); foreach (IGrouping<string, ContentItem> baselineRuntimeSpecificAssetsRidGroup in baselineRuntimeSpecificAssetsRidGroupedPerRid) { IReadOnlyList<ContentItem>? latestRuntimeSpecificAssets = options.Package.FindBestRuntimeAssetForFrameworkAndRuntime(baselineTargetFramework, baselineRuntimeSpecificAssetsRidGroup.Key); if (latestRuntimeSpecificAssets == null) { log.LogError(new Suppression(DiagnosticIds.TargetFrameworkAndRidPairDropped) { Target = baselineTargetFramework.ToString() + "-" + baselineRuntimeSpecificAssetsRidGroup.Key }, DiagnosticIds.TargetFrameworkAndRidPairDropped, string.Format(Resources.MissingTargetFrameworkAndRid, baselineTargetFramework, baselineRuntimeSpecificAssetsRidGroup.Key)); } else if (options.EnqueueApiCompatWorkItems) { apiCompatRunner.QueueApiCompatFromContentItem(log, baselineRuntimeSpecificAssetsRidGroup.ToArray(), latestRuntimeSpecificAssets, apiCompatOptions, options.BaselinePackage, options.Package); } } } } // If baseline target frameworks are excluded, provide additional logging. if (options.BaselinePackageFrameworkFilter is not null && options.BaselinePackageFrameworkFilter.FoundExcludedTargetFrameworks.Count > 0) { log.LogMessage(ApiSymbolExtensions.Logging.MessageImportance.Normal, string.Format(Resources.BaselineTargetFrameworksIgnored, string.Join(", ", options.BaselinePackageFrameworkFilter.FoundExcludedTargetFrameworks))); // If a baseline target framework is ignored but present in the current package, emit a warning. // This should help avoiding unintentional exclusions when using wildcards in the exclusion patterns. string[] baselineTargetFrameworksExcludedButPresentInCurrentPackage = options.Package.FrameworksInPackage .Select(framework => framework.GetShortFolderName()) .Intersect(options.BaselinePackageFrameworkFilter.FoundExcludedTargetFrameworks) .ToArray(); foreach (string baselineTargetFrameworkExcludedButPresentInCurrentPackage in baselineTargetFrameworksExcludedButPresentInCurrentPackage) { log.LogWarning(new Suppression(DiagnosticIds.BaselineTargetFrameworkIgnoredButPresentInCurrentPackage, baselineTargetFrameworkExcludedButPresentInCurrentPackage), DiagnosticIds.BaselineTargetFrameworkIgnoredButPresentInCurrentPackage, string.Format(Resources.BaselineTargetFrameworkIgnoredButPresentInCurrentPackage, baselineTargetFrameworkExcludedButPresentInCurrentPackage)); } } if (options.ExecuteApiCompatWorkItems) { apiCompatRunner.ExecuteWorkItems(); } } } }