private static void WriteDiff(DiffConfiguration configuration, ICciDifferenceWriter writer) { var oldSet = configuration.Left; var oldAssemblies = oldSet.Assemblies; var oldAssembliesName = oldSet.Name; // The diff writer special cases the name being null // to indicated that there is only one "set". var newSet = configuration.Right; var newAssembliesName = newSet.IsNull ? null : newSet.Name; var newAssemblies = newSet.Assemblies; writer.Write(oldAssembliesName, oldAssemblies, newAssembliesName, newAssemblies); }
public static int Main(string[] args) { var app = new CommandLineApplication { Name = "ApiCompat", FullName = "A command line tool to verify that two sets of APIs are compatible.", ResponseFileHandling = ResponseFileHandling.ParseArgsAsSpaceSeparated }; app.HelpOption("-?|-h|--help"); app.VersionOption("-v|--version", GetAssemblyVersion()); CommandArgument contracts = app.Argument("contracts", "Comma delimited list of assemblies or directories of assemblies for all the contract assemblies."); contracts.IsRequired(); CommandOption implDirs = app.Option("-i|--impl-dirs", "Comma delimited list of directories to find the implementation assemblies for each contract assembly.", CommandOptionType.SingleValue); implDirs.IsRequired(allowEmptyStrings: true); CommandOption baseline = app.Option("-b|--baseline", "Baseline file to skip known diffs.", CommandOptionType.SingleValue); CommandOption mdil = app.Option("-m|--mdil", "Enforce MDIL servicing rules in addition to IL rules.", CommandOptionType.NoValue); CommandOption outFilePath = app.Option("-o|--out", "Output file path. Default is the console.", CommandOptionType.SingleValue); CommandOption leftOperand = app.Option("-l|--left-operand", "Name for left operand in comparison, default is 'contract'.", CommandOptionType.SingleValue); CommandOption rightOperand = app.Option("-r|--right-operand", "Name for right operand in comparison, default is 'implementation'.", CommandOptionType.SingleValue); CommandOption listRules = app.Option("--list-rules", "Outputs all the rules. If this options is supplied all other options are ignored.", CommandOptionType.NoValue); CommandOption remapFile = app.Option("--remap-file", "File with a list of type and/or namespace remappings to consider apply to names while diffing.", CommandOptionType.SingleValue); CommandOption skipGroupByAssembly = app.Option("--skip-group-by-assembly", "Skip grouping the differences by assembly instead of flattening the namespaces.", CommandOptionType.NoValue); CommandOption skipUnifyToLibPath = app.Option("--skip-unify-to-lib-path", "Skip unifying the assembly references to the loaded assemblies and the assemblies found in the given directories (contractDepends and implDirs).", CommandOptionType.NoValue); CommandOption resolveFx = app.Option("--resolve-fx", "If a contract or implementation dependency cannot be found in the given directories, fallback to try to resolve against the framework directory on the machine.", CommandOptionType.NoValue); CommandOption contractDepends = app.Option("--contract-depends", "Comma delimited list of directories used to resolve the dependencies of the contract assemblies.", CommandOptionType.SingleValue); CommandOption contractCoreAssembly = app.Option("--contract-core-assembly", "Simple name for the core assembly to use.", CommandOptionType.SingleValue); CommandOption ignoreDesignTimeFacades = app.Option("--ignore-design-time-facades", "Ignore design time facades in the contract set while analyzing.", CommandOptionType.NoValue); CommandOption warnOnIncorrectVersion = app.Option("--warn-on-incorrect-version", "Warn if the contract version number doesn't match the found implementation version number.", CommandOptionType.NoValue); CommandOption warnOnMissingAssemblies = app.Option("--warn-on-missing-assemblies", "Warn if the contract assembly cannot be found in the implementation directories. Default is to error and not do analysis.", CommandOptionType.NoValue); CommandOption excludeNonBrowsable = app.Option("--exclude-non-browsable", "When MDIL servicing rules are not being enforced, exclude validation on types that are marked with EditorBrowsable(EditorBrowsableState.Never).", CommandOptionType.NoValue); CommandOption excludeAttributes = app.Option("--exclude-attributes", "Specify a api list in the DocId format of which attributes to exclude.", CommandOptionType.SingleValue); CommandOption enforceOptionalRules = app.Option("--enforce-optional-rules", "Enforce optional rules, in addition to the mandatory set of rules.", CommandOptionType.NoValue); CommandOption allowDefaultInterfaceMethods = app.Option("--allow-default-interface-methods", "Allow default interface methods additions to not be considered breaks. This flag should only be used if you know your consumers support DIM", CommandOptionType.NoValue); app.OnExecute(() => { string leftOperandValue = leftOperand.HasValue() ? leftOperand.Value() : "contract"; string rightOperandValue = rightOperand.HasValue() ? rightOperand.Value() : "implementation"; if (listRules.HasValue()) { CompositionHost c = GetCompositionHost(); ExportCciSettings.StaticSettings = CciComparers.Default.GetEqualityComparer <ITypeReference>(); var rules = c.GetExports <IDifferenceRule>(); foreach (var rule in rules.OrderBy(r => r.GetType().Name, StringComparer.OrdinalIgnoreCase)) { string ruleName = rule.GetType().Name; if (IsOptionalRule(rule)) { ruleName += " (optional)"; } Console.WriteLine(ruleName); } return(0); } using (TextWriter output = GetOutput(outFilePath.Value())) { if (DifferenceWriter.ExitCode != 0) { return(0); } if (output != Console.Out) { Trace.Listeners.Add(new TextWriterTraceListener(output) { Filter = new EventTypeFilter(SourceLevels.Error | SourceLevels.Warning) }); } try { BaselineDifferenceFilter filter = GetBaselineDifferenceFilter(baseline.Value()); NameTable sharedNameTable = new NameTable(); HostEnvironment contractHost = new HostEnvironment(sharedNameTable); contractHost.UnableToResolve += (sender, e) => Trace.TraceError($"Unable to resolve assembly '{e.Unresolved}' referenced by the {leftOperandValue} assembly '{e.Referrer}'."); contractHost.ResolveAgainstRunningFramework = resolveFx.HasValue(); contractHost.UnifyToLibPath = !skipUnifyToLibPath.HasValue(); contractHost.AddLibPaths(HostEnvironment.SplitPaths(contractDepends.Value())); IEnumerable <IAssembly> contractAssemblies = contractHost.LoadAssemblies(contracts.Value, contractCoreAssembly.Value()); if (ignoreDesignTimeFacades.HasValue()) { contractAssemblies = contractAssemblies.Where(a => !a.IsFacade()); } HostEnvironment implHost = new HostEnvironment(sharedNameTable); implHost.UnableToResolve += (sender, e) => Trace.TraceError($"Unable to resolve assembly '{e.Unresolved}' referenced by the {rightOperandValue} assembly '{e.Referrer}'."); implHost.ResolveAgainstRunningFramework = resolveFx.HasValue(); implHost.UnifyToLibPath = !skipUnifyToLibPath.HasValue(); implHost.AddLibPaths(HostEnvironment.SplitPaths(implDirs.Value())); if (warnOnMissingAssemblies.HasValue()) { implHost.LoadErrorTreatment = ErrorTreatment.TreatAsWarning; } // The list of contractAssemblies already has the core assembly as the first one (if _contractCoreAssembly was specified). IEnumerable <IAssembly> implAssemblies = implHost.LoadAssemblies(contractAssemblies.Select(a => a.AssemblyIdentity), warnOnIncorrectVersion.HasValue()); // Exit after loading if the code is set to non-zero if (DifferenceWriter.ExitCode != 0) { return(0); } ICciDifferenceWriter writer = GetDifferenceWriter(output, filter, enforceOptionalRules.HasValue(), mdil.HasValue(), excludeNonBrowsable.HasValue(), remapFile.Value(), !skipGroupByAssembly.HasValue(), leftOperandValue, rightOperandValue, excludeAttributes.Value(), allowDefaultInterfaceMethods.HasValue()); writer.Write(implDirs.Value(), implAssemblies, contracts.Value, contractAssemblies); return(0); } catch (FileNotFoundException) { // FileNotFoundException will be thrown by GetBaselineDifferenceFilter if it doesn't find the baseline file // OR if GetComparers doesn't find the remap file. return(2); } } }); return(app.Execute(args)); }
public static int Main(string[] args) { ParseCommandLine(args); CommandLineTraceHandler.Enable(); if (s_listRules) { CompositionHost c = GetCompositionHost(); ExportCciSettings.StaticSettings = CciComparers.Default.GetEqualityComparer <ITypeReference>(); var rules = c.GetExports <IDifferenceRule>(); foreach (var rule in rules.Select(r => r.GetType().Name).OrderBy(r => r, StringComparer.OrdinalIgnoreCase)) { Console.WriteLine(rule); } return(0); } using (TextWriter output = GetOutput()) { if (DifferenceWriter.ExitCode != 0) { return(0); } if (output != Console.Out) { Trace.Listeners.Add(new TextWriterTraceListener(output) { Filter = new EventTypeFilter(SourceLevels.Error | SourceLevels.Warning) }); } try { BaselineDifferenceFilter filter = GetBaselineDifferenceFilter(); NameTable sharedNameTable = new NameTable(); HostEnvironment contractHost = new HostEnvironment(sharedNameTable); contractHost.UnableToResolve += new EventHandler <UnresolvedReference <IUnit, AssemblyIdentity> >(contractHost_UnableToResolve); contractHost.ResolveAgainstRunningFramework = s_resolveFx; contractHost.UnifyToLibPath = s_unifyToLibPaths; contractHost.AddLibPaths(HostEnvironment.SplitPaths(s_contractLibDirs)); IEnumerable <IAssembly> contractAssemblies = contractHost.LoadAssemblies(s_contractSet, s_contractCoreAssembly); if (s_ignoreDesignTimeFacades) { contractAssemblies = contractAssemblies.Where(a => !a.IsFacade()); } HostEnvironment implHost = new HostEnvironment(sharedNameTable); implHost.UnableToResolve += new EventHandler <UnresolvedReference <IUnit, AssemblyIdentity> >(implHost_UnableToResolve); implHost.ResolveAgainstRunningFramework = s_resolveFx; implHost.UnifyToLibPath = s_unifyToLibPaths; implHost.AddLibPaths(HostEnvironment.SplitPaths(s_implDirs)); if (s_warnOnMissingAssemblies) { implHost.LoadErrorTreatment = ErrorTreatment.TreatAsWarning; } // The list of contractAssemblies already has the core assembly as the first one (if _contractCoreAssembly was specified). IEnumerable <IAssembly> implAssemblies = implHost.LoadAssemblies(contractAssemblies.Select(a => a.AssemblyIdentity), s_warnOnIncorrectVersion); // Exit after loading if the code is set to non-zero if (DifferenceWriter.ExitCode != 0) { return(0); } ICciDifferenceWriter writer = GetDifferenceWriter(output, filter); writer.Write(s_implDirs, implAssemblies, s_contractSet, contractAssemblies); return(0); } catch (FileNotFoundException) { // FileNotFoundException will be thrown by GetBaselineDifferenceFilter if it doesn't find the baseline file // OR if GetComparers doesn't find the remap file. return(2); } } }
/// <summary> /// The core part of ApiCompat which accepts a given set of arguments and /// performs api compatibility checks. /// </summary> public static int Run(bool usesMSBuildLog, bool disableAssemblyResolveTraceListener, IEnumerable <string> contracts, IEnumerable <string> implementationDirectories, TextWriter output, string rightOperand = "implementation", string leftOperand = "contract", bool listRules = false, IEnumerable <string> baselineFileNames = null, bool validateBaseline = false, bool resolveFramework = false, bool skipUnifyToLibPath = false, IEnumerable <string> contractDependsFileNames = null, string contractCoreAssembly = null, bool ignoreDesignTimeFacades = false, bool warnOnMissingAssemblies = false, bool respectInternals = false, bool warnOnIncorrectVersion = false, bool enforceOptionalRules = false, bool mdil = false, bool excludeNonBrowsable = false, bool excludeCompilerGenerated = false, string remapFile = null, bool skipGroupByAssembly = false, IEnumerable <string> excludeAttributes = null, bool allowDefaultInterfaceMethods = false) { // Clear exit code from previous runs on the same domain given this is a static property. DifferenceWriter.ExitCode = 0; if (listRules) { CompositionHost c = GetCompositionHost(); ExportCciSettings.StaticSettings = CciComparers.Default.GetEqualityComparer <ITypeReference>(); IEnumerable <IDifferenceRule> rules = c.GetExports <IDifferenceRule>(); foreach (IDifferenceRule rule in rules.OrderBy(r => r.GetType().Name, StringComparer.OrdinalIgnoreCase)) { string ruleName = rule.GetType().Name; if (IsOptionalRule(rule)) { ruleName += " (optional)"; } output.WriteLine(ruleName); } return(0); } using (output) { if (DifferenceWriter.ExitCode != 0) { return(0); } if (!disableAssemblyResolveTraceListener) { Trace.Listeners.Add(new TextWriterTraceListener(output) { Filter = new EventTypeFilter(SourceLevels.Error | SourceLevels.Warning) }); } try { BaselineDifferenceFilter filter = GetBaselineDifferenceFilter(baselineFileNames, validateBaseline); NameTable sharedNameTable = new(); HostEnvironment contractHost = new(sharedNameTable); contractHost.UnableToResolve += (sender, e) => Trace.TraceError($"Unable to resolve assembly '{e.Unresolved}' referenced by the {leftOperand} assembly '{e.Referrer}'."); contractHost.ResolveAgainstRunningFramework = resolveFramework; contractHost.UnifyToLibPath = !skipUnifyToLibPath; contractHost.AddLibPaths(contractDependsFileNames); IEnumerable <IAssembly> contractAssemblies = contractHost.LoadAssemblies(contracts, contractCoreAssembly); if (ignoreDesignTimeFacades) { contractAssemblies = contractAssemblies.Where(a => !a.IsFacade()); } HostEnvironment implHost = new(sharedNameTable); implHost.UnableToResolve += (sender, e) => Trace.TraceError($"Unable to resolve assembly '{e.Unresolved}' referenced by the {rightOperand} assembly '{e.Referrer}'."); implHost.ResolveAgainstRunningFramework = resolveFramework; implHost.UnifyToLibPath = !skipUnifyToLibPath; implHost.AddLibPaths(implementationDirectories); if (warnOnMissingAssemblies) { implHost.LoadErrorTreatment = ErrorTreatment.TreatAsWarning; } // The list of contractAssemblies already has the core assembly as the first one (if _contractCoreAssembly was specified). IEnumerable <IAssembly> implAssemblies = implHost.LoadAssemblies(contractAssemblies.Select(a => a.AssemblyIdentity), warnOnIncorrectVersion); // Exit after loading if the code is set to non-zero if (DifferenceWriter.ExitCode != 0) { return(0); } bool includeInternals = respectInternals && contractAssemblies.Any(assembly => assembly.Attributes.HasAttributeOfType( "System.Runtime.CompilerServices.InternalsVisibleToAttribute")); ICciDifferenceWriter writer = GetDifferenceWriter( output, filter, enforceOptionalRules, mdil, excludeNonBrowsable, includeInternals, excludeCompilerGenerated, remapFile, !skipGroupByAssembly, leftOperand, rightOperand, excludeAttributes, allowDefaultInterfaceMethods, usesMSBuildLog); writer.Write(string.Join(",", implementationDirectories), implAssemblies, string.Join(",", contracts), contractAssemblies); return(0); } catch (FileNotFoundException) { // FileNotFoundException will be thrown by GetBaselineDifferenceFilter if it doesn't find the baseline file // OR if GetComparers doesn't find the remap file. return(2); } } }