diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/Microsoft.DotNet.ApiCompat.Shared.projitems b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/Microsoft.DotNet.ApiCompat.Shared.projitems index 524152c56342..70da7b738d25 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/Microsoft.DotNet.ApiCompat.Shared.projitems +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/Microsoft.DotNet.ApiCompat.Shared.projitems @@ -11,8 +11,10 @@ - + + + diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssemblies.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssemblies.cs index 5ca7266c730c..a30b70b63561 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssemblies.cs +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssemblies.cs @@ -11,52 +11,35 @@ namespace Microsoft.DotNet.ApiCompat internal static class ValidateAssemblies { public static int Run(Func 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 leftMetadataInformation = GetMetadataInformation(leftAssemblies[i], GetAssemblyReferences(leftAssembliesReferences, i), leftAssembliesStringTransformer); - List rightMetadataInformation = GetMetadataInformation(rightAssemblies[i], GetAssemblyReferences(rightAssembliesReferences, i), rightAssembliesStringTransformer); + List leftMetadataInformation = GetMetadataInformation(options.LeftAssemblies[i], GetAssemblyReferences(options.LeftAssembliesReferences, i), leftAssembliesStringTransformer); + List rightMetadataInformation = GetMetadataInformation(options.RightAssemblies[i], GetAssemblyReferences(options.RightAssembliesReferences, i), rightAssembliesStringTransformer); // Enqueue the work item ApiCompatRunnerWorkItem workItem = new(leftMetadataInformation, apiCompatOptions, rightMetadataInformation); @@ -66,16 +49,16 @@ public static int Run(Func logFactory, else { // Create the work item that corresponds to the passed in left assembly. - List leftAssembliesMetadataInformation = new(leftAssemblies.Length); - for (int i = 0; i < leftAssemblies.Length; i++) + List 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 rightAssembliesMetadataInformation = new(rightAssemblies.Length); - for (int i = 0; i < rightAssemblies.Length; i++) + List 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 @@ -86,17 +69,17 @@ public static int Run(Func 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); } diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs new file mode 100644 index 000000000000..33fa6f5a5afa --- /dev/null +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs @@ -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 +{ + /// + /// Options for running . + /// + internal sealed class ValidateAssembliesOptions + { + public ValidateAssembliesOptions(string[] leftAssemblies, string[] rightAssemblies) + { + LeftAssemblies = leftAssemblies ?? throw new ArgumentNullException(nameof(leftAssemblies)); + RightAssemblies = rightAssemblies ?? throw new ArgumentNullException(nameof(rightAssemblies)); + } + + /// + /// The assemblies that represent the contract (left side). + /// + public string[] LeftAssemblies { get; } + + /// + /// The assemblies that represent the implementation (right side). + /// + public string[] RightAssemblies { get; } + + /// + /// If true, generates a suppression file that contains the api compatibility errors. + /// + public bool GenerateSuppressionFile { get; set; } + + /// + /// If true, preserves unnecessary suppressions when re-generating the suppression file. + /// + public bool PreserveUnnecessarySuppressions { get; set; } + + /// + /// If true, permits unnecessary suppressions in the suppression file. + /// + public bool PermitUnnecessarySuppressions { get; set; } + + /// + /// The path to one or more suppression files to read from. + /// + public string[]? SuppressionFiles { get; set; } + + /// + /// The path to a suppression output file to write to when is true. + /// + public string? SuppressionOutputFile { get; set; } + + /// + /// A NoWarn string that allows disabling specific rules. + /// + public string? NoWarn { get; set; } + + /// + /// If true, includes both internal and public API. + /// + public bool RespectInternals { get; set; } + + /// + /// Enables rule to check that attributes match. + /// + public bool EnableRuleAttributesMustMatch { get; set; } + + /// + /// The path to one or more attribute exclusion files with types in DocId format. + /// + public string[]? ExcludeAttributesFiles { get; set; } + + /// + /// Enables rule to check that the parameter names between public methods do not change. + /// + public bool EnableRuleCannotChangeParameterName { get; set; } + + /// + /// Performs api comparison checks in strict mode. + /// + public bool EnableStrictMode { get; set; } + + /// + /// The left assemblies' references. The index in the array maps to the index of the passed in left assembly. + /// + public string[][]? LeftAssembliesReferences { get; set; } + + /// + /// The right assemblies' references. The index in the array maps to the index of the passed in right assembly. + /// + public string[][]? RightAssembliesReferences { get; set; } + + /// + /// Create dedicated api compatibility checks for each left and right assembly tuple. + /// + public bool CreateWorkItemPerAssembly { get; set; } + + /// + /// Regex transformation patterns (regex + replacement string) that transform left assembly paths. + /// + public (string CaptureGroupPattern, string ReplacementString)[]? LeftAssembliesTransformationPatterns { get; set; } + + /// + /// Regex transformation patterns (regex + replacement string) that transform right assembly paths. + /// + public (string CaptureGroupPattern, string ReplacementString)[]? RightAssembliesTransformationPatterns { get; set; } + } +} diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackage.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackage.cs index 1b82442c985d..98168e4a14a3 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackage.cs +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackage.cs @@ -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 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>? packageAssemblyReferences, - IReadOnlyDictionary>? 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); } diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs new file mode 100644 index 000000000000..a401ef2ef27c --- /dev/null +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using NuGet.Frameworks; + +namespace Microsoft.DotNet.ApiCompat +{ + /// + /// Options for running . + /// + internal sealed class ValidatePackageOptions + { + public ValidatePackageOptions(string packagePath) + { + PackagePath = packagePath ?? throw new ArgumentNullException(nameof(packagePath)); + } + + /// + /// The path to the package to inspect. + /// + public string PackagePath { get; } + + /// + /// If true, generates a suppression file that contains the api compatibility errors. + /// + public bool GenerateSuppressionFile { get; set; } + + /// + /// If true, preserves unnecessary suppressions when re-generating the suppression file. + /// + public bool PreserveUnnecessarySuppressions { get; set; } + + /// + /// If true, permits unnecessary suppressions in the suppression file. + /// + public bool PermitUnnecessarySuppressions { get; set; } + + /// + /// The path to one or more suppression files to read from. + /// + public string[]? SuppressionFiles { get; set; } + + /// + /// The path to a suppression output file to write to when is true. + /// + public string? SuppressionOutputFile { get; set; } + + /// + /// A NoWarn string that allows disabling specific rules. + /// + public string? NoWarn { get; set; } + + /// + /// If true, includes both internal and public API. + /// + public bool RespectInternals { get; set; } + + /// + /// Enables rule to check that attributes match. + /// + public bool EnableRuleAttributesMustMatch { get; set; } + + /// + /// The path to one or more attribute exclusion files with types in DocId format. + /// + public string[]? ExcludeAttributesFiles { get; set; } + + /// + /// Enables rule to check that the parameter names between public methods do not change. + /// + public bool EnableRuleCannotChangeParameterName { get; set; } + + /// + /// If true, performs api compatibility checks on the package assets. + /// + public bool RunApiCompat { get; set; } = true; + + /// + /// Validates api compatibility in strict mode for contract and implementation assemblies for all compatible target frameworks. + /// + public bool EnableStrictModeForCompatibleTfms { get; set; } = true; + + /// + /// Validates api compatibility in strict mode for assemblies that are compatible based on their target framework. + /// + public bool EnableStrictModeForCompatibleFrameworksInPackage { get; set; } + + /// + /// Validates api compatibility in strict mode for package baseline checks. + /// + public bool EnableStrictModeForBaselineValidation { get; set; } + + /// + /// The path to a baseline package to validate against the current package. + /// + public string? BaselinePackagePath { get; set; } + + /// + /// A runtime graph that can be provided for package asset selection. + /// + public string? RuntimeGraph { get; set; } + + /// + /// Assembly references grouped by target framework, for the assets inside the package. + /// + public IReadOnlyDictionary>? PackageAssemblyReferences { get; set; } + + /// + /// Assembly references grouped by target framework, for the assets inside the baseline package. + /// + public IReadOnlyDictionary>? BaselinePackageAssemblyReferences { get; set; } + + /// + /// Target frameworks to ignore from the baseline package. + /// + public string[]? BaselinePackageFrameworksToIgnore { get; set; } + } +} diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs index 424d81b4862a..63166e69ea1f 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs @@ -132,26 +132,30 @@ public override bool Execute() protected override void ExecuteCore() { + if (LeftAssemblies is null) throw new ArgumentNullException(nameof(LeftAssemblies)); + if (RightAssemblies is null) throw new ArgumentNullException(nameof(RightAssemblies)); + SuppressibleMSBuildLog logFactory(ISuppressionEngine suppressionEngine) => new(Log, suppressionEngine, NoWarn); ValidateAssemblies.Run(logFactory, - GenerateSuppressionFile, - PreserveUnnecessarySuppressions, - PermitUnnecessarySuppressions, - SuppressionFiles, - SuppressionOutputFile, - NoWarn, - RespectInternals, - EnableRuleAttributesMustMatch, - ExcludeAttributesFiles, - EnableRuleCannotChangeParameterName, - LeftAssemblies!, - RightAssemblies!, - EnableStrictMode, - ParseAssembliesReferences(LeftAssembliesReferences), - ParseAssembliesReferences(RightAssembliesReferences), - CreateWorkItemPerAssembly, - ParseTransformationPattern(LeftAssembliesTransformationPattern), - ParseTransformationPattern(RightAssembliesTransformationPattern)); + new ValidateAssembliesOptions(LeftAssemblies, RightAssemblies) + { + GenerateSuppressionFile = GenerateSuppressionFile, + PreserveUnnecessarySuppressions = PreserveUnnecessarySuppressions, + PermitUnnecessarySuppressions = PermitUnnecessarySuppressions, + SuppressionFiles = SuppressionFiles, + SuppressionOutputFile = SuppressionOutputFile, + NoWarn = NoWarn, + RespectInternals = RespectInternals, + EnableRuleAttributesMustMatch = EnableRuleAttributesMustMatch, + ExcludeAttributesFiles = ExcludeAttributesFiles, + EnableRuleCannotChangeParameterName = EnableRuleCannotChangeParameterName, + EnableStrictMode = EnableStrictMode, + LeftAssembliesReferences = ParseAssembliesReferences(LeftAssembliesReferences), + RightAssembliesReferences = ParseAssembliesReferences(RightAssembliesReferences), + CreateWorkItemPerAssembly = CreateWorkItemPerAssembly, + LeftAssembliesTransformationPatterns = ParseTransformationPattern(LeftAssembliesTransformationPattern), + RightAssembliesTransformationPatterns = ParseTransformationPattern(RightAssembliesTransformationPattern), + }); if (SemaphoreFile != null) { diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs index 196d90c47272..5dcab0c4a5e2 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs @@ -140,28 +140,32 @@ public override bool Execute() protected override void ExecuteCore() { + if (PackageTargetPath is null) throw new ArgumentNullException(nameof(PackageTargetPath)); + 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); + new ValidatePackageOptions(PackageTargetPath) + { + GenerateSuppressionFile = GenerateSuppressionFile, + PreserveUnnecessarySuppressions = PreserveUnnecessarySuppressions, + PermitUnnecessarySuppressions = PermitUnnecessarySuppressions, + SuppressionFiles = SuppressionFiles, + SuppressionOutputFile = SuppressionOutputFile, + NoWarn = NoWarn, + RespectInternals = RespectInternals, + EnableRuleAttributesMustMatch = EnableRuleAttributesMustMatch, + ExcludeAttributesFiles = ExcludeAttributesFiles, + EnableRuleCannotChangeParameterName = EnableRuleCannotChangeParameterName, + RunApiCompat = RunApiCompat, + EnableStrictModeForCompatibleTfms = EnableStrictModeForCompatibleTfms, + EnableStrictModeForCompatibleFrameworksInPackage = EnableStrictModeForCompatibleFrameworksInPackage, + EnableStrictModeForBaselineValidation = EnableStrictModeForBaselineValidation, + BaselinePackagePath = BaselinePackageTargetPath, + RuntimeGraph = RuntimeGraph, + PackageAssemblyReferences = ParsePackageAssemblyReferences(PackageAssemblyReferences), + BaselinePackageAssemblyReferences = ParsePackageAssemblyReferences(BaselinePackageAssemblyReferences), + BaselinePackageFrameworksToIgnore = BaselinePackageFrameworksToIgnore, + }); } private static Dictionary>? ParsePackageAssemblyReferences(ITaskItem[]? packageAssemblyReferences) diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs index 4d1f8e416df8..ea6afd102be3 100644 --- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs +++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs @@ -198,24 +198,25 @@ static int Main(string[] args) SuppressibleConsoleLog logFactory(ISuppressionEngine suppressionEngine) => new(suppressionEngine, verbosity, noWarn); int exitCode = ValidateAssemblies.Run(logFactory, - generateSuppressionFile, - preserveUnnecessarySuppressions, - permitUnnecessarySuppressions, - suppressionFiles, - suppressionOutputFile, - noWarn, - respectInternals, - enableRuleAttributesMustMatch, - excludeAttributesFiles, - enableRuleCannotChangeParameterName, - leftAssemblies, - rightAssemblies, - strictMode, - leftAssembliesReferences, - rightAssembliesReferences, - createWorkItemPerAssembly, - leftAssembliesTransformationPattern, - rightAssembliesTransformationPattern); + new ValidateAssembliesOptions(leftAssemblies, rightAssemblies) + { + GenerateSuppressionFile = generateSuppressionFile, + PreserveUnnecessarySuppressions = preserveUnnecessarySuppressions, + PermitUnnecessarySuppressions = permitUnnecessarySuppressions, + SuppressionFiles = suppressionFiles, + SuppressionOutputFile = suppressionOutputFile, + NoWarn = noWarn, + RespectInternals = respectInternals, + EnableRuleAttributesMustMatch = enableRuleAttributesMustMatch, + ExcludeAttributesFiles = excludeAttributesFiles, + EnableRuleCannotChangeParameterName = enableRuleCannotChangeParameterName, + EnableStrictMode = strictMode, + LeftAssembliesReferences = leftAssembliesReferences, + RightAssembliesReferences = rightAssembliesReferences, + CreateWorkItemPerAssembly = createWorkItemPerAssembly, + LeftAssembliesTransformationPatterns = leftAssembliesTransformationPattern, + RightAssembliesTransformationPatterns = rightAssembliesTransformationPattern, + }); roslynResolver.Unregister(); @@ -310,6 +311,8 @@ static int Main(string[] args) bool enableRuleCannotChangeParameterName = parseResult.GetValue(enableRuleCannotChangeParameterNameOption); string? package = parseResult.GetValue(packageArgument); + if (package is null) throw new ArgumentNullException(nameof(package)); + bool runApiCompat = parseResult.GetValue(runApiCompatOption); bool enableStrictModeForCompatibleTfms = parseResult.GetValue(enableStrictModeForCompatibleTfmsOption); bool enableStrictModeForCompatibleFrameworksInPackage = parseResult.GetValue(enableStrictModeForCompatibleFrameworksInPackageOption); @@ -322,26 +325,28 @@ static int Main(string[] args) SuppressibleConsoleLog logFactory(ISuppressionEngine suppressionEngine) => new(suppressionEngine, verbosity, noWarn); int exitCode = ValidatePackage.Run(logFactory, - generateSuppressionFile, - preserveUnnecessarySuppressions, - permitUnnecessarySuppressions, - suppressionFiles, - suppressionOutputFile, - noWarn, - respectInternals, - enableRuleAttributesMustMatch, - excludeAttributesFiles, - enableRuleCannotChangeParameterName, - package, - runApiCompat, - enableStrictModeForCompatibleTfms, - enableStrictModeForCompatibleFrameworksInPackage, - enableStrictModeForBaselineValidation, - baselinePackage, - runtimeGraph, - packageAssemblyReferences, - baselinePackageAssemblyReferences, - baselinePackageFrameworksToIgnore); + new ValidatePackageOptions(package) + { + GenerateSuppressionFile = generateSuppressionFile, + PreserveUnnecessarySuppressions = preserveUnnecessarySuppressions, + PermitUnnecessarySuppressions = permitUnnecessarySuppressions, + SuppressionFiles = suppressionFiles, + SuppressionOutputFile = suppressionOutputFile, + NoWarn = noWarn, + RespectInternals = respectInternals, + EnableRuleAttributesMustMatch = enableRuleAttributesMustMatch, + ExcludeAttributesFiles = excludeAttributesFiles, + EnableRuleCannotChangeParameterName = enableRuleCannotChangeParameterName, + RunApiCompat = runApiCompat, + EnableStrictModeForCompatibleTfms = enableStrictModeForCompatibleTfms, + EnableStrictModeForCompatibleFrameworksInPackage = enableStrictModeForCompatibleFrameworksInPackage, + EnableStrictModeForBaselineValidation = enableStrictModeForBaselineValidation, + BaselinePackagePath = baselinePackage, + RuntimeGraph = runtimeGraph, + PackageAssemblyReferences = packageAssemblyReferences, + BaselinePackageAssemblyReferences = baselinePackageAssemblyReferences, + BaselinePackageFrameworksToIgnore = baselinePackageFrameworksToIgnore, + }); roslynResolver.Unregister(); @@ -376,7 +381,7 @@ private static string[] ParseAssemblyArgument(ArgumentResult argumentResult) private static (string CaptureGroupPattern, string ReplacementString)[]? ParseTransformationPattern(ArgumentResult argumentResult) { - var patterns = new(string CaptureGroupPattern, string ReplacementPattern)[argumentResult.Tokens.Count]; + var patterns = new (string CaptureGroupPattern, string ReplacementString)[argumentResult.Tokens.Count]; for (int i = 0; i < argumentResult.Tokens.Count; i++) { string[] parts = argumentResult.Tokens[i].Value.Split(';'); diff --git a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs index 0a8f5cbf47e7..02ab54101f22 100644 --- a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs +++ b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs @@ -183,18 +183,7 @@ private static Task HandleCommandAsync(DiffConfiguration diffConfig, Cancellatio } IDiffGenerator diffGenerator = DiffGeneratorFactory.Create(log, - diffConfig.BeforeAssembliesFolderPath, - diffConfig.BeforeAssemblyReferencesFolderPath, - diffConfig.AfterAssembliesFolderPath, - diffConfig.AfterAssemblyReferencesFolderPath, - diffConfig.OutputFolderPath, - diffConfig.BeforeFriendlyName, - diffConfig.AfterFriendlyName, - diffConfig.TableOfContentsTitle, - diffConfig.FilesWithAssembliesToExclude, - diffConfig.FilesWithAttributesToExclude, - diffConfig.FilesWithApisToExclude, - diffConfig.AddPartialModifier, + diffConfig, writeToDisk: true, diagnosticOptions: null // TODO: If needed, add CLI option to pass specific diagnostic options ); diff --git a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs index bc30f66251da..e158e8dfe0cb 100644 --- a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs +++ b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs @@ -24,51 +24,29 @@ public static class DiffGeneratorFactory /// Creates a new instance of that writes the diff to disk. /// /// The logger to use for logging messages. - /// The folder path containing the assemblies before the change. - /// The folder path containing the assembly references before the change. - /// The folder path containing the assemblies after the change. - /// The folder path containing the assembly references after the change. - /// The folder path where the output will be written. - /// The friendly name for the assemblies before the change. - /// The friendly name for the assemblies after the change. - /// The title for the table of contents in the generated diff. - /// An optional array of filepaths each containing a list of assemblies to avoid showing in the diff. If , no assemblies are excluded. - /// An optional array of filepaths each containing a list of attributes to avoid showing in the diff. - /// An optional array of filepaths each containing a list of APIs to avoid showing in the diff. - /// Indicates whether to add the partial modifier to types. + /// The configuration options for the diff. /// If , when calling , the generated markdown files get written to disk, and no item is added to the dictionary. If , when calling , the generated markdown files get added to the dictionary (with the file path as the dictionary key) and none of them is written to disk. This is meant for testing purposes. /// An optional list of diagnostic options to use when generating the diff. /// A new instance of that writes the diff to disk. - /// + public static IDiffGenerator Create(ILog log, - string beforeAssembliesFolderPath, - string? beforeAssemblyReferencesFolderPath, - string afterAssembliesFolderPath, - string? afterAssemblyReferencesFolderPath, - string outputFolderPath, - string beforeFriendlyName, - string afterFriendlyName, - string tableOfContentsTitle, - FileInfo[]? filesWithAssembliesToExclude, - FileInfo[]? filesWithAttributesToExclude, - FileInfo[]? filesWithApisToExclude, - bool addPartialModifier, + DiffConfiguration diffConfig, bool writeToDisk, IEnumerable>? diagnosticOptions = null) { return new FileOutputDiffGenerator(log, - beforeAssembliesFolderPath, - beforeAssemblyReferencesFolderPath, - afterAssembliesFolderPath, - afterAssemblyReferencesFolderPath, - outputFolderPath, - beforeFriendlyName, - afterFriendlyName, - tableOfContentsTitle, - filesWithAssembliesToExclude, - filesWithAttributesToExclude, - filesWithApisToExclude, - addPartialModifier, + diffConfig.BeforeAssembliesFolderPath, + diffConfig.BeforeAssemblyReferencesFolderPath, + diffConfig.AfterAssembliesFolderPath, + diffConfig.AfterAssemblyReferencesFolderPath, + diffConfig.OutputFolderPath, + diffConfig.BeforeFriendlyName, + diffConfig.AfterFriendlyName, + diffConfig.TableOfContentsTitle, + diffConfig.FilesWithAssembliesToExclude, + diffConfig.FilesWithAttributesToExclude, + diffConfig.FilesWithApisToExclude, + diffConfig.AddPartialModifier, writeToDisk, diagnosticOptions); } diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Task/GenAPITask.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Task/GenAPITask.cs index 674c6f68b212..34263794d8cd 100644 --- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Task/GenAPITask.cs +++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Task/GenAPITask.cs @@ -66,15 +66,17 @@ protected override void ExecuteCore() Debug.Assert(Assemblies != null, "Assemblies cannot be null."); GenAPIApp.Run(new MSBuildLog(Log), - Assemblies, - AssemblyReferences, - OutputPath, - HeaderFile, - ExceptionMessage, - ExcludeApiFiles, - ExcludeAttributesFiles, - RespectInternals, - IncludeAssemblyAttributes); + new GenAPIOptions(Assemblies) + { + AssemblyReferencesPaths = AssemblyReferences, + OutputPath = OutputPath, + HeaderFile = HeaderFile, + ExceptionMessage = ExceptionMessage, + ExcludeApiFiles = ExcludeApiFiles, + ExcludeAttributesFiles = ExcludeAttributesFiles, + RespectInternals = RespectInternals, + IncludeAssemblyAttributes = IncludeAssemblyAttributes, + }); } } } diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Tool/Program.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Tool/Program.cs index 0595e4c0c5b5..f4b01e10dce5 100644 --- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Tool/Program.cs +++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Tool/Program.cs @@ -105,16 +105,17 @@ static int Main(string[] args) Debug.Assert(assemblies != null, "Assemblies cannot be null."); GenAPIApp.Run(log, - assemblies, - parseResult.GetValue(assemblyReferencesOption), - parseResult.GetValue(outputPathOption), - parseResult.GetValue(headerFileOption), - parseResult.GetValue(exceptionMessageOption), - parseResult.GetValue(excludeApiFilesOption), - parseResult.GetValue(excludeAttributesFilesOption), - respectInternals, - parseResult.GetValue(includeAssemblyAttributesOption) - ); + new GenAPIOptions(assemblies) + { + AssemblyReferencesPaths = parseResult.GetValue(assemblyReferencesOption), + OutputPath = parseResult.GetValue(outputPathOption), + HeaderFile = parseResult.GetValue(headerFileOption), + ExceptionMessage = parseResult.GetValue(exceptionMessageOption), + ExcludeApiFiles = parseResult.GetValue(excludeApiFilesOption), + ExcludeAttributesFiles = parseResult.GetValue(excludeAttributesFilesOption), + RespectInternals = respectInternals, + IncludeAssemblyAttributes = parseResult.GetValue(includeAssemblyAttributesOption), + }); }); return rootCommand.Parse(args).Invoke(); diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs index 25ef58d6481b..50d30828d6f1 100644 --- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs +++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs @@ -18,36 +18,27 @@ namespace Microsoft.DotNet.GenAPI public static class GenAPIApp { /// - /// Initialize and run Roslyn-based GenAPI tool specifying the assemblies to load. + /// Initialize and run Roslyn-based GenAPI tool using . /// - public static void Run(ILog log, - string[] assembliesPaths, - string[]? assemblyReferencesPaths, - string? outputPath, - string? headerFile, - string? exceptionMessage, - string[]? excludeApiFiles, - string[]? excludeAttributesFiles, - bool respectInternals, - bool includeAssemblyAttributes) + public static void Run(ILog log, GenAPIOptions options) { (IAssemblySymbolLoader loader, Dictionary assemblySymbols) = AssemblySymbolLoader.CreateFromFiles( log, - assembliesPaths, - assemblyReferencesPaths, + options.AssembliesPaths, + options.AssemblyReferencesPaths, assembliesToExclude: [], - respectInternals: respectInternals); + respectInternals: options.RespectInternals); Run(log, loader, assemblySymbols, - outputPath, - headerFile, - exceptionMessage, - excludeApiFiles, - excludeAttributesFiles, - respectInternals, - includeAssemblyAttributes); + options.OutputPath, + options.HeaderFile, + options.ExceptionMessage, + options.ExcludeApiFiles, + options.ExcludeAttributesFiles, + options.RespectInternals, + options.IncludeAssemblyAttributes); } /// diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs new file mode 100644 index 000000000000..0f7f2b51f4b3 --- /dev/null +++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs @@ -0,0 +1,62 @@ +// 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.GenAPI; + +/// +/// Options for running GenAPI via . +/// +public sealed class GenAPIOptions +{ + public GenAPIOptions(string[] assembliesPaths) + { + AssembliesPaths = assembliesPaths ?? throw new ArgumentNullException(nameof(assembliesPaths)); + } + + /// + /// The path to one or more assemblies or directories with assemblies. + /// + public string[] AssembliesPaths { get; } + + /// + /// Paths to assembly references or their underlying directories. + /// + public string[]? AssemblyReferencesPaths { get; set; } + + /// + /// Output path. Default is the console. Can specify an existing directory as well and + /// then a file will be created for each assembly with the matching name of the assembly. + /// + public string? OutputPath { get; set; } + + /// + /// Specify a file with an alternate header content to prepend to output. + /// + public string? HeaderFile { get; set; } + + /// + /// When set, method bodies will consist of throw new PlatformNotSupportedException(ExceptionMessage);. + /// When is , method bodies will instead be emitted as throw null;. + /// + public string? ExceptionMessage { get; set; } + + /// + /// The path to one or more api exclusion files with types in DocId format. + /// + public string[]? ExcludeApiFiles { get; set; } + + /// + /// The path to one or more attribute exclusion files with types in DocId format. + /// + public string[]? ExcludeAttributesFiles { get; set; } + + /// + /// If true, includes both internal and public API. + /// + public bool RespectInternals { get; set; } + + /// + /// Includes assembly attributes which are values that provide information about an assembly. + /// + public bool IncludeAssemblyAttributes { get; set; } +} diff --git a/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Disk.Tests.cs b/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Disk.Tests.cs index 97af160dbaad..c1b8894f97a5 100644 --- a/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Disk.Tests.cs +++ b/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Disk.Tests.cs @@ -473,18 +473,20 @@ private IDiffGenerator TestDiskShared( Mock log = new(); return DiffGeneratorFactory.Create(log.Object, - beforeAssembliesFolderPath, - beforeAssemblyReferencesFolderPath: null, - afterAssembliesFolderPath, - afterAssemblyReferencesFolderPath: null, - outputFolderPath, - beforeFriendlyName, - afterFriendlyName, - tableOfContentsTitle, - filesWithAssembliesToExclude ?? [], - filesWithAttributesToExclude ?? [], - filesWithAPIsToExclude ?? [], - addPartialModifier: false, + new DiffConfiguration( + AfterAssembliesFolderPath: afterAssembliesFolderPath, + AfterAssemblyReferencesFolderPath: null, + BeforeAssembliesFolderPath: beforeAssembliesFolderPath, + BeforeAssemblyReferencesFolderPath: null, + OutputFolderPath: outputFolderPath, + BeforeFriendlyName: beforeFriendlyName, + AfterFriendlyName: afterFriendlyName, + TableOfContentsTitle: tableOfContentsTitle, + FilesWithAssembliesToExclude: filesWithAssembliesToExclude ?? [], + FilesWithAttributesToExclude: filesWithAttributesToExclude ?? [], + FilesWithApisToExclude: filesWithAPIsToExclude ?? [], + AddPartialModifier: false, + AttachDebugger: false), writeToDisk, DiffGeneratorFactory.DefaultDiagnosticOptions); }