|
// 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;
using Microsoft.DotNet.ApiCompatibility.Logging;
using Microsoft.DotNet.ApiCompatibility.Rules;
using Microsoft.DotNet.ApiCompatibility.Runner;
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)
{
// Initialize the service provider
ApiCompatServiceProvider serviceProvider = new(logFactory,
() => SuppressionFileHelper.CreateSuppressionEngine(suppressionFiles, noWarn, generateSuppressionFile),
(log) => new RuleFactory(log,
enableRuleAttributesMustMatch,
enableRuleCannotChangeParameterName),
respectInternals,
excludeAttributesFiles);
IApiCompatRunner apiCompatRunner = serviceProvider.ApiCompatRunner;
ApiCompatRunnerOptions apiCompatOptions = new(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;
if (createWorkItemPerAssembly)
{
if (leftAssemblies.Length != rightAssemblies.Length)
{
throw new Exception(CommonResources.CreateWorkItemPerAssemblyAssembliesNotEqual);
}
for (int i = 0; i < leftAssemblies.Length; i++)
{
List<MetadataInformation> leftMetadataInformation = GetMetadataInformation(leftAssemblies[i], GetAssemblyReferences(leftAssembliesReferences, i), leftAssembliesStringTransformer);
List<MetadataInformation> rightMetadataInformation = GetMetadataInformation(rightAssemblies[i], GetAssemblyReferences(rightAssembliesReferences, i), rightAssembliesStringTransformer);
// Enqueue the work item
ApiCompatRunnerWorkItem workItem = new(leftMetadataInformation, apiCompatOptions, rightMetadataInformation);
apiCompatRunner.EnqueueWorkItem(workItem);
}
}
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++)
{
leftAssembliesMetadataInformation.AddRange(GetMetadataInformation(leftAssemblies[i], GetAssemblyReferences(leftAssembliesReferences, i), leftAssembliesStringTransformer));
}
List<MetadataInformation> rightAssembliesMetadataInformation = new(rightAssemblies.Length);
for (int i = 0; i < rightAssemblies.Length; i++)
{
rightAssembliesMetadataInformation.AddRange(GetMetadataInformation(rightAssemblies[i], GetAssemblyReferences(rightAssembliesReferences, i), rightAssembliesStringTransformer));
}
// Enqueue the work item
ApiCompatRunnerWorkItem workItem = new(leftAssembliesMetadataInformation, apiCompatOptions, rightAssembliesMetadataInformation);
apiCompatRunner.EnqueueWorkItem(workItem);
}
// Execute the enqueued work item(s).
apiCompatRunner.ExecuteWorkItems();
SuppressionFileHelper.LogApiCompatSuccessOrFailure(generateSuppressionFile, serviceProvider.SuppressibleLog);
if (generateSuppressionFile)
{
SuppressionFileHelper.GenerateSuppressionFile(serviceProvider.SuppressionEngine,
serviceProvider.SuppressibleLog,
preserveUnnecessarySuppressions,
suppressionFiles,
suppressionOutputFile);
}
else if (!permitUnnecessarySuppressions)
{
SuppressionFileHelper.ValidateUnnecessarySuppressions(serviceProvider.SuppressionEngine, serviceProvider.SuppressibleLog);
}
return serviceProvider.SuppressibleLog.HasLoggedErrorSuppressions ? 1 : 0;
}
private static string[]? GetAssemblyReferences(string[][]? assemblyReferences, int counter)
{
if (assemblyReferences == null || assemblyReferences.Length == 0)
return null;
if (assemblyReferences.Length > counter)
{
return assemblyReferences[counter];
}
// If explicit assembly references weren't provided for an assembly, return the ones provided first
// so that consumers can provide one shareable set of references for all left/right inputs.
return assemblyReferences[0];
}
private static List<MetadataInformation> GetMetadataInformation(string path,
IEnumerable<string>? assemblyReferences,
RegexStringTransformer? regexStringTransformer)
{
List<MetadataInformation> metadataInformation = [];
foreach (string assembly in GetFilesFromPath(path))
{
metadataInformation.Add(new MetadataInformation(
assemblyName: Path.GetFileNameWithoutExtension(assembly),
assemblyId: regexStringTransformer?.Transform(assembly) ?? assembly,
fullPath: assembly,
references: assemblyReferences));
}
return metadataInformation;
}
private static IEnumerable<string> GetFilesFromPath(string path)
{
// Check if the given path is a directory
if (Directory.Exists(path))
{
return Directory.EnumerateFiles(path, "*.dll");
}
// If the path isn't a directory, see if it's a glob expression.
string filename = Path.GetFileName(path);
#if NET
if (filename.Contains('*'))
#else
if (filename.Contains("*"))
#endif
{
string? directoryName = Path.GetDirectoryName(path);
if (directoryName != null)
{
try
{
return Directory.EnumerateFiles(directoryName, filename);
}
catch (ArgumentException)
{
}
}
}
return [path];
}
}
}
|