|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Build.Framework;
using Microsoft.DotNet.ApiCompatibility.Logging;
using Microsoft.NET.Build.Tasks;
using NuGet.Frameworks;
namespace Microsoft.DotNet.ApiCompat.Task
{
/// <summary>
/// ApiCompat's ValidatePackage msbuild frontend.
/// This task provides the functionality to compare package assets based on given inputs.
/// </summary>
public class ValidatePackageTask : TaskBase
{
// Important: Keep properties exposed in sync with the CLI frontend.
/// <summary>
/// The path to the package to inspect.
/// </summary>
[Required]
public string? PackageTargetPath { get; set; }
/// <summary>
/// The path to the roslyn assemblies that should be loaded.
/// </summary>
[Required]
public string? RoslynAssembliesPath { get; set; }
/// <summary>
/// A runtime graph that can be provided for package asset selection.
/// </summary>
public string? RuntimeGraph { get; set; }
/// <summary>
/// If true, includes both internal and public API.
/// </summary>
public bool RespectInternals { get; set; }
/// <summary>
/// Enables rule to check that attributes match.
/// </summary>
public bool EnableRuleAttributesMustMatch { get; set; }
/// <summary>
/// Set of files with types in DocId format of which attributes to exclude.
/// </summary>
public string[]? ExcludeAttributesFiles { get; set; }
/// <summary>
/// Enables rule to check that the parameter names between public methods do not change.
/// </summary>
public bool EnableRuleCannotChangeParameterName { get; set; }
/// <summary>
/// If true, performs api compatibility checks on the package assets.
/// </summary>
public bool RunApiCompat { get; set; } = true;
/// <summary>
/// Validates api compatibility in strict mode for contract and implementation assemblies for all compatible target frameworks.
/// </summary>
public bool EnableStrictModeForCompatibleTfms { get; set; } = true;
/// <summary>
/// Validates api compatibility in strict mode for assemblies that are compatible based on their target framework.
/// </summary>
public bool EnableStrictModeForCompatibleFrameworksInPackage { get; set; }
/// <summary>
/// Validates api compatibility in strict mode for package baseline checks.
/// </summary>
public bool EnableStrictModeForBaselineValidation { get; set; }
/// <summary>
/// The path to the baseline package that acts as the contract to inspect.
/// </summary>
public string? BaselinePackageTargetPath { get; set; }
/// <summary>
/// If true, generates a suppression file that contains the api compatibility errors.
/// </summary>
public bool GenerateSuppressionFile { get; set; }
/// <summary>
/// If true, preserves unnecessary suppressions when re-generating the suppression file.
/// </summary>
public bool PreserveUnnecessarySuppressions { get; set; }
/// <summary>
/// If true, permits unnecessary suppressions in the suppression file.
/// </summary>
public bool PermitUnnecessarySuppressions { get; set; }
/// <summary>
/// The path to suppression files. If provided, the suppressions are read and stored.
/// </summary>
public string[]? SuppressionFiles { get; set; }
/// <summary>
/// The path to the suppression output file that is written to, when <see cref="GenerateSuppressionFile"/> is true.
/// </summary>
public string? SuppressionOutputFile { get; set; }
/// <summary>
/// A NoWarn string contains the error codes that should be ignored.
/// </summary>
public string? NoWarn { get; set; }
/// <summary>
/// Assembly references grouped by target framework, for the assets inside the package.
/// </summary>
public ITaskItem[]? PackageAssemblyReferences { get; set; }
/// <summary>
/// Assembly references grouped by target framework, for the assets inside the baseline package.
/// </summary>
public ITaskItem[]? BaselinePackageAssemblyReferences { get; set; }
/// <summary>
/// Target frameworks to ignore from the baseline package.
/// Supports the wildcard character '*' at the end of the string. Culture and casing is ignored.
/// The framework string must match the folder name in the baseline package.
/// </summary>
public string[]? BaselinePackageFrameworksToIgnore { get; set; }
public override bool Execute()
{
RoslynResolver roslynResolver = RoslynResolver.Register(RoslynAssembliesPath!);
try
{
return base.Execute();
}
finally
{
roslynResolver.Unregister();
}
}
protected override void ExecuteCore()
{
SuppressibleMSBuildLog logFactory(ISuppressionEngine suppressionEngine) => new(Log, suppressionEngine, NoWarn);
ValidatePackage.Run(logFactory,
GenerateSuppressionFile,
PreserveUnnecessarySuppressions,
PermitUnnecessarySuppressions,
SuppressionFiles,
SuppressionOutputFile,
NoWarn,
RespectInternals,
EnableRuleAttributesMustMatch,
ExcludeAttributesFiles,
EnableRuleCannotChangeParameterName,
PackageTargetPath!,
RunApiCompat,
EnableStrictModeForCompatibleTfms,
EnableStrictModeForCompatibleFrameworksInPackage,
EnableStrictModeForBaselineValidation,
BaselinePackageTargetPath,
RuntimeGraph,
ParsePackageAssemblyReferences(PackageAssemblyReferences),
ParsePackageAssemblyReferences(BaselinePackageAssemblyReferences),
BaselinePackageFrameworksToIgnore);
}
private static Dictionary<NuGetFramework, IEnumerable<string>>? ParsePackageAssemblyReferences(ITaskItem[]? packageAssemblyReferences)
{
if (packageAssemblyReferences is null || packageAssemblyReferences.Length == 0)
return null;
Dictionary<NuGetFramework, IEnumerable<string>> packageAssemblyReferencesDict = new(packageAssemblyReferences.Length);
foreach (ITaskItem taskItem in packageAssemblyReferences)
{
string targetFrameworkMoniker = taskItem.GetMetadata("TargetFrameworkMoniker");
string targetPlatformMoniker = taskItem.GetMetadata("TargetPlatformMoniker");
string referencePath = taskItem.GetMetadata("ReferencePath");
// The TPM is null when the assembly doesn't target a platform.
if (targetFrameworkMoniker == string.Empty || referencePath == string.Empty)
continue;
NuGetFramework nuGetFramework = NuGetFramework.ParseComponents(targetFrameworkMoniker, targetPlatformMoniker);
// Skip duplicate frameworks which could be passed in when using TFM aliases.
if (packageAssemblyReferencesDict.ContainsKey(nuGetFramework))
{
continue;
}
string[] references = referencePath.Split(',');
packageAssemblyReferencesDict.Add(nuGetFramework, references);
}
return packageAssemblyReferencesDict;
}
}
}
|