Esempio n. 1
0
        private static BaselineDifferenceFilter GetBaselineDifferenceFilter(string[] baselineFileNames, bool validateBaseline)
        {
            BaselineDifferenceFilter baselineDifferenceFilter = null;

            AddFiles(baselineFileNames, (file) =>
                     (baselineDifferenceFilter ??= new BaselineDifferenceFilter(new DifferenceFilter <IncompatibleDifference>(), validateBaseline)).AddBaselineFile(file));

            return(baselineDifferenceFilter);
        }
Esempio n. 2
0
        private static BaselineDifferenceFilter GetBaselineDifferenceFilter(IEnumerable <string> baselineFileNames, bool validateBaseline)
        {
            if (baselineFileNames == null)
            {
                return(null);
            }

            BaselineDifferenceFilter baselineDifferenceFilter = null;

            AddFiles(baselineFileNames, (file) =>
                     (baselineDifferenceFilter ??= new BaselineDifferenceFilter(new DifferenceFilter <IncompatibleDifference>(), validateBaseline)).AddBaselineFile(file));

            return(baselineDifferenceFilter);
        }
Esempio n. 3
0
        private static BaselineDifferenceFilter GetBaselineDifferenceFilter(string baselineFileName)
        {
            BaselineDifferenceFilter filter = null;

            if (!string.IsNullOrEmpty(baselineFileName))
            {
                if (!File.Exists(baselineFileName))
                {
                    throw new FileNotFoundException("Baseline file {0} was not found!", baselineFileName);
                }
                IDifferenceFilter incompatibleFilter = new DifferenceFilter <IncompatibleDifference>();
                filter = new BaselineDifferenceFilter(incompatibleFilter, baselineFileName);
            }
            return(filter);
        }
Esempio n. 4
0
        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));
        }
Esempio n. 5
0
        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);
                }
            }
        }
Esempio n. 6
0
        /// <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);
                }
            }
        }