Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)ApiCompatServiceProvider.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SuppressionFileHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ValidatePackage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ValidateAssemblies.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ValidateAssembliesOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ValidatePackage.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ValidatePackageOptions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RegexStringTransformer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)RoslynResolver.cs" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,35 @@ namespace Microsoft.DotNet.ApiCompat
internal static class ValidateAssemblies
{
public static int Run(Func<ISuppressionEngine, ISuppressibleLog> logFactory,
bool generateSuppressionFile,
bool preserveUnnecessarySuppressions,
bool permitUnnecessarySuppressions,
string[]? suppressionFiles,
string? suppressionOutputFile,
string? noWarn,
bool respectInternals,
bool enableRuleAttributesMustMatch,
string[]? excludeAttributesFiles,
bool enableRuleCannotChangeParameterName,
string[] leftAssemblies,
string[] rightAssemblies,
bool enableStrictMode,
string[][]? leftAssembliesReferences,
string[][]? rightAssembliesReferences,
bool createWorkItemPerAssembly,
(string CaptureGroupPattern, string ReplacementString)[]? leftAssembliesTransformationPatterns,
(string CaptureGroupPattern, string ReplacementString)[]? rightAssembliesTransformationPatterns)
ValidateAssembliesOptions options)
{
// Initialize the service provider
ApiCompatServiceProvider serviceProvider = new(logFactory,
() => SuppressionFileHelper.CreateSuppressionEngine(suppressionFiles, noWarn, generateSuppressionFile),
() => SuppressionFileHelper.CreateSuppressionEngine(options.SuppressionFiles, options.NoWarn, options.GenerateSuppressionFile),
(log) => new RuleFactory(log,
enableRuleAttributesMustMatch,
enableRuleCannotChangeParameterName),
respectInternals,
excludeAttributesFiles);
options.EnableRuleAttributesMustMatch,
options.EnableRuleCannotChangeParameterName),
options.RespectInternals,
options.ExcludeAttributesFiles);

IApiCompatRunner apiCompatRunner = serviceProvider.ApiCompatRunner;
ApiCompatRunnerOptions apiCompatOptions = new(enableStrictMode);
ApiCompatRunnerOptions apiCompatOptions = new(options.EnableStrictMode);

// Optionally provide a string transformer if a transformation pattern is passed in.
RegexStringTransformer? leftAssembliesStringTransformer = leftAssembliesTransformationPatterns != null ? new RegexStringTransformer(leftAssembliesTransformationPatterns) : null;
RegexStringTransformer? rightAssembliesStringTransformer = rightAssembliesTransformationPatterns != null ? new RegexStringTransformer(rightAssembliesTransformationPatterns) : null;
RegexStringTransformer? leftAssembliesStringTransformer = options.LeftAssembliesTransformationPatterns != null ? new RegexStringTransformer(options.LeftAssembliesTransformationPatterns) : null;
RegexStringTransformer? rightAssembliesStringTransformer = options.RightAssembliesTransformationPatterns != null ? new RegexStringTransformer(options.RightAssembliesTransformationPatterns) : null;

if (createWorkItemPerAssembly)
if (options.CreateWorkItemPerAssembly)
{
if (leftAssemblies.Length != rightAssemblies.Length)
if (options.LeftAssemblies.Length != options.RightAssemblies.Length)
{
throw new Exception(CommonResources.CreateWorkItemPerAssemblyAssembliesNotEqual);
}

for (int i = 0; i < leftAssemblies.Length; i++)
for (int i = 0; i < options.LeftAssemblies.Length; i++)
{
List<MetadataInformation> leftMetadataInformation = GetMetadataInformation(leftAssemblies[i], GetAssemblyReferences(leftAssembliesReferences, i), leftAssembliesStringTransformer);
List<MetadataInformation> rightMetadataInformation = GetMetadataInformation(rightAssemblies[i], GetAssemblyReferences(rightAssembliesReferences, i), rightAssembliesStringTransformer);
List<MetadataInformation> leftMetadataInformation = GetMetadataInformation(options.LeftAssemblies[i], GetAssemblyReferences(options.LeftAssembliesReferences, i), leftAssembliesStringTransformer);
List<MetadataInformation> rightMetadataInformation = GetMetadataInformation(options.RightAssemblies[i], GetAssemblyReferences(options.RightAssembliesReferences, i), rightAssembliesStringTransformer);

// Enqueue the work item
ApiCompatRunnerWorkItem workItem = new(leftMetadataInformation, apiCompatOptions, rightMetadataInformation);
Expand All @@ -66,16 +49,16 @@ public static int Run(Func<ISuppressionEngine, ISuppressibleLog> logFactory,
else
{
// Create the work item that corresponds to the passed in left assembly.
List<MetadataInformation> leftAssembliesMetadataInformation = new(leftAssemblies.Length);
for (int i = 0; i < leftAssemblies.Length; i++)
List<MetadataInformation> leftAssembliesMetadataInformation = new(options.LeftAssemblies.Length);
for (int i = 0; i < options.LeftAssemblies.Length; i++)
{
leftAssembliesMetadataInformation.AddRange(GetMetadataInformation(leftAssemblies[i], GetAssemblyReferences(leftAssembliesReferences, i), leftAssembliesStringTransformer));
leftAssembliesMetadataInformation.AddRange(GetMetadataInformation(options.LeftAssemblies[i], GetAssemblyReferences(options.LeftAssembliesReferences, i), leftAssembliesStringTransformer));
}

List<MetadataInformation> rightAssembliesMetadataInformation = new(rightAssemblies.Length);
for (int i = 0; i < rightAssemblies.Length; i++)
List<MetadataInformation> rightAssembliesMetadataInformation = new(options.RightAssemblies.Length);
for (int i = 0; i < options.RightAssemblies.Length; i++)
{
rightAssembliesMetadataInformation.AddRange(GetMetadataInformation(rightAssemblies[i], GetAssemblyReferences(rightAssembliesReferences, i), rightAssembliesStringTransformer));
rightAssembliesMetadataInformation.AddRange(GetMetadataInformation(options.RightAssemblies[i], GetAssemblyReferences(options.RightAssembliesReferences, i), rightAssembliesStringTransformer));
}

// Enqueue the work item
Expand All @@ -86,17 +69,17 @@ public static int Run(Func<ISuppressionEngine, ISuppressibleLog> logFactory,
// Execute the enqueued work item(s).
apiCompatRunner.ExecuteWorkItems();

SuppressionFileHelper.LogApiCompatSuccessOrFailure(generateSuppressionFile, serviceProvider.SuppressibleLog);
SuppressionFileHelper.LogApiCompatSuccessOrFailure(options.GenerateSuppressionFile, serviceProvider.SuppressibleLog);

if (generateSuppressionFile)
if (options.GenerateSuppressionFile)
{
SuppressionFileHelper.GenerateSuppressionFile(serviceProvider.SuppressionEngine,
serviceProvider.SuppressibleLog,
preserveUnnecessarySuppressions,
suppressionFiles,
suppressionOutputFile);
options.PreserveUnnecessarySuppressions,
options.SuppressionFiles,
options.SuppressionOutputFile);
}
else if (!permitUnnecessarySuppressions)
else if (!options.PermitUnnecessarySuppressions)
{
SuppressionFileHelper.ValidateUnnecessarySuppressions(serviceProvider.SuppressionEngine, serviceProvider.SuppressibleLog);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.ApiCompat
{
/// <summary>
/// Options for running <see cref="ValidateAssemblies"/>.
/// </summary>
internal sealed class ValidateAssembliesOptions
{
public ValidateAssembliesOptions(string[] leftAssemblies, string[] rightAssemblies)
{
LeftAssemblies = leftAssemblies ?? throw new ArgumentNullException(nameof(leftAssemblies));
RightAssemblies = rightAssemblies ?? throw new ArgumentNullException(nameof(rightAssemblies));
}

/// <summary>
/// The assemblies that represent the contract (left side).
/// </summary>
public string[] LeftAssemblies { get; }

/// <summary>
/// The assemblies that represent the implementation (right side).
/// </summary>
public string[] RightAssemblies { get; }

/// <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 one or more suppression files to read from.
/// </summary>
public string[]? SuppressionFiles { get; set; }

/// <summary>
/// The path to a suppression output file to write to when <see cref="GenerateSuppressionFile"/> is true.
/// </summary>
public string? SuppressionOutputFile { get; set; }

/// <summary>
/// A NoWarn string that allows disabling specific rules.
/// </summary>
public string? NoWarn { 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>
/// The path to one or more attribute exclusion files with types in DocId format.
/// </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>
/// Performs api comparison checks in strict mode.
/// </summary>
public bool EnableStrictMode { get; set; }

/// <summary>
/// The left assemblies' references. The index in the array maps to the index of the passed in left assembly.
/// </summary>
public string[][]? LeftAssembliesReferences { get; set; }

/// <summary>
/// The right assemblies' references. The index in the array maps to the index of the passed in right assembly.
/// </summary>
public string[][]? RightAssembliesReferences { get; set; }

/// <summary>
/// Create dedicated api compatibility checks for each left and right assembly tuple.
/// </summary>
public bool CreateWorkItemPerAssembly { get; set; }

/// <summary>
/// Regex transformation patterns (regex + replacement string) that transform left assembly paths.
/// </summary>
public (string CaptureGroupPattern, string ReplacementString)[]? LeftAssembliesTransformationPatterns { get; set; }

/// <summary>
/// Regex transformation patterns (regex + replacement string) that transform right assembly paths.
/// </summary>
public (string CaptureGroupPattern, string ReplacementString)[]? RightAssembliesTransformationPatterns { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,93 +6,73 @@
using Microsoft.DotNet.PackageValidation;
using Microsoft.DotNet.PackageValidation.Filtering;
using Microsoft.DotNet.PackageValidation.Validators;
using NuGet.Frameworks;

namespace Microsoft.DotNet.ApiCompat
{
internal static class ValidatePackage
{
public static int Run(Func<ISuppressionEngine, ISuppressibleLog> logFactory,
bool generateSuppressionFile,
bool preserveUnnecessarySuppressions,
bool permitUnnecessarySuppressions,
string[]? suppressionFiles,
string? suppressionOutputFile,
string? noWarn,
bool respectInternals,
bool enableRuleAttributesMustMatch,
string[]? excludeAttributesFiles,
bool enableRuleCannotChangeParameterName,
string? packagePath,
bool runApiCompat,
bool enableStrictModeForCompatibleTfms,
bool enableStrictModeForCompatibleFrameworksInPackage,
bool enableStrictModeForBaselineValidation,
string? baselinePackagePath,
string? runtimeGraph,
IReadOnlyDictionary<NuGetFramework, IEnumerable<string>>? packageAssemblyReferences,
IReadOnlyDictionary<NuGetFramework, IEnumerable<string>>? baselinePackageAssemblyReferences,
string[]? baselinePackageFrameworksToIgnore)
ValidatePackageOptions options)
{
// Initialize the service provider
ApiCompatServiceProvider serviceProvider = new(logFactory,
() => SuppressionFileHelper.CreateSuppressionEngine(suppressionFiles, noWarn, generateSuppressionFile),
() => SuppressionFileHelper.CreateSuppressionEngine(options.SuppressionFiles, options.NoWarn, options.GenerateSuppressionFile),
(log) => new RuleFactory(log,
enableRuleAttributesMustMatch,
enableRuleCannotChangeParameterName),
respectInternals,
excludeAttributesFiles);
options.EnableRuleAttributesMustMatch,
options.EnableRuleCannotChangeParameterName),
options.RespectInternals,
options.ExcludeAttributesFiles);

// If a runtime graph is provided, parse and use it for asset selection during the in-memory package construction.
if (runtimeGraph != null)
if (options.RuntimeGraph != null)
{
Package.InitializeRuntimeGraph(runtimeGraph);
Package.InitializeRuntimeGraph(options.RuntimeGraph);
}

// Create the in-memory representation of the passed in package path
Package package = Package.Create(packagePath, packageAssemblyReferences);
Package package = Package.Create(options.PackagePath, options.PackageAssemblyReferences);

// Invoke all validators and pass the specific validation options in. Don't execute work items, just enqueue them.
CompatibleTfmValidator tfmValidator = new(serviceProvider.SuppressibleLog, serviceProvider.ApiCompatRunner);
tfmValidator.Validate(new PackageValidatorOption(package,
enableStrictModeForCompatibleTfms,
enqueueApiCompatWorkItems: runApiCompat,
options.EnableStrictModeForCompatibleTfms,
enqueueApiCompatWorkItems: options.RunApiCompat,
executeApiCompatWorkItems: false));

CompatibleFrameworkInPackageValidator compatibleFrameworkInPackageValidator = new(serviceProvider.SuppressibleLog, serviceProvider.ApiCompatRunner);
compatibleFrameworkInPackageValidator.Validate(new PackageValidatorOption(package,
enableStrictModeForCompatibleFrameworksInPackage,
enqueueApiCompatWorkItems: runApiCompat,
options.EnableStrictModeForCompatibleFrameworksInPackage,
enqueueApiCompatWorkItems: options.RunApiCompat,
executeApiCompatWorkItems: false));

if (!string.IsNullOrEmpty(baselinePackagePath))
if (!string.IsNullOrEmpty(options.BaselinePackagePath))
{
BaselinePackageValidator baselineValidator = new(serviceProvider.SuppressibleLog, serviceProvider.ApiCompatRunner);
baselineValidator.Validate(new PackageValidatorOption(package,
enableStrictMode: enableStrictModeForBaselineValidation,
enqueueApiCompatWorkItems: runApiCompat,
enableStrictMode: options.EnableStrictModeForBaselineValidation,
enqueueApiCompatWorkItems: options.RunApiCompat,
executeApiCompatWorkItems: false,
Package.Create(baselinePackagePath, baselinePackageAssemblyReferences),
baselinePackageFrameworksToIgnore is not null ? new TargetFrameworkFilter(baselinePackageFrameworksToIgnore) : null));
Package.Create(options.BaselinePackagePath, options.BaselinePackageAssemblyReferences),
options.BaselinePackageFrameworksToIgnore is not null ? new TargetFrameworkFilter(options.BaselinePackageFrameworksToIgnore) : null));
}

if (runApiCompat)
if (options.RunApiCompat)
{
// Execute the work items that were enqueued.
serviceProvider.ApiCompatRunner.ExecuteWorkItems();

SuppressionFileHelper.LogApiCompatSuccessOrFailure(generateSuppressionFile, serviceProvider.SuppressibleLog);
SuppressionFileHelper.LogApiCompatSuccessOrFailure(options.GenerateSuppressionFile, serviceProvider.SuppressibleLog);
}

if (generateSuppressionFile)
if (options.GenerateSuppressionFile)
{
SuppressionFileHelper.GenerateSuppressionFile(serviceProvider.SuppressionEngine,
serviceProvider.SuppressibleLog,
preserveUnnecessarySuppressions,
suppressionFiles,
suppressionOutputFile);
options.PreserveUnnecessarySuppressions,
options.SuppressionFiles,
options.SuppressionOutputFile);
}
else if (!permitUnnecessarySuppressions)
else if (!options.PermitUnnecessarySuppressions)
{
SuppressionFileHelper.ValidateUnnecessarySuppressions(serviceProvider.SuppressionEngine, serviceProvider.SuppressibleLog);
}
Expand Down
Loading
Loading