From 93192d95d65312b5a9bfbc9c96ed4e64dae4ea4c Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Mon, 26 Sep 2022 15:35:24 -0700 Subject: [PATCH] Revert "Adapt System.CommandLine (v2) in more places (#72082)" This reverts commit d0559e6e33906e4e6db675e646b47ab36eb5bffc. --- .../tools/Common/CommandLineHelpers.cs | 291 -------- .../DependencyGraphTests.cs | 2 +- .../Compiler/UsageBasedMetadataManager.cs | 2 - .../tools/aot/ILCompiler/ILCompiler.props | 21 +- .../aot/ILCompiler/ILCompilerRootCommand.cs | 390 ---------- src/coreclr/tools/aot/ILCompiler/Program.cs | 691 ++++++++++++++---- .../TestCasesRunner/ILCompilerDriver.cs | 1 - .../tools/aot/crossgen2/CommandLineOptions.cs | 273 +++++++ .../aot/crossgen2/Crossgen2RootCommand.cs | 376 ---------- src/coreclr/tools/aot/crossgen2/Program.cs | 494 +++++++++---- .../aot/crossgen2/Properties/Resources.resx | 5 +- .../tools/aot/crossgen2/crossgen2.props | 20 +- 12 files changed, 1196 insertions(+), 1370 deletions(-) delete mode 100644 src/coreclr/tools/Common/CommandLineHelpers.cs delete mode 100644 src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs create mode 100644 src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs delete mode 100644 src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs diff --git a/src/coreclr/tools/Common/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLineHelpers.cs deleted file mode 100644 index 2e8028abfd841a..00000000000000 --- a/src/coreclr/tools/Common/CommandLineHelpers.cs +++ /dev/null @@ -1,291 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.CommandLine.Binding; -using System.CommandLine.Parsing; -using System.IO; -using System.IO.Compression; -using System.Runtime.InteropServices; - -using Internal.TypeSystem; - -namespace System.CommandLine -{ - internal sealed class CommandLineException : Exception - { - public CommandLineException(string message) : base(message) { } - } - - // - // Helpers for command line processing - // - internal static class Helpers - { - public const string DefaultSystemModule = "System.Private.CoreLib"; - - public static Dictionary BuildPathDictionay(IReadOnlyList tokens, bool strict) - { - Dictionary dictionary = new(StringComparer.OrdinalIgnoreCase); - - foreach (Token token in tokens) - { - AppendExpandedPaths(dictionary, token.Value, strict); - } - - return dictionary; - } - - public static TargetOS GetTargetOS(string token) - { - if(string.IsNullOrEmpty(token)) - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return TargetOS.Windows; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - return TargetOS.Linux; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - return TargetOS.OSX; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) - return TargetOS.FreeBSD; - - throw new NotImplementedException(); - } - - if (token.Equals("windows", StringComparison.OrdinalIgnoreCase)) - return TargetOS.Windows; - else if (token.Equals("linux", StringComparison.OrdinalIgnoreCase)) - return TargetOS.Linux; - else if (token.Equals("osx", StringComparison.OrdinalIgnoreCase)) - return TargetOS.OSX; - - throw new CommandLineException($"Target OS '{token}' is not supported"); - } - - public static TargetArchitecture GetTargetArchitecture(string token) - { - if(string.IsNullOrEmpty(token)) - { - return RuntimeInformation.ProcessArchitecture switch - { - Architecture.X86 => TargetArchitecture.X86, - Architecture.X64 => TargetArchitecture.X64, - Architecture.Arm => TargetArchitecture.ARM, - Architecture.Arm64 => TargetArchitecture.ARM64, - Architecture.LoongArch64 => TargetArchitecture.LoongArch64, - _ => throw new NotImplementedException() - }; - } - - if (token.Equals("x86", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.X86; - else if (token.Equals("x64", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.X64; - else if (token.Equals("arm", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.ARM; - else if (token.Equals("arm64", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.ARM64; - else if (token.Equals("loongarch64", StringComparison.OrdinalIgnoreCase)) - return TargetArchitecture.LoongArch64; - - throw new CommandLineException($"Target architecture '{token}' is not supported"); - } - - public static void MakeReproPackage(string makeReproPath, string outputFilePath, string[] args, ParseResult res, IEnumerable inputOptions) - { - Directory.CreateDirectory(makeReproPath); - - List details = new List(); - details.Add("Tool version"); - try - { - details.Add(Environment.GetCommandLineArgs()[0]); - } - catch { } - try - { - details.Add(System.Diagnostics.FileVersionInfo.GetVersionInfo(Environment.GetCommandLineArgs()[0]).ToString()); - } - catch { } - - details.Add("------------------------"); - details.Add("Actual Command Line Args"); - details.Add("------------------------"); - details.AddRange(args); - foreach (string arg in args) - { - if (arg.StartsWith('@')) - { - string rspFileName = arg.Substring(1); - details.Add("------------------------"); - details.Add(rspFileName); - details.Add("------------------------"); - try - { - details.AddRange(File.ReadAllLines(rspFileName)); - } - catch { } - } - } - - HashCode hashCodeOfArgs = default; - foreach (string s in details) - hashCodeOfArgs.Add(s); - - string zipFileName = ((uint)hashCodeOfArgs.ToHashCode()).ToString(); - - if (outputFilePath != null) - zipFileName = zipFileName + "_" + Path.GetFileName(outputFilePath); - - zipFileName = Path.Combine(makeReproPath, Path.ChangeExtension(zipFileName, ".zip")); - - Console.WriteLine($"Creating {zipFileName}"); - using (var archive = ZipFile.Open(zipFileName, ZipArchiveMode.Create)) - { - ZipArchiveEntry commandEntry = archive.CreateEntry("command.txt"); - using (StreamWriter writer = new StreamWriter(commandEntry.Open())) - { - foreach (string s in details) - writer.WriteLine(s); - } - - HashSet inputOptionNames = new HashSet(inputOptions); - Dictionary inputToReproPackageFileName = new(); - - List rspFile = new List(); - foreach (var option in res.CommandResult.Command.Options) - { - if (!res.HasOption(option) || option.Name == "make-repro-path") - { - continue; - } - - IValueDescriptor descriptor = option; - object val = res.CommandResult.GetValueForOption(option); - if (val is not null && !(descriptor.HasDefaultValue && descriptor.GetDefaultValue().Equals(val))) - { - if (val is IEnumerable values) - { - if (inputOptionNames.Contains(option.Name)) - { - Dictionary dictionary = new(); - foreach (string optInList in values) - { - Helpers.AppendExpandedPaths(dictionary, optInList, false); - } - foreach (string inputFile in dictionary.Values) - { - rspFile.Add($"--{option.Name}:{ConvertFromInputPathToReproPackagePath(inputFile)}"); - } - } - else - { - foreach (string optInList in values) - { - rspFile.Add($"--{option.Name}:{optInList}"); - } - } - } - else - { - rspFile.Add($"--{option.Name}:{val}"); - } - } - } - - foreach (var argument in res.CommandResult.Command.Arguments) - { - object val = res.CommandResult.GetValueForArgument(argument); - if (val is IEnumerable values) - { - foreach (string optInList in values) - { - rspFile.Add($"{ConvertFromInputPathToReproPackagePath((string)optInList)}"); - } - } - else - { - rspFile.Add($"{ConvertFromInputPathToReproPackagePath((string)val)}"); - } - } - - ZipArchiveEntry rspEntry = archive.CreateEntry("repro.rsp"); - using (StreamWriter writer = new StreamWriter(rspEntry.Open())) - { - foreach (string s in rspFile) - writer.WriteLine(s); - } - - string ConvertFromInputPathToReproPackagePath(string inputPath) - { - if (inputToReproPackageFileName.TryGetValue(inputPath, out string reproPackagePath)) - { - return reproPackagePath; - } - - try - { - string inputFileDir = inputToReproPackageFileName.Count.ToString(); - reproPackagePath = Path.Combine(inputFileDir, Path.GetFileName(inputPath)); - archive.CreateEntryFromFile(inputPath, reproPackagePath); - inputToReproPackageFileName.Add(inputPath, reproPackagePath); - - return reproPackagePath; - } - catch - { - return inputPath; - } - } - } - } - - // Helper to create a collection of paths unique in their simple names. - private static void AppendExpandedPaths(Dictionary dictionary, string pattern, bool strict) - { - bool empty = true; - string directoryName = Path.GetDirectoryName(pattern); - string searchPattern = Path.GetFileName(pattern); - - if (directoryName == "") - directoryName = "."; - - if (Directory.Exists(directoryName)) - { - foreach (string fileName in Directory.EnumerateFiles(directoryName, searchPattern)) - { - string fullFileName = Path.GetFullPath(fileName); - - string simpleName = Path.GetFileNameWithoutExtension(fileName); - - if (dictionary.ContainsKey(simpleName)) - { - if (strict) - { - throw new CommandLineException("Multiple input files matching same simple name " + - fullFileName + " " + dictionary[simpleName]); - } - } - else - { - dictionary.Add(simpleName, fullFileName); - } - - empty = false; - } - } - - if (empty) - { - if (strict) - { - throw new CommandLineException("No files matching " + pattern); - } - else - { - Console.WriteLine("Warning: No files matching " + pattern); - } - } - } - } -} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs index b382e3babb8dac..5d7a4ab2eac3ce 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DependencyGraphTests.cs @@ -73,7 +73,7 @@ public void TestDependencyGraphInvariants(EcmaMethod method) new FullyBlockedMetadataBlockingPolicy(), new FullyBlockedManifestResourceBlockingPolicy(), null, new NoStackTraceEmissionPolicy(), new NoDynamicInvokeThunkGenerationPolicy(), new ILLink.Shared.TrimAnalysis.FlowAnnotations(Logger.Null, ilProvider, compilerGeneratedState), UsageBasedMetadataGenerationOptions.None, - Logger.Null, Array.Empty>(), Array.Empty(), Array.Empty(), Array.Empty()); + Logger.Null, Array.Empty>(), Array.Empty(), Array.Empty()); CompilationBuilder builder = new RyuJitCompilationBuilder(context, compilationGroup) .UseILProvider(ilProvider); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index f5e9126850d8f6..b194f265944859 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -79,7 +79,6 @@ public UsageBasedMetadataManager( Logger logger, IEnumerable> featureSwitchValues, IEnumerable rootEntireAssembliesModules, - IEnumerable additionalRootedAssemblies, IEnumerable trimmedAssemblies) : base(typeSystemContext, blockingPolicy, resourceBlockingPolicy, logFile, stackTracePolicy, invokeThunkGenerationPolicy) { @@ -93,7 +92,6 @@ public UsageBasedMetadataManager( FeatureSwitches = new Dictionary(featureSwitchValues); _rootEntireAssembliesModules = new HashSet(rootEntireAssembliesModules); - _rootEntireAssembliesModules.UnionWith(additionalRootedAssemblies); _trimmedAssemblies = new HashSet(trimmedAssemblies); } diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompiler.props b/src/coreclr/tools/aot/ILCompiler/ILCompiler.props index 51569f7d1b4454..8c4fce523906e7 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompiler.props +++ b/src/coreclr/tools/aot/ILCompiler/ILCompiler.props @@ -82,11 +82,28 @@ - + + + + + + + + + + + + + + + - + + true + Internal.CommandLine.Strings + diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs deleted file mode 100644 index 9f9346089b2ff3..00000000000000 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ /dev/null @@ -1,390 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.CommandLine; -using System.CommandLine.Help; -using System.CommandLine.Parsing; -using System.IO; -using System.Runtime.InteropServices; - -using Internal.TypeSystem; - -namespace ILCompiler -{ - internal sealed class ILCompilerRootCommand : RootCommand - { - public Argument> InputFilePaths { get; } = - new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; - public Option> ReferenceFiles { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, "Reference file(s) for compilation"); - public Option OutputFilePath { get; } = - new(new[] { "--out", "-o" }, "Output file path"); - public Option Optimize { get; } = - new(new[] { "--optimize", "-O" }, "Enable optimizations"); - public Option OptimizeSpace { get; } = - new(new[] { "--optimize-space", "-Os" }, "Enable optimizations, favor code space"); - public Option OptimizeTime { get; } = - new(new[] { "--optimize-time", "-Ot" }, "Enable optimizations, favor code speed"); - public Option MibcFilePaths { get; } = - new(new[] { "--mibc", "-m" }, Array.Empty, "Mibc file(s) for profile guided optimization"); - public Option EnableDebugInfo { get; } = - new(new[] { "--debug", "-g" }, "Emit debugging information"); - public Option UseDwarf5 { get; } = - new(new[] { "--gdwarf-5" }, "Generate source-level debug information with dwarf version 5"); - public Option NativeLib { get; } = - new(new[] { "--nativelib" }, "Compile as static or shared library"); - public Option ExportsFile { get; } = - new(new[] { "--exportsfile" }, "File to write exported method definitions"); - public Option DgmlLogFileName { get; } = - new(new[] { "--dgmllog" }, "Save result of dependency analysis as DGML"); - public Option GenerateFullDgmlLog { get; } = - new(new[] { "--fulllog" }, "Save detailed log of dependency analysis"); - public Option ScanDgmlLogFileName { get; } = - new(new[] { "--scandgmllog" }, "Save result of scanner dependency analysis as DGML"); - public Option GenerateFullScanDgmlLog { get; } = - new(new[] { "--scanfulllog" }, "Save detailed log of scanner dependency analysis"); - public Option IsVerbose { get; } = - new(new[] { "--verbose" }, "Enable verbose logging"); - public Option SystemModuleName { get; } = - new(new[] { "--systemmodule" }, () => Helpers.DefaultSystemModule, "System module name (default: System.Private.CoreLib)"); - public Option MultiFile { get; } = - new(new[] { "--multifile" }, "Compile only input files (do not compile referenced assemblies)"); - public Option WaitForDebugger { get; } = - new(new[] { "--waitfordebugger" }, "Pause to give opportunity to attach debugger"); - public Option Resilient { get; } = - new(new[] { "--resilient" }, "Ignore unresolved types, methods, and assemblies. Defaults to false"); - public Option CodegenOptions { get; } = - new(new[] { "--codegenopt" }, Array.Empty, "Define a codegen option"); - public Option RdXmlFilePaths { get; } = - new(new[] { "--rdxml" }, Array.Empty, "RD.XML file(s) for compilation"); - public Option LinkTrimFilePaths { get; } = - new(new[] { "--descriptor" }, Array.Empty, "ILLinkTrim.Descriptor file(s) for compilation"); - public Option MapFileName { get; } = - new(new[] { "--map" }, "Generate a map file"); - public Option MstatFileName { get; } = - new(new[] { "--mstat" }, "Generate an mstat file"); - public Option MetadataLogFileName { get; } = - new(new[] { "--metadatalog" }, "Generate a metadata log file"); - public Option NoMetadataBlocking { get; } = - new(new[] { "--nometadatablocking" }, "Ignore metadata blocking for internal implementation details"); - public Option CompleteTypesMetadata { get; } = - new(new[] { "--completetypemetadata" }, "Generate complete metadata for types"); - public Option ReflectionData { get; } = - new(new[] { "--reflectiondata" }, "Reflection data to generate (one of: all, none)"); - public Option ScanReflection { get; } = - new(new[] { "--scanreflection" }, "Scan IL for reflection patterns"); - public Option UseScanner { get; } = - new(new[] { "--scan" }, "Use IL scanner to generate optimized code (implied by -O)"); - public Option NoScanner { get; } = - new(new[] { "--noscan" }, "Do not use IL scanner to generate optimized code"); - public Option IlDump { get; } = - new(new[] { "--ildump" }, "Dump IL assembly listing for compiler-generated IL"); - public Option EmitStackTraceData { get; } = - new(new[] { "--stacktracedata" }, "Emit data to support generating stack trace strings at runtime"); - public Option MethodBodyFolding { get; } = - new(new[] { "--methodbodyfolding" }, "Fold identical method bodies"); - public Option InitAssemblies { get; } = - new(new[] { "--initassembly" }, Array.Empty, "Assembly(ies) with a library initializer"); - public Option AppContextSwitches { get; } = - new(new[] { "--appcontextswitch" }, Array.Empty, "System.AppContext switches to set (format: 'Key=Value')"); - public Option FeatureSwitches { get; } = - new(new[] { "--feature" }, Array.Empty, "Feature switches to apply (format: 'Namespace.Name=[true|false]'"); - public Option RuntimeOptions { get; } = - new(new[] { "--runtimeopt" }, Array.Empty, "Runtime options to set"); - public Option Parallelism { get; } = - new(new[] { "--parallelism" }, result => - { - if (result.Tokens.Count > 0) - return int.Parse(result.Tokens[0].Value); - - // Limit parallelism to 24 wide at most by default, more parallelism is unlikely to improve compilation speed - // as many portions of the process are single threaded, and is known to use excessive memory. - var parallelism = Math.Min(24, Environment.ProcessorCount); - - // On 32bit platforms restrict it more, as virtual address space is quite limited - if (!Environment.Is64BitProcess) - parallelism = Math.Min(4, parallelism); - - return parallelism; - }, true, "Maximum number of threads to use during compilation"); - public Option InstructionSet { get; } = - new(new[] { "--instruction-set" }, "Instruction set to allow or disallow"); - public Option Guard { get; } = - new(new[] { "--guard" }, "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)"); - public Option PreinitStatics { get; } = - new(new[] { "--preinitstatics" }, "Interpret static constructors at compile time if possible (implied by -O)"); - public Option NoPreinitStatics { get; } = - new(new[] { "--nopreinitstatics" }, "Do not interpret static constructors at compile time"); - public Option SuppressedWarnings { get; } = - new(new[] { "--nowarn" }, Array.Empty, "Disable specific warning messages"); - public Option SingleWarn { get; } = - new(new[] { "--singlewarn" }, "Generate single AOT/trimming warning per assembly"); - public Option NoTrimWarn { get; } = - new(new[] { "--notrimwarn" }, "Disable warnings related to trimming"); - public Option NoAotWarn { get; } = - new(new[] { "--noaotwarn" }, "Disable warnings related to AOT"); - public Option SingleWarnEnabledAssemblies { get; } = - new(new[] { "--singlewarnassembly" }, Array.Empty, "Generate single AOT/trimming warning for given assembly"); - public Option SingleWarnDisabledAssemblies { get; } = - new(new[] { "--nosinglewarnassembly" }, Array.Empty, "Expand AOT/trimming warnings for given assembly"); - public Option DirectPInvokes { get; } = - new(new[] { "--directpinvoke" }, Array.Empty, "PInvoke to call directly"); - public Option DirectPInvokeLists { get; } = - new(new[] { "--directpinvokelist" }, Array.Empty, "File with list of PInvokes to call directly"); - public Option MaxGenericCycle { get; } = - new(new[] { "--maxgenericcycle" }, () => CompilerTypeSystemContext.DefaultGenericCycleCutoffPoint, "Max depth of generic cycle"); - public Option RootedAssemblies { get; } = - new(new[] { "--root" }, Array.Empty, "Fully generate given assembly"); - public Option> ConditionallyRootedAssemblies { get; } = - new(new[] { "--conditionalroot" }, result => ILLinkify(result.Tokens, true), true, "Fully generate given assembly if it's used"); - public Option> TrimmedAssemblies { get; } = - new(new[] { "--trim" }, result => ILLinkify(result.Tokens, true), true, "Trim the specified assembly"); - public Option RootDefaultAssemblies { get; } = - new(new[] { "--defaultrooting" }, "Root assemblies that are not marked [IsTrimmable]"); - public Option TargetArchitecture { get; } = - new(new[] { "--targetarch" }, result => Helpers.GetTargetArchitecture(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, "Target architecture for cross compilation"); - public Option TargetOS { get; } = - new(new[] { "--targetos" }, result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, "Target OS for cross compilation"); - public Option JitPath { get; } = - new(new[] { "--jitpath" }, "Path to JIT compiler library"); - public Option SingleMethodTypeName { get; } = - new(new[] { "--singlemethodtypename" }, "Single method compilation: assembly-qualified name of the owning type"); - public Option SingleMethodName { get; } = - new(new[] { "--singlemethodname" }, "Single method compilation: name of the method"); - public Option SingleMethodGenericArgs { get; } = - new(new[] { "--singlemethodgenericarg" }, "Single method compilation: generic arguments to the method"); - public Option MakeReproPath { get; } = - new(new[] { "--make-repro-path" }, "Path where to place a repro package"); - - public OptimizationMode OptimizationMode { get; private set; } - public ParseResult Result; - - public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler") - { - AddArgument(InputFilePaths); - AddOption(ReferenceFiles); - AddOption(OutputFilePath); - AddOption(Optimize); - AddOption(OptimizeSpace); - AddOption(OptimizeTime); - AddOption(MibcFilePaths); - AddOption(EnableDebugInfo); - AddOption(UseDwarf5); - AddOption(NativeLib); - AddOption(ExportsFile); - AddOption(DgmlLogFileName); - AddOption(GenerateFullDgmlLog); - AddOption(ScanDgmlLogFileName); - AddOption(GenerateFullScanDgmlLog); - AddOption(IsVerbose); - AddOption(SystemModuleName); - AddOption(MultiFile); - AddOption(WaitForDebugger); - AddOption(Resilient); - AddOption(CodegenOptions); - AddOption(RdXmlFilePaths); - AddOption(LinkTrimFilePaths); - AddOption(MapFileName); - AddOption(MstatFileName); - AddOption(MetadataLogFileName); - AddOption(NoMetadataBlocking); - AddOption(CompleteTypesMetadata); - AddOption(ReflectionData); - AddOption(ScanReflection); - AddOption(UseScanner); - AddOption(NoScanner); - AddOption(IlDump); - AddOption(EmitStackTraceData); - AddOption(MethodBodyFolding); - AddOption(InitAssemblies); - AddOption(AppContextSwitches); - AddOption(FeatureSwitches); - AddOption(RuntimeOptions); - AddOption(Parallelism); - AddOption(InstructionSet); - AddOption(Guard); - AddOption(PreinitStatics); - AddOption(NoPreinitStatics); - AddOption(SuppressedWarnings); - AddOption(SingleWarn); - AddOption(NoTrimWarn); - AddOption(NoAotWarn); - AddOption(SingleWarnEnabledAssemblies); - AddOption(SingleWarnDisabledAssemblies); - AddOption(DirectPInvokes); - AddOption(DirectPInvokeLists); - AddOption(MaxGenericCycle); - AddOption(RootedAssemblies); - AddOption(ConditionallyRootedAssemblies); - AddOption(TrimmedAssemblies); - AddOption(RootDefaultAssemblies); - AddOption(TargetArchitecture); - AddOption(TargetOS); - AddOption(JitPath); - AddOption(SingleMethodTypeName); - AddOption(SingleMethodName); - AddOption(SingleMethodGenericArgs); - AddOption(MakeReproPath); - - this.SetHandler(context => - { - Result = context.ParseResult; - - if (context.ParseResult.GetValueForOption(OptimizeSpace)) - { - OptimizationMode = OptimizationMode.PreferSize; - } - else if (context.ParseResult.GetValueForOption(OptimizeTime)) - { - OptimizationMode = OptimizationMode.PreferSpeed; - } - else if (context.ParseResult.GetValueForOption(Optimize)) - { - OptimizationMode = OptimizationMode.Blended; - } - else - { - OptimizationMode = OptimizationMode.None; - } - - try - { - string makeReproPath = context.ParseResult.GetValueForOption(MakeReproPath); - if (makeReproPath != null) - { - // Create a repro package in the specified path - // This package will have the set of input files needed for compilation - // + the original command line arguments - // + a rsp file that should work to directly run out of the zip file - - Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValueForOption(OutputFilePath), args, - context.ParseResult, new[] { "r", "reference", "m", "mibc", "rdxml", "directpinvokelist", "descriptor" }); - } - - context.ExitCode = new Program(this).Run(); - } -#if DEBUG - catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) - { - throw new NotSupportedException(); // Unreachable - } -#else - catch (Exception e) - { - Console.ResetColor(); - Console.ForegroundColor = ConsoleColor.Red; - - Console.Error.WriteLine("Error: " + e.Message); - Console.Error.WriteLine(e.ToString()); - - Console.ResetColor(); - - context.ExitCode = 1; - } -#endif - }); - } - - public static IEnumerable GetExtendedHelp(HelpContext _) - { - foreach (HelpSectionDelegate sectionDelegate in HelpBuilder.Default.GetLayout()) - yield return sectionDelegate; - - yield return _ => - { - Console.WriteLine("Options may be passed on the command line, or via response file. On the command line switch values may be specified by passing " + - "the option followed by a space followed by the value of the option, or by specifying a : between option and switch value. A response file " + - "is specified by passing the @ symbol before the response file name. In a response file all options must be specified on their own lines, and " + - "only the : syntax for switches is supported.\n"); - - Console.WriteLine("Use the '--' option to disambiguate between input files that have begin with -- and options. After a '--' option, all arguments are " + - "considered to be input files. If no input files begin with '--' then this option is not necessary.\n"); - - string[] ValidArchitectures = new string[] { "arm", "arm64", "x86", "x64" }; - string[] ValidOS = new string[] { "windows", "linux", "osx" }; - - Console.WriteLine("Valid switches for {0} are: '{1}'. The default value is '{2}'\n", "--targetos", string.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant()); - - Console.WriteLine(string.Format("Valid switches for {0} are: '{1}'. The default value is '{2}'\n", "--targetarch", string.Join("', '", ValidArchitectures), Helpers.GetTargetArchitecture(null).ToString().ToLowerInvariant())); - - Console.WriteLine("The allowable values for the --instruction-set option are described in the table below. Each architecture has a different set of valid " + - "instruction sets, and multiple instruction sets may be specified by separating the instructions sets by a ','. For example 'avx2,bmi,lzcnt'"); - - foreach (string arch in ValidArchitectures) - { - Console.Write(arch); - Console.Write(": "); - - TargetArchitecture targetArch = Helpers.GetTargetArchitecture(arch); - bool first = true; - foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) - { - // Only instruction sets with are specifiable should be printed to the help text - if (instructionSet.Specifiable) - { - if (first) - { - first = false; - } - else - { - Console.Write(", "); - } - Console.Write(instructionSet.Name); - } - - Console.WriteLine(); - } - } - - Console.WriteLine(); - Console.WriteLine("The following CPU names are predefined groups of instruction sets and can be used in --instruction-set too:"); - Console.WriteLine(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); - }; - } - - private static IEnumerable ILLinkify(IReadOnlyList tokens, bool setDefaultToEmpty) - { - if (tokens.Count == 0) - { - yield return string.Empty; - yield break; - } - - foreach(Token token in tokens) - { - string rootedAssembly = token.Value; - - // For compatibility with IL Linker, the parameter could be a file name or an assembly name. - // This is the logic IL Linker uses to decide how to interpret the string. Really. - string simpleName; - if (File.Exists(rootedAssembly)) - simpleName = Path.GetFileNameWithoutExtension(rootedAssembly); - else - simpleName = rootedAssembly; - yield return simpleName; - } - } - -#if DEBUG - private static bool DumpReproArguments(CodeGenerationFailedException ex) - { - Console.WriteLine("To repro, add following arguments to the command line:"); - - MethodDesc failingMethod = ex.Method; - - var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)failingMethod.Context.SystemModule); - - Console.Write($"--singlemethodtypename \"{formatter.FormatName(failingMethod.OwningType, true)}\""); - Console.Write($" --singlemethodname {failingMethod.Name}"); - - for (int i = 0; i < failingMethod.Instantiation.Length; i++) - Console.Write($" --singlemethodgenericarg \"{formatter.FormatName(failingMethod.Instantiation[i], true)}\""); - - return false; - } -#endif - } -} diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index 6d73b489c0b518..7adb06231ed9a1 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -3,9 +3,6 @@ using System; using System.Collections.Generic; -using System.CommandLine; -using System.CommandLine.Help; -using System.CommandLine.Parsing; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; @@ -16,6 +13,8 @@ using Internal.TypeSystem; using Internal.TypeSystem.Ecma; +using Internal.CommandLine; + using ILCompiler.Dataflow; using ILLink.Shared; @@ -26,17 +25,346 @@ namespace ILCompiler { internal sealed class Program { - private readonly ILCompilerRootCommand _command; + private const string DefaultSystemModule = "System.Private.CoreLib"; + + private Dictionary _inputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + private Dictionary _referenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + + private string _outputFilePath; + private bool _isVerbose; + + private string _dgmlLogFileName; + private bool _generateFullDgmlLog; + private string _scanDgmlLogFileName; + private bool _generateFullScanDgmlLog; + + private TargetArchitecture _targetArchitecture; + private string _targetArchitectureStr; + private TargetOS _targetOS; + private string _targetOSStr; + private OptimizationMode _optimizationMode; + private bool _enableDebugInfo; + private string _ilDump; + private string _systemModuleName = DefaultSystemModule; + private bool _multiFile; + private bool _nativeLib; + private string _exportsFile; + private bool _useScanner; + private bool _noScanner; + private bool _preinitStatics; + private bool _noPreinitStatics; + private bool _emitStackTraceData; + private string _mapFileName; + private string _mstatFileName; + private string _metadataLogFileName; + private bool _noMetadataBlocking; + private string _reflectionData; + private bool _completeTypesMetadata; + private bool _scanReflection; + private bool _methodBodyFolding; + private int _parallelism = Environment.ProcessorCount; + private string _instructionSet; + private string _guard; + private int _maxGenericCycle = CompilerTypeSystemContext.DefaultGenericCycleCutoffPoint; + private bool _useDwarf5; + private string _jitPath; + + private string _singleMethodTypeName; + private string _singleMethodName; + private IReadOnlyList _singleMethodGenericArgs; + + private IReadOnlyList _codegenOptions = Array.Empty(); + + private IReadOnlyList _rdXmlFilePaths = Array.Empty(); + + private IReadOnlyList _linkTrimFilePaths = Array.Empty(); + + private IReadOnlyList _initAssemblies = Array.Empty(); + + private IReadOnlyList _appContextSwitches = Array.Empty(); + + private IReadOnlyList _runtimeOptions = Array.Empty(); + + private IReadOnlyList _featureSwitches = Array.Empty(); + + private IReadOnlyList _suppressedWarnings = Array.Empty(); + + private IReadOnlyList _directPInvokes = Array.Empty(); + + private IReadOnlyList _directPInvokeLists = Array.Empty(); + + private bool _resilient; - public Program(ILCompilerRootCommand command) + private IReadOnlyList _rootedAssemblies = Array.Empty(); + private IReadOnlyList _conditionallyRootedAssemblies = Array.Empty(); + private IReadOnlyList _trimmedAssemblies = Array.Empty(); + private bool _rootDefaultAssemblies; + + public IReadOnlyList _mibcFilePaths = Array.Empty(); + + private IReadOnlyList _singleWarnEnabledAssemblies = Array.Empty(); + private IReadOnlyList _singleWarnDisabledAssemblies = Array.Empty(); + private bool _singleWarn; + private bool _noTrimWarn; + private bool _noAotWarn; + + private string _makeReproPath; + + private bool _help; + + private Program() { - _command = command; + } + + private static void Help(string helpText) + { + Console.WriteLine(); + Console.Write(".NET Native IL Compiler"); + Console.Write(" "); + Console.Write(typeof(Program).GetTypeInfo().Assembly.GetName().Version); + Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine(helpText); + } - if (command.Result.GetValueForOption(command.WaitForDebugger)) + public static void ComputeDefaultOptions(out TargetOS os, out TargetArchitecture arch) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + os = TargetOS.Windows; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + os = TargetOS.Linux; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + os = TargetOS.OSX; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + os = TargetOS.FreeBSD; + else + throw new NotImplementedException(); + + switch (RuntimeInformation.ProcessArchitecture) + { + case Architecture.X86: + arch = TargetArchitecture.X86; + break; + case Architecture.X64: + arch = TargetArchitecture.X64; + break; + case Architecture.Arm: + arch = TargetArchitecture.ARM; + break; + case Architecture.Arm64: + arch = TargetArchitecture.ARM64; + break; + default: + throw new NotImplementedException(); + } + + } + + private void InitializeDefaultOptions() + { + ComputeDefaultOptions(out _targetOS, out _targetArchitecture); + } + + private ArgumentSyntax ParseCommandLine(string[] args) + { + var validReflectionDataOptions = new string[] { "all", "none" }; + + IReadOnlyList inputFiles = Array.Empty(); + IReadOnlyList referenceFiles = Array.Empty(); + + bool optimize = false; + bool optimizeSpace = false; + bool optimizeTime = false; + + bool waitForDebugger = false; + AssemblyName name = typeof(Program).GetTypeInfo().Assembly.GetName(); + ArgumentSyntax argSyntax = ArgumentSyntax.Parse(args, syntax => + { + syntax.ApplicationName = name.Name.ToString(); + + // HandleHelp writes to error, fails fast with crash dialog and lacks custom formatting. + syntax.HandleHelp = false; + syntax.HandleErrors = true; + + syntax.DefineOption("h|help", ref _help, "Help message for ILC"); + syntax.DefineOptionList("r|reference", ref referenceFiles, "Reference file(s) for compilation"); + syntax.DefineOption("o|out", ref _outputFilePath, "Output file path"); + syntax.DefineOption("O", ref optimize, "Enable optimizations"); + syntax.DefineOption("Os", ref optimizeSpace, "Enable optimizations, favor code space"); + syntax.DefineOption("Ot", ref optimizeTime, "Enable optimizations, favor code speed"); + syntax.DefineOptionList("m|mibc", ref _mibcFilePaths, "Mibc file(s) for profile guided optimization"); ; + syntax.DefineOption("g", ref _enableDebugInfo, "Emit debugging information"); + syntax.DefineOption("gdwarf-5", ref _useDwarf5, "Generate source-level debug information with dwarf version 5"); + syntax.DefineOption("nativelib", ref _nativeLib, "Compile as static or shared library"); + syntax.DefineOption("exportsfile", ref _exportsFile, "File to write exported method definitions"); + syntax.DefineOption("dgmllog", ref _dgmlLogFileName, "Save result of dependency analysis as DGML"); + syntax.DefineOption("fulllog", ref _generateFullDgmlLog, "Save detailed log of dependency analysis"); + syntax.DefineOption("scandgmllog", ref _scanDgmlLogFileName, "Save result of scanner dependency analysis as DGML"); + syntax.DefineOption("scanfulllog", ref _generateFullScanDgmlLog, "Save detailed log of scanner dependency analysis"); + syntax.DefineOption("verbose", ref _isVerbose, "Enable verbose logging"); + syntax.DefineOption("systemmodule", ref _systemModuleName, "System module name (default: System.Private.CoreLib)"); + syntax.DefineOption("multifile", ref _multiFile, "Compile only input files (do not compile referenced assemblies)"); + syntax.DefineOption("waitfordebugger", ref waitForDebugger, "Pause to give opportunity to attach debugger"); + syntax.DefineOption("resilient", ref _resilient, "Ignore unresolved types, methods, and assemblies. Defaults to false"); + syntax.DefineOptionList("codegenopt", ref _codegenOptions, "Define a codegen option"); + syntax.DefineOptionList("rdxml", ref _rdXmlFilePaths, "RD.XML file(s) for compilation"); + syntax.DefineOptionList("descriptor", ref _linkTrimFilePaths, "ILLinkTrim.Descriptor file(s) for compilation"); + syntax.DefineOption("map", ref _mapFileName, "Generate a map file"); + syntax.DefineOption("mstat", ref _mstatFileName, "Generate an mstat file"); + syntax.DefineOption("metadatalog", ref _metadataLogFileName, "Generate a metadata log file"); + syntax.DefineOption("nometadatablocking", ref _noMetadataBlocking, "Ignore metadata blocking for internal implementation details"); + syntax.DefineOption("completetypemetadata", ref _completeTypesMetadata, "Generate complete metadata for types"); + syntax.DefineOption("reflectiondata", ref _reflectionData, $"Reflection data to generate (one of: {string.Join(", ", validReflectionDataOptions)})"); + syntax.DefineOption("scanreflection", ref _scanReflection, "Scan IL for reflection patterns"); + syntax.DefineOption("scan", ref _useScanner, "Use IL scanner to generate optimized code (implied by -O)"); + syntax.DefineOption("noscan", ref _noScanner, "Do not use IL scanner to generate optimized code"); + syntax.DefineOption("ildump", ref _ilDump, "Dump IL assembly listing for compiler-generated IL"); + syntax.DefineOption("stacktracedata", ref _emitStackTraceData, "Emit data to support generating stack trace strings at runtime"); + syntax.DefineOption("methodbodyfolding", ref _methodBodyFolding, "Fold identical method bodies"); + syntax.DefineOptionList("initassembly", ref _initAssemblies, "Assembly(ies) with a library initializer"); + syntax.DefineOptionList("appcontextswitch", ref _appContextSwitches, "System.AppContext switches to set (format: 'Key=Value')"); + syntax.DefineOptionList("feature", ref _featureSwitches, "Feature switches to apply (format: 'Namespace.Name=[true|false]'"); + syntax.DefineOptionList("runtimeopt", ref _runtimeOptions, "Runtime options to set"); + syntax.DefineOption("parallelism", ref _parallelism, "Maximum number of threads to use during compilation"); + syntax.DefineOption("instruction-set", ref _instructionSet, "Instruction set to allow or disallow"); + syntax.DefineOption("guard", ref _guard, "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)"); + syntax.DefineOption("preinitstatics", ref _preinitStatics, "Interpret static constructors at compile time if possible (implied by -O)"); + syntax.DefineOption("nopreinitstatics", ref _noPreinitStatics, "Do not interpret static constructors at compile time"); + syntax.DefineOptionList("nowarn", ref _suppressedWarnings, "Disable specific warning messages"); + syntax.DefineOption("singlewarn", ref _singleWarn, "Generate single AOT/trimming warning per assembly"); + syntax.DefineOption("notrimwarn", ref _noTrimWarn, "Disable warnings related to trimming"); + syntax.DefineOption("noaotwarn", ref _noAotWarn, "Disable warnings related to AOT"); + syntax.DefineOptionList("singlewarnassembly", ref _singleWarnEnabledAssemblies, "Generate single AOT/trimming warning for given assembly"); + syntax.DefineOptionList("nosinglewarnassembly", ref _singleWarnDisabledAssemblies, "Expand AOT/trimming warnings for given assembly"); + syntax.DefineOptionList("directpinvoke", ref _directPInvokes, "PInvoke to call directly"); + syntax.DefineOptionList("directpinvokelist", ref _directPInvokeLists, "File with list of PInvokes to call directly"); + syntax.DefineOption("maxgenericcycle", ref _maxGenericCycle, "Max depth of generic cycle"); + syntax.DefineOptionList("root", ref _rootedAssemblies, "Fully generate given assembly"); + syntax.DefineOptionList("conditionalroot", ref _conditionallyRootedAssemblies, "Fully generate given assembly if it's used"); + syntax.DefineOptionList("trim", ref _trimmedAssemblies, "Trim the specified assembly"); + syntax.DefineOption("defaultrooting", ref _rootDefaultAssemblies, "Root assemblies that are not marked [IsTrimmable]"); + + syntax.DefineOption("targetarch", ref _targetArchitectureStr, "Target architecture for cross compilation"); + syntax.DefineOption("targetos", ref _targetOSStr, "Target OS for cross compilation"); + syntax.DefineOption("jitpath", ref _jitPath, "Path to JIT compiler library"); + + syntax.DefineOption("singlemethodtypename", ref _singleMethodTypeName, "Single method compilation: assembly-qualified name of the owning type"); + syntax.DefineOption("singlemethodname", ref _singleMethodName, "Single method compilation: name of the method"); + syntax.DefineOptionList("singlemethodgenericarg", ref _singleMethodGenericArgs, "Single method compilation: generic arguments to the method"); + + syntax.DefineOption("make-repro-path", ref _makeReproPath, "Path where to place a repro package"); + + syntax.DefineParameterList("in", ref inputFiles, "Input file(s) to compile"); + }); + + if (_help) + { + List extraHelp = new List(); + + extraHelp.Add("Options may be passed on the command line, or via response file. On the command line switch values may be specified by passing " + + "the option followed by a space followed by the value of the option, or by specifying a : between option and switch value. A response file " + + "is specified by passing the @ symbol before the response file name. In a response file all options must be specified on their own lines, and " + + "only the : syntax for switches is supported."); + + extraHelp.Add(""); + + extraHelp.Add("Use the '--' option to disambiguate between input files that have begin with -- and options. After a '--' option, all arguments are " + + "considered to be input files. If no input files begin with '--' then this option is not necessary."); + + extraHelp.Add(""); + + string[] ValidArchitectures = new string[] { "arm", "arm64", "x86", "x64" }; + string[] ValidOS = new string[] { "windows", "linux", "osx" }; + + ComputeDefaultOptions(out TargetOS defaultOs, out TargetArchitecture defaultArch); + + extraHelp.Add(string.Format("Valid switches for {0} are: '{1}'. The default value is '{2}'", "--targetos", string.Join("', '", ValidOS), defaultOs.ToString().ToLowerInvariant())); + + extraHelp.Add(""); + + extraHelp.Add(string.Format("Valid switches for {0} are: '{1}'. The default value is '{2}'", "--targetarch", string.Join("', '", ValidArchitectures), defaultArch.ToString().ToLowerInvariant())); + + extraHelp.Add(""); + + extraHelp.Add("The allowable values for the --instruction-set option are described in the table below. Each architecture has a different set of valid " + + "instruction sets, and multiple instruction sets may be specified by separating the instructions sets by a ','. For example 'avx2,bmi,lzcnt'"); + + foreach (string arch in ValidArchitectures) + { + StringBuilder archString = new StringBuilder(); + + archString.Append(arch); + archString.Append(": "); + + TargetArchitecture targetArch = GetTargetArchitectureFromArg(arch); + bool first = true; + foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) + { + // Only instruction sets with are specifiable should be printed to the help text + if (instructionSet.Specifiable) + { + if (first) + { + first = false; + } + else + { + archString.Append(", "); + } + archString.Append(instructionSet.Name); + } + } + + extraHelp.Add(archString.ToString()); + } + + extraHelp.Add(""); + extraHelp.Add("The following CPU names are predefined groups of instruction sets and can be used in --instruction-set too:"); + extraHelp.Add(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); + + argSyntax.ExtraHelpParagraphs = extraHelp; + } + + if (waitForDebugger) { Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); Console.ReadLine(); } + + _optimizationMode = OptimizationMode.None; + if (optimizeSpace) + { + if (optimizeTime) + Console.WriteLine("Warning: overriding -Ot with -Os"); + _optimizationMode = OptimizationMode.PreferSize; + } + else if (optimizeTime) + _optimizationMode = OptimizationMode.PreferSpeed; + else if (optimize) + _optimizationMode = OptimizationMode.Blended; + + foreach (var input in inputFiles) + Helpers.AppendExpandedPaths(_inputFilePaths, input, true); + + foreach (var reference in referenceFiles) + Helpers.AppendExpandedPaths(_referenceFilePaths, reference, false); + + if (_makeReproPath != null) + { + // Create a repro package in the specified path + // This package will have the set of input files needed for compilation + // + the original command line arguments + // + a rsp file that should work to directly run out of the zip file + + Helpers.MakeReproPackage(_makeReproPath, _outputFilePath, args, argSyntax, new[] { "-r", "-m", "--rdxml", "--directpinvokelist", "--descriptor" }); + } + + if (_reflectionData != null && Array.IndexOf(validReflectionDataOptions, _reflectionData) < 0) + { + Console.WriteLine($"Warning: option '{_reflectionData}' not recognized"); + } + + return argSyntax; } private IReadOnlyCollection CreateInitializerList(CompilerTypeSystemContext context) @@ -45,7 +373,7 @@ private IReadOnlyCollection CreateInitializerList(CompilerTypeSystem // Build a list of assemblies that have an initializer that needs to run before // any user code runs. - foreach (string initAssemblyName in Get(_command.InitAssemblies)) + foreach (string initAssemblyName in _initAssemblies) { ModuleDesc assembly = context.ResolveAssembly(new AssemblyName(initAssemblyName), throwIfNotFound: true); assembliesWithInitializers.Add(assembly); @@ -56,35 +384,78 @@ private IReadOnlyCollection CreateInitializerList(CompilerTypeSystem List initializerList = new List(libraryInitializers.LibraryInitializerMethods); // If there are any AppContext switches the user wishes to enable, generate code that sets them. - string[] appContextSwitches = Get(_command.AppContextSwitches); - if (appContextSwitches.Length > 0) + if (_appContextSwitches.Count > 0) { MethodDesc appContextInitMethod = new Internal.IL.Stubs.StartupCode.AppContextInitializerMethod( - context.GeneratedAssembly.GetGlobalModuleType(), appContextSwitches); + context.GeneratedAssembly.GetGlobalModuleType(), _appContextSwitches); initializerList.Add(appContextInitMethod); } return initializerList; } - public int Run() + private static TargetArchitecture GetTargetArchitectureFromArg(string archArg) { - string outputFilePath = Get(_command.OutputFilePath); - if (outputFilePath == null) + if (archArg.Equals("x86", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.X86; + else if (archArg.Equals("x64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.X64; + else if (archArg.Equals("arm", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.ARM; + else if (archArg.Equals("arm64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.ARM64; + else + throw new CommandLineException("Target architecture is not supported"); + } + + private static TargetOS GetTargetOSFromArg(string osArg) + { + if (osArg.Equals("windows", StringComparison.OrdinalIgnoreCase)) + return TargetOS.Windows; + else if (osArg.Equals("linux", StringComparison.OrdinalIgnoreCase)) + return TargetOS.Linux; + else if (osArg.Equals("osx", StringComparison.OrdinalIgnoreCase)) + return TargetOS.OSX; + else + throw new CommandLineException("Target OS is not supported"); + } + + private int Run(string[] args) + { + InitializeDefaultOptions(); + + ArgumentSyntax syntax = ParseCommandLine(args); + if (_help) + { + Help(syntax.GetHelpText()); + return 1; + } + + if (_outputFilePath == null) throw new CommandLineException("Output filename must be specified (/out )"); - TargetArchitecture targetArchitecture = Get(_command.TargetArchitecture); - InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(targetArchitecture); - TargetOS targetOS = Get(_command.TargetOS); + // + // Set target Architecture and OS + // + if (_targetArchitectureStr != null) + { + _targetArchitecture = GetTargetArchitectureFromArg(_targetArchitectureStr); + } + if (_targetOSStr != null) + { + _targetOS = GetTargetOSFromArg(_targetOSStr); + } + + InstructionSetSupportBuilder instructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); // The runtime expects certain baselines that the codegen can assume as well. - if ((targetArchitecture == TargetArchitecture.X86) || (targetArchitecture == TargetArchitecture.X64)) + if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) { instructionSetSupportBuilder.AddSupportedInstructionSet("sse2"); // Lower baselines included by implication } - else if (targetArchitecture == TargetArchitecture.ARM64) + else if (_targetArchitecture == TargetArchitecture.ARM64) { - if (targetOS == TargetOS.OSX) + if (_targetOS == TargetOS.OSX) { // For osx-arm64 we know that apple-m1 is a baseline instructionSetSupportBuilder.AddSupportedInstructionSet("apple-m1"); @@ -95,13 +466,12 @@ public int Run() } } - string instructionSetArg = Get(_command.InstructionSet); - if (instructionSetArg != null) + if (_instructionSet != null) { - var instructionSetParams = new List(); + List instructionSetParams = new List(); // Normalize instruction set format to include implied +. - string[] instructionSetParamsInput = instructionSetArg.Split(','); + string[] instructionSetParamsInput = _instructionSet.Split(','); for (int i = 0; i < instructionSetParamsInput.Length; i++) { string instructionSet = instructionSetParamsInput[i]; @@ -140,10 +510,10 @@ public int Run() (string specifiedInstructionSet, string impliedInstructionSet) => throw new CommandLineException(string.Format("Unsupported combination of instruction sets: {0}/{1}", specifiedInstructionSet, impliedInstructionSet))); - InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(targetArchitecture); + InstructionSetSupportBuilder optimisticInstructionSetSupportBuilder = new InstructionSetSupportBuilder(_targetArchitecture); // Optimistically assume some instruction sets are present. - if (targetArchitecture == TargetArchitecture.X86 || targetArchitecture == TargetArchitecture.X64) + if ((_targetArchitecture == TargetArchitecture.X86) || (_targetArchitecture == TargetArchitecture.X64)) { // We set these hardware features as opportunistically enabled as most of hardware in the wild supports them. // Note that we do not indicate support for AVX, or any other instruction set which uses the VEX encodings as @@ -166,7 +536,7 @@ public int Run() optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("avxvnni"); } } - else if (targetArchitecture == TargetArchitecture.ARM64) + else if (_targetArchitecture == TargetArchitecture.ARM64) { optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("aes"); optimisticInstructionSetSupportBuilder.AddSupportedInstructionSet("crc"); @@ -182,14 +552,12 @@ public int Run() optimisticInstructionSet.Add(supportedInstructionSet); var instructionSetSupport = new InstructionSetSupport(supportedInstructionSet, - unsupportedInstructionSet, - optimisticInstructionSet, - InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(targetArchitecture), - targetArchitecture); + unsupportedInstructionSet, + optimisticInstructionSet, + InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), + _targetArchitecture); - string systemModuleName = Get(_command.SystemModuleName); - string reflectionData = Get(_command.ReflectionData); - bool supportsReflection = reflectionData != "none" && systemModuleName == Helpers.DefaultSystemModule; + bool supportsReflection = _reflectionData != "none" && _systemModuleName == DefaultSystemModule; // // Initialize type system context @@ -199,9 +567,9 @@ public int Run() var simdVectorLength = instructionSetSupport.GetVectorTSimdVector(); var targetAbi = TargetAbi.NativeAot; - var targetDetails = new TargetDetails(targetArchitecture, targetOS, targetAbi, simdVectorLength); + var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, targetAbi, simdVectorLength); CompilerTypeSystemContext typeSystemContext = - new CompilerTypeSystemContext(targetDetails, genericsMode, supportsReflection ? DelegateFeature.All : 0, Get(_command.MaxGenericCycle)); + new CompilerTypeSystemContext(targetDetails, genericsMode, supportsReflection ? DelegateFeature.All : 0, _maxGenericCycle); // // TODO: To support our pre-compiled test tree, allow input files that aren't managed assemblies since @@ -210,10 +578,10 @@ public int Run() // See: https://github.com/dotnet/corert/issues/2785 // // When we undo this hack, replace the foreach with - // typeSystemContext.InputFilePaths = _command.Result.GetValueForArgument(inputFilePaths); + // typeSystemContext.InputFilePaths = _inputFilePaths; // Dictionary inputFilePaths = new Dictionary(); - foreach (var inputFile in _command.Result.GetValueForArgument(_command.InputFilePaths)) + foreach (var inputFile in _inputFilePaths) { try { @@ -227,30 +595,29 @@ public int Run() } typeSystemContext.InputFilePaths = inputFilePaths; - typeSystemContext.ReferenceFilePaths = Get(_command.ReferenceFiles); - if (!typeSystemContext.InputFilePaths.ContainsKey(systemModuleName) - && !typeSystemContext.ReferenceFilePaths.ContainsKey(systemModuleName)) - throw new CommandLineException($"System module {systemModuleName} does not exists. Make sure that you specify --systemmodule"); + typeSystemContext.ReferenceFilePaths = _referenceFilePaths; + if (!typeSystemContext.InputFilePaths.ContainsKey(_systemModuleName) + && !typeSystemContext.ReferenceFilePaths.ContainsKey(_systemModuleName)) + throw new CommandLineException($"System module {_systemModuleName} does not exists. Make sure that you specify --systemmodule"); - typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(systemModuleName)); + typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(_systemModuleName)); if (typeSystemContext.InputFilePaths.Count == 0) throw new CommandLineException("No input files specified"); SecurityMitigationOptions securityMitigationOptions = 0; - string guard = Get(_command.Guard); - if (StringComparer.OrdinalIgnoreCase.Equals(guard, "cf")) + if (StringComparer.OrdinalIgnoreCase.Equals(_guard, "cf")) { - if (targetOS != TargetOS.Windows) + if (_targetOS != TargetOS.Windows) { throw new CommandLineException($"Control flow guard only available on Windows"); } securityMitigationOptions = SecurityMitigationOptions.ControlFlowGuardAnnotations; } - else if (!string.IsNullOrEmpty(guard)) + else if (!string.IsNullOrEmpty(_guard)) { - throw new CommandLineException($"Unrecognized mitigation option '{guard}'"); + throw new CommandLineException($"Unrecognized mitigation option '{_guard}'"); } // @@ -262,7 +629,6 @@ public int Run() CompilationModuleGroup compilationGroup; List compilationRoots = new List(); - bool multiFile = Get(_command.MultiFile); if (singleMethod != null) { // Compiling just a single method @@ -291,16 +657,14 @@ public int Run() compilationRoots.Add(new ExportedMethodsRootProvider(module)); } - string[] runtimeOptions = Get(_command.RuntimeOptions); if (entrypointModule != null) { compilationRoots.Add(new MainMethodRootProvider(entrypointModule, CreateInitializerList(typeSystemContext))); - compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); + compilationRoots.Add(new RuntimeConfigurationRootProvider(_runtimeOptions)); compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); } - bool nativeLib = Get(_command.NativeLib); - if (multiFile) + if (_multiFile) { List inputModules = new List(); @@ -320,7 +684,7 @@ public int Run() } else { - if (entrypointModule == null && !nativeLib) + if (entrypointModule == null && !_nativeLib) throw new Exception("No entrypoint module"); if (!systemModuleIsInputModule) @@ -328,21 +692,21 @@ public int Run() compilationGroup = new SingleFileCompilationModuleGroup(); } - if (nativeLib) + if (_nativeLib) { // Set owning module of generated native library startup method to compiler generated module, // to ensure the startup method is included in the object file during multimodule mode build compilationRoots.Add(new NativeLibraryInitializerRootProvider(typeSystemContext.GeneratedAssembly, CreateInitializerList(typeSystemContext))); - compilationRoots.Add(new RuntimeConfigurationRootProvider(runtimeOptions)); + compilationRoots.Add(new RuntimeConfigurationRootProvider(_runtimeOptions)); compilationRoots.Add(new ExpectedIsaFeaturesRootProvider(instructionSetSupport)); } - foreach (var rdXmlFilePath in Get(_command.RdXmlFilePaths)) + foreach (var rdXmlFilePath in _rdXmlFilePaths) { compilationRoots.Add(new RdXmlRootProvider(typeSystemContext, rdXmlFilePath)); } - foreach (var linkTrimFilePath in Get(_command.LinkTrimFilePaths)) + foreach (var linkTrimFilePath in _linkTrimFilePaths) { if (!File.Exists(linkTrimFilePath)) throw new CommandLineException($"'{linkTrimFilePath}' doesn't exist"); @@ -350,9 +714,23 @@ public int Run() } } + _conditionallyRootedAssemblies = new List(_conditionallyRootedAssemblies.Select(ILLinkify)); + _trimmedAssemblies = new List(_trimmedAssemblies.Select(ILLinkify)); + + static string ILLinkify(string rootedAssembly) + { + // For compatibility with IL Linker, the parameter could be a file name or an assembly name. + // This is the logic IL Linker uses to decide how to interpret the string. Really. + string simpleName; + if (File.Exists(rootedAssembly)) + simpleName = Path.GetFileNameWithoutExtension(rootedAssembly); + else + simpleName = rootedAssembly; + return simpleName; + } + // Root whatever assemblies were specified on the command line - string[] rootedAssemblies = Get(_command.RootedAssemblies); - foreach (var rootedAssembly in rootedAssemblies) + foreach (var rootedAssembly in _rootedAssemblies) { // For compatibility with IL Linker, the parameter could be a file name or an assembly name. // This is the logic IL Linker uses to decide how to interpret the string. Really. @@ -360,7 +738,7 @@ public int Run() ? typeSystemContext.GetModuleFromPath(rootedAssembly) : typeSystemContext.GetModuleForSimpleName(rootedAssembly); - // We only root the module type. The rest will fall out because we treat rootedAssemblies + // We only root the module type. The rest will fall out because we treat _rootedAssemblies // same as conditionally rooted ones and here we're fulfilling the condition ("something is used"). compilationRoots.Add( new GenericRootProvider(module, @@ -373,24 +751,20 @@ public int Run() CompilationBuilder builder = new RyuJitCompilationBuilder(typeSystemContext, compilationGroup); - string compilationUnitPrefix = multiFile ? Path.GetFileNameWithoutExtension(outputFilePath) : ""; + string compilationUnitPrefix = _multiFile ? Path.GetFileNameWithoutExtension(_outputFilePath) : ""; builder.UseCompilationUnitPrefix(compilationUnitPrefix); - string[] mibcFilePaths = Get(_command.MibcFilePaths); - if (mibcFilePaths.Length > 0) - ((RyuJitCompilationBuilder)builder).UseProfileData(mibcFilePaths); + if (_mibcFilePaths.Count > 0) + ((RyuJitCompilationBuilder)builder).UseProfileData(_mibcFilePaths); + if (!string.IsNullOrEmpty(_jitPath)) + ((RyuJitCompilationBuilder)builder).UseJitPath(_jitPath); - string jitPath = Get(_command.JitPath); - if (!string.IsNullOrEmpty(jitPath)) - ((RyuJitCompilationBuilder)builder).UseJitPath(jitPath); - - PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target, - Get(_command.DirectPInvokes), Get(_command.DirectPInvokeLists)); + PInvokeILEmitterConfiguration pinvokePolicy = new ConfigurablePInvokePolicy(typeSystemContext.Target, _directPInvokes, _directPInvokeLists); ILProvider ilProvider = new NativeAotILProvider(); List> featureSwitches = new List>(); - foreach (var switchPair in Get(_command.FeatureSwitches)) + foreach (var switchPair in _featureSwitches) { string[] switchAndValue = switchPair.Split('='); if (switchAndValue.Length != 2 @@ -401,16 +775,15 @@ public int Run() ilProvider = new FeatureSwitchManager(ilProvider, featureSwitches); var suppressedWarningCategories = new List(); - if (Get(_command.NoTrimWarn)) + if (_noTrimWarn) suppressedWarningCategories.Add(MessageSubCategory.TrimAnalysis); - if (Get(_command.NoAotWarn)) + if (_noAotWarn) suppressedWarningCategories.Add(MessageSubCategory.AotAnalysis); - var logger = new Logger(Console.Out, ilProvider, Get(_command.IsVerbose), ProcessWarningCodes(Get(_command.SuppressedWarnings)), - Get(_command.SingleWarn), Get(_command.SingleWarnEnabledAssemblies), Get(_command.SingleWarnDisabledAssemblies), suppressedWarningCategories); + var logger = new Logger(Console.Out, ilProvider, _isVerbose, ProcessWarningCodes(_suppressedWarnings), _singleWarn, _singleWarnEnabledAssemblies, _singleWarnDisabledAssemblies, suppressedWarningCategories); CompilerGeneratedState compilerGeneratedState = new CompilerGeneratedState(ilProvider, logger); - var stackTracePolicy = Get(_command.EmitStackTraceData) ? + var stackTracePolicy = _emitStackTraceData ? (StackTraceEmissionPolicy)new EcmaMethodStackTraceEmissionPolicy() : new NoStackTraceEmissionPolicy(); MetadataBlockingPolicy mdBlockingPolicy; @@ -418,19 +791,20 @@ public int Run() UsageBasedMetadataGenerationOptions metadataGenerationOptions = default; if (supportsReflection) { - mdBlockingPolicy = Get(_command.NoMetadataBlocking) ? - new NoMetadataBlockingPolicy() : new BlockedInternalsBlockingPolicy(typeSystemContext); + mdBlockingPolicy = _noMetadataBlocking + ? (MetadataBlockingPolicy)new NoMetadataBlockingPolicy() + : new BlockedInternalsBlockingPolicy(typeSystemContext); resBlockingPolicy = new ManifestResourceBlockingPolicy(featureSwitches); metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.AnonymousTypeHeuristic; - if (Get(_command.CompleteTypesMetadata)) + if (_completeTypesMetadata) metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CompleteTypesOnly; - if (Get(_command.ScanReflection)) + if (_scanReflection) metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.ReflectionILScanning; - if (reflectionData == "all") + if (_reflectionData == "all") metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts; - if (Get(_command.RootDefaultAssemblies)) + if (_rootDefaultAssemblies) metadataGenerationOptions |= UsageBasedMetadataGenerationOptions.RootDefaultAssemblies; } else @@ -448,16 +822,15 @@ public int Run() typeSystemContext, mdBlockingPolicy, resBlockingPolicy, - Get(_command.MetadataLogFileName), + _metadataLogFileName, stackTracePolicy, invokeThunkGenerationPolicy, flowAnnotations, metadataGenerationOptions, logger, featureSwitches, - Get(_command.ConditionallyRootedAssemblies), - rootedAssemblies, - Get(_command.TrimmedAssemblies)); + _conditionallyRootedAssemblies.Concat(_rootedAssemblies), + _trimmedAssemblies); InteropStateManager interopStateManager = new InteropStateManager(typeSystemContext.GeneratedAssembly); InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy, logger); @@ -466,15 +839,15 @@ public int Run() // We also don't do this for multifile because scanner doesn't simulate inlining (this would be // fixable by using a CompilationGroup for the scanner that has a bigger worldview, but // let's cross that bridge when we get there). - bool useScanner = Get(_command.UseScanner) || - (_command.OptimizationMode != OptimizationMode.None && !multiFile); + bool useScanner = _useScanner || + (_optimizationMode != OptimizationMode.None && !_multiFile); - useScanner &= !Get(_command.NoScanner); + useScanner &= !_noScanner; // Enable static data preinitialization in optimized builds. - bool preinitStatics = Get(_command.PreinitStatics) || - (_command.OptimizationMode != OptimizationMode.None && !multiFile); - preinitStatics &= !Get(_command.NoPreinitStatics); + bool preinitStatics = _preinitStatics || + (_optimizationMode != OptimizationMode.None && !_multiFile); + preinitStatics &= !_noPreinitStatics; var preinitManager = new PreinitializationManager(typeSystemContext, compilationGroup, ilProvider, preinitStatics); builder @@ -486,7 +859,6 @@ public int Run() List scannerCompiledMethods = null; #endif - int parallelism = Get(_command.Parallelism); if (useScanner) { // Run the scanner in a separate stack frame so that there's no dangling references to @@ -500,14 +872,12 @@ void RunScanner() ILScannerBuilder scannerBuilder = builder.GetILScannerBuilder() .UseCompilationRoots(compilationRoots) .UseMetadataManager(metadataManager) - .UseParallelism(parallelism) + .UseParallelism(_parallelism) .UseInteropStubManager(interopStubManager) .UseLogger(logger); - string scanDgmlLogFileName = Get(_command.ScanDgmlLogFileName); - if (scanDgmlLogFileName != null) - scannerBuilder.UseDependencyTracking(Get(_command.GenerateFullScanDgmlLog) ? - DependencyTrackingLevel.All : DependencyTrackingLevel.First); + if (_scanDgmlLogFileName != null) + scannerBuilder.UseDependencyTracking(_generateFullScanDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); IILScanner scanner = scannerBuilder.ToILScanner(); @@ -518,8 +888,8 @@ void RunScanner() scannerConstructedTypes = new List(scanResults.ConstructedEETypes); #endif - if (scanDgmlLogFileName != null) - scanResults.WriteDependencyLog(scanDgmlLogFileName); + if (_scanDgmlLogFileName != null) + scanResults.WriteDependencyLog(_scanDgmlLogFileName); metadataManager = ((UsageBasedMetadataManager)metadataManager).ToAnalysisBasedMetadataManager(); @@ -550,54 +920,47 @@ void RunScanner() builder.UseMethodImportationErrorProvider(scanResults.GetMethodImportationErrorProvider()); } - string ilDump = Get(_command.IlDump); - DebugInformationProvider debugInfoProvider = Get(_command.EnableDebugInfo) ? - (ilDump == null ? new DebugInformationProvider() : new ILAssemblyGeneratingMethodDebugInfoProvider(ilDump, new EcmaOnlyDebugInformationProvider())) : + DebugInformationProvider debugInfoProvider = _enableDebugInfo ? + (_ilDump == null ? new DebugInformationProvider() : new ILAssemblyGeneratingMethodDebugInfoProvider(_ilDump, new EcmaOnlyDebugInformationProvider())) : new NullDebugInformationProvider(); - string dgmlLogFileName = Get(_command.DgmlLogFileName); - DependencyTrackingLevel trackingLevel = dgmlLogFileName == null ? - DependencyTrackingLevel.None : (Get(_command.GenerateFullDgmlLog) ? - DependencyTrackingLevel.All : DependencyTrackingLevel.First); + DependencyTrackingLevel trackingLevel = _dgmlLogFileName == null ? + DependencyTrackingLevel.None : (_generateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); compilationRoots.Add(metadataManager); compilationRoots.Add(interopStubManager); builder .UseInstructionSetSupport(instructionSetSupport) - .UseBackendOptions(Get(_command.CodegenOptions)) - .UseMethodBodyFolding(enable: Get(_command.MethodBodyFolding)) - .UseParallelism(parallelism) + .UseBackendOptions(_codegenOptions) + .UseMethodBodyFolding(enable: _methodBodyFolding) + .UseParallelism(_parallelism) .UseMetadataManager(metadataManager) .UseInteropStubManager(interopStubManager) .UseLogger(logger) .UseDependencyTracking(trackingLevel) .UseCompilationRoots(compilationRoots) - .UseOptimizationMode(_command.OptimizationMode) + .UseOptimizationMode(_optimizationMode) .UseSecurityMitigationOptions(securityMitigationOptions) .UseDebugInfoProvider(debugInfoProvider) - .UseDwarf5(Get(_command.UseDwarf5)); + .UseDwarf5(_useDwarf5); - builder.UseResilience(Get(_command.Resilient)); + builder.UseResilience(_resilient); ICompilation compilation = builder.ToCompilation(); - string mapFileName = Get(_command.MapFileName); - string mstatFileName = Get(_command.MstatFileName); - List dumpers = new List(); - if (mapFileName != null) - dumpers.Add(new XmlObjectDumper(mapFileName)); + if (_mapFileName != null) + dumpers.Add(new XmlObjectDumper(_mapFileName)); - if (mstatFileName != null) - dumpers.Add(new MstatObjectDumper(mstatFileName, typeSystemContext)); + if (_mstatFileName != null) + dumpers.Add(new MstatObjectDumper(_mstatFileName, typeSystemContext)); - CompilationResults compilationResults = compilation.Compile(outputFilePath, ObjectDumper.Compose(dumpers)); - string exportsFile = Get(_command.ExportsFile); - if (exportsFile != null) + CompilationResults compilationResults = compilation.Compile(_outputFilePath, ObjectDumper.Compose(dumpers)); + if (_exportsFile != null) { - ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, exportsFile); + ExportsFileWriter defFileWriter = new ExportsFileWriter(typeSystemContext, _exportsFile); foreach (var compilationRoot in compilationRoots) { if (compilationRoot is ExportedMethodsRootProvider provider) @@ -609,8 +972,8 @@ void RunScanner() typeSystemContext.LogWarnings(logger); - if (dgmlLogFileName != null) - compilationResults.WriteDependencyLog(dgmlLogFileName); + if (_dgmlLogFileName != null) + compilationResults.WriteDependencyLog(_dgmlLogFileName); #if DEBUG if (scannerConstructedTypes != null) @@ -639,7 +1002,7 @@ static bool IsRelatedToInvalidInput(MethodDesc method) // If optimizations are enabled, the results will for sure not match in the other direction due to inlining, etc. // But there's at least some value in checking the scanner doesn't expand the universe too much in debug. - if (_command.OptimizationMode == OptimizationMode.None) + if (_optimizationMode == OptimizationMode.None) { // Check that methods and types scanned are a subset of methods and types compiled @@ -707,34 +1070,30 @@ private static TypeDesc FindType(CompilerTypeSystemContext context, string typeN private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) { - string singleMethodName = Get(_command.SingleMethodName); - string singleMethodTypeName = Get(_command.SingleMethodTypeName); - string[] singleMethodGenericArgs = Get(_command.SingleMethodGenericArgs); - - if (singleMethodName == null && singleMethodTypeName == null && singleMethodGenericArgs.Length == 0) + if (_singleMethodName == null && _singleMethodTypeName == null && _singleMethodGenericArgs == null) return null; - if (singleMethodName == null || singleMethodTypeName == null) + if (_singleMethodName == null || _singleMethodTypeName == null) throw new CommandLineException("Both method name and type name are required parameters for single method mode"); - TypeDesc owningType = FindType(context, singleMethodTypeName); + TypeDesc owningType = FindType(context, _singleMethodTypeName); // TODO: allow specifying signature to distinguish overloads - MethodDesc method = owningType.GetMethod(singleMethodName, null); + MethodDesc method = owningType.GetMethod(_singleMethodName, null); if (method == null) - throw new CommandLineException($"Method '{singleMethodName}' not found in '{singleMethodTypeName}'"); + throw new CommandLineException($"Method '{_singleMethodName}' not found in '{_singleMethodTypeName}'"); - if (method.HasInstantiation != (singleMethodGenericArgs != null) || - (method.HasInstantiation && (method.Instantiation.Length != singleMethodGenericArgs.Length))) + if (method.HasInstantiation != (_singleMethodGenericArgs != null) || + (method.HasInstantiation && (method.Instantiation.Length != _singleMethodGenericArgs.Count))) { throw new CommandLineException( - $"Expected {method.Instantiation.Length} generic arguments for method '{singleMethodName}' on type '{singleMethodTypeName}'"); + $"Expected {method.Instantiation.Length} generic arguments for method '{_singleMethodName}' on type '{_singleMethodTypeName}'"); } if (method.HasInstantiation) { List genericArguments = new List(); - foreach (var argString in singleMethodGenericArgs) + foreach (var argString in _singleMethodGenericArgs) genericArguments.Add(FindType(context, argString)); method = method.MakeInstantiatedMethod(genericArguments.ToArray()); } @@ -742,6 +1101,23 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont return method; } + private static bool DumpReproArguments(CodeGenerationFailedException ex) + { + Console.WriteLine("To repro, add following arguments to the command line:"); + + MethodDesc failingMethod = ex.Method; + + var formatter = new CustomAttributeTypeNameFormatter((IAssemblyDesc)failingMethod.Context.SystemModule); + + Console.Write($"--singlemethodtypename \"{formatter.FormatName(failingMethod.OwningType, true)}\""); + Console.Write($" --singlemethodname {failingMethod.Name}"); + + for (int i = 0; i < failingMethod.Instantiation.Length; i++) + Console.Write($" --singlemethodgenericarg \"{formatter.FormatName(failingMethod.Instantiation[i], true)}\""); + + return false; + } + private static IEnumerable ProcessWarningCodes(IEnumerable warningCodes) { foreach (string value in warningCodes) @@ -757,14 +1133,29 @@ private static IEnumerable ProcessWarningCodes(IEnumerable warningC } } - private T Get(Option option) => _command.Result.GetValueForOption(option); - - private static int Main(string[] args) => - new CommandLineBuilder(new ILCompilerRootCommand(args)) - .UseVersionOption("-v") - .UseHelp(context => context.HelpBuilder.CustomizeLayout(ILCompilerRootCommand.GetExtendedHelp)) - .UseParseErrorReporting() - .Build() - .Invoke(args); + private static int Main(string[] args) + { +#if DEBUG + try + { + return new Program().Run(args); + } + catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + { + throw new NotSupportedException(); // Unreachable + } +#else + try + { + return new Program().Run(args); + } + catch (Exception e) + { + Console.Error.WriteLine("Error: " + e.Message); + Console.Error.WriteLine(e.ToString()); + return 1; + } +#endif + } } } diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs index b86d53165e51c7..94389b716e22f5 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs @@ -73,7 +73,6 @@ public void Trim (ILCompilerOptions options, ILogWriter logWriter) logger, Array.Empty> (), Array.Empty (), - Array.Empty (), options.TrimAssemblies.ToArray ()); CompilationBuilder builder = new RyuJitCompilationBuilder (typeSystemContext, compilationGroup) diff --git a/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs new file mode 100644 index 00000000000000..d298ae8a2019b2 --- /dev/null +++ b/src/coreclr/tools/aot/crossgen2/CommandLineOptions.cs @@ -0,0 +1,273 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Text; + +using Internal.CommandLine; +using Internal.TypeSystem; + +namespace ILCompiler +{ + internal class CommandLineOptions + { + public const int DefaultPerfMapFormatVersion = 0; + + public bool Help; + public string HelpText; + public bool Version; + + public IReadOnlyList InputFilePaths; + public IReadOnlyList InputBubbleReferenceFilePaths; + public IReadOnlyList UnrootedInputFilePaths; + public IReadOnlyList ReferenceFilePaths; + public IReadOnlyList MibcFilePaths; + public IReadOnlyList CrossModuleInlining; + public string InstructionSet; + public string OutputFilePath; + + public string CompositeRootPath; + public bool Optimize; + public bool OptimizeDisabled; + public bool OptimizeSpace; + public bool OptimizeTime; + public bool AsyncMethodOptimization; + public string NonLocalGenericsModule; + public bool InputBubble; + public bool CompileBubbleGenerics; + public bool Verbose; + public bool Composite; + public string CompositeKeyFile; + public bool CompileNoMethods; + public bool EmbedPgoData; + public bool OutNearInput; + public bool SingleFileCompilation; + + public string DgmlLogFileName; + public bool GenerateFullDgmlLog; + + public string TargetArch; + public string TargetOS; + public string JitPath; + public string SystemModule; + public bool WaitForDebugger; + public bool Partial; + public bool Resilient; + public bool Map; + public bool MapCsv; + public bool PrintReproInstructions; + public bool Pdb; + public bool SupportIbc; + public string PdbPath; + public bool PerfMap; + public string PerfMapPath; + public int PerfMapFormatVersion; + public int Parallelism; + public int CustomPESectionAlignment; + public string MethodLayout; + public string FileLayout; + public bool VerifyTypeAndFieldLayout; + public string CallChainProfileFile; + public string ImageBase; + + public string SingleMethodTypeName; + public string SingleMethodName; + public int SingleMethodIndex; + public IReadOnlyList SingleMethodGenericArg; + + public IReadOnlyList CodegenOptions; + + public string MakeReproPath; + + public bool CompositeOrInputBubble => Composite || InputBubble; + + public CommandLineOptions(string[] args) + { + InputFilePaths = Array.Empty(); + InputBubbleReferenceFilePaths = Array.Empty(); + UnrootedInputFilePaths = Array.Empty(); + ReferenceFilePaths = Array.Empty(); + MibcFilePaths = Array.Empty(); + CodegenOptions = Array.Empty(); + NonLocalGenericsModule = ""; + + PerfMapFormatVersion = DefaultPerfMapFormatVersion; + // Limit parallelism to 24 wide at most by default, more parallelism is unlikely to improve compilation speed + // as many portions of the process are single threaded, and is known to use excessive memory. + Parallelism = Math.Min(24, Environment.ProcessorCount); + + // On 32bit platforms restrict it more, as virtual address space is quite limited + if (!Environment.Is64BitProcess) + Parallelism = Math.Min(4, Parallelism); + + SingleMethodGenericArg = null; + + // These behaviors default to enabled + AsyncMethodOptimization = true; + + bool forceHelp = false; + if (args.Length == 0) + { + forceHelp = true; + } + + foreach (string arg in args) + { + if (arg == "-?") + forceHelp = true; + } + + if (forceHelp) + { + args = new string[] {"--help"}; + } + + ArgumentSyntax argSyntax = ArgumentSyntax.Parse(args, syntax => + { + syntax.ApplicationName = typeof(Program).Assembly.GetName().Name.ToString(); + + // HandleHelp writes to error, fails fast with crash dialog and lacks custom formatting. + syntax.HandleHelp = false; + syntax.HandleErrors = true; + + syntax.DefineOptionList("u|unrooted-input-file-paths", ref UnrootedInputFilePaths, SR.UnrootedInputFilesToCompile); + syntax.DefineOptionList("r|reference", ref ReferenceFilePaths, SR.ReferenceFiles); + syntax.DefineOption("instruction-set", ref InstructionSet, SR.InstructionSets); + syntax.DefineOptionList("m|mibc", ref MibcFilePaths, SR.MibcFiles); + syntax.DefineOption("o|out|outputfilepath", ref OutputFilePath, SR.OutputFilePath); + syntax.DefineOption("crp|compositerootpath", ref CompositeRootPath, SR.CompositeRootPath); + syntax.DefineOption("O|optimize", ref Optimize, SR.EnableOptimizationsOption); + syntax.DefineOption("Od|optimize-disabled", ref OptimizeDisabled, SR.DisableOptimizationsOption); + syntax.DefineOption("Os|optimize-space", ref OptimizeSpace, SR.OptimizeSpaceOption); + syntax.DefineOption("Ot|optimize-time", ref OptimizeTime, SR.OptimizeSpeedOption); + syntax.DefineOption("inputbubble", ref InputBubble, SR.InputBubbleOption); + syntax.DefineOptionList("inputbubbleref", ref InputBubbleReferenceFilePaths, SR.InputBubbleReferenceFiles); + syntax.DefineOption("composite", ref Composite, SR.CompositeBuildMode); + syntax.DefineOption("compositekeyfile", ref CompositeKeyFile, SR.CompositeKeyFile); + syntax.DefineOption("compile-no-methods", ref CompileNoMethods, SR.CompileNoMethodsOption); + syntax.DefineOption("out-near-input", ref OutNearInput, SR.OutNearInputOption); + syntax.DefineOption("single-file-compilation", ref SingleFileCompilation, SR.SingleFileCompilationOption); + syntax.DefineOption("partial", ref Partial, SR.PartialImageOption); + syntax.DefineOption("compilebubblegenerics", ref CompileBubbleGenerics, SR.BubbleGenericsOption); + syntax.DefineOption("embed-pgo-data", ref EmbedPgoData, SR.EmbedPgoDataOption); + syntax.DefineOption("dgmllog|dgml-log-file-name", ref DgmlLogFileName, SR.SaveDependencyLogOption); + syntax.DefineOption("fulllog|generate-full-dmgl-log", ref GenerateFullDgmlLog, SR.SaveDetailedLogOption); + syntax.DefineOption("verbose", ref Verbose, SR.VerboseLoggingOption); + syntax.DefineOption("systemmodule", ref SystemModule, SR.SystemModuleOverrideOption); + syntax.DefineOption("waitfordebugger", ref WaitForDebugger, SR.WaitForDebuggerOption); + syntax.DefineOptionList("codegenopt|codegen-options", ref CodegenOptions, SR.CodeGenOptions); + syntax.DefineOption("support-ibc", ref SupportIbc, SR.SupportIbc); + syntax.DefineOption("resilient", ref Resilient, SR.ResilientOption); + syntax.DefineOption("imagebase", ref ImageBase, SR.ImageBase); + + syntax.DefineOption("targetarch", ref TargetArch, SR.TargetArchOption); + syntax.DefineOption("targetos", ref TargetOS, SR.TargetOSOption); + syntax.DefineOption("jitpath", ref JitPath, SR.JitPathOption); + + syntax.DefineOption("print-repro-instructions", ref PrintReproInstructions, SR.PrintReproInstructionsOption); + syntax.DefineOption("singlemethodtypename", ref SingleMethodTypeName, SR.SingleMethodTypeName); + syntax.DefineOption("singlemethodname", ref SingleMethodName, SR.SingleMethodMethodName); + syntax.DefineOption("singlemethodindex", ref SingleMethodIndex, SR.SingleMethodIndex); + syntax.DefineOptionList("singlemethodgenericarg", ref SingleMethodGenericArg, SR.SingleMethodGenericArgs); + + syntax.DefineOption("parallelism", ref Parallelism, SR.ParalellismOption); + syntax.DefineOption("custom-pe-section-alignment", ref CustomPESectionAlignment, SR.CustomPESectionAlignmentOption); + syntax.DefineOption("map", ref Map, SR.MapFileOption); + syntax.DefineOption("mapcsv", ref MapCsv, SR.MapCsvFileOption); + syntax.DefineOption("pdb", ref Pdb, SR.PdbFileOption); + syntax.DefineOption("pdb-path", ref PdbPath, SR.PdbFilePathOption); + syntax.DefineOption("perfmap", ref PerfMap, SR.PerfMapFileOption); + syntax.DefineOption("perfmap-path", ref PerfMapPath, SR.PerfMapFilePathOption); + syntax.DefineOption("perfmap-format-version", ref PerfMapFormatVersion, SR.PerfMapFormatVersionOption); + + syntax.DefineOptionList("opt-cross-module", ref this.CrossModuleInlining, SR.CrossModuleInlining); + syntax.DefineOption("opt-async-methods", ref AsyncMethodOptimization, SR.AsyncModuleOptimization); + syntax.DefineOption("non-local-generics-module", ref NonLocalGenericsModule, SR.NonLocalGenericsModule); + + syntax.DefineOption("method-layout", ref MethodLayout, SR.MethodLayoutOption); + syntax.DefineOption("file-layout", ref FileLayout, SR.FileLayoutOption); + syntax.DefineOption("verify-type-and-field-layout", ref VerifyTypeAndFieldLayout, SR.VerifyTypeAndFieldLayoutOption); + syntax.DefineOption("callchain-profile", ref CallChainProfileFile, SR.CallChainProfileFile); + + syntax.DefineOption("make-repro-path", ref MakeReproPath, SR.MakeReproPathHelp); + + syntax.DefineOption("h|help", ref Help, SR.HelpOption); + syntax.DefineOption("v|version", ref Version, SR.VersionOption); + + syntax.DefineParameterList("in", ref InputFilePaths, SR.InputFilesToCompile); + }); + + if (Help) + { + List extraHelp = new List(); + extraHelp.Add(SR.OptionPassingHelp); + extraHelp.Add(""); + extraHelp.Add(SR.DashDashHelp); + extraHelp.Add(""); + + string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64"}; + string[] ValidOS = new string[] {"windows", "linux", "osx"}; + TargetOS defaultOs; + TargetArchitecture defaultArch; + Program.ComputeDefaultOptions(out defaultOs, out defaultArch); + + extraHelp.Add(String.Format(SR.SwitchWithDefaultHelp, "--targetos", String.Join("', '", ValidOS), defaultOs.ToString().ToLowerInvariant())); + + extraHelp.Add(""); + + extraHelp.Add(String.Format(SR.SwitchWithDefaultHelp, "--targetarch", String.Join("', '", ValidArchitectures), defaultArch.ToString().ToLowerInvariant())); + + extraHelp.Add(""); + + extraHelp.Add(SR.InstructionSetHelp); + foreach (string arch in ValidArchitectures) + { + StringBuilder archString = new StringBuilder(); + + archString.Append(arch); + archString.Append(": "); + + TargetArchitecture targetArch = Program.GetTargetArchitectureFromArg(arch, out _); + bool first = true; + foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) + { + // Only instruction sets with are specifiable should be printed to the help text + if (instructionSet.Specifiable) + { + if (first) + { + first = false; + } + else + { + archString.Append(", "); + } + archString.Append(instructionSet.Name); + } + } + + extraHelp.Add(archString.ToString()); + } + + extraHelp.Add(""); + extraHelp.Add(SR.CpuFamilies); + extraHelp.Add(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); + + argSyntax.ExtraHelpParagraphs = extraHelp; + + HelpText = argSyntax.GetHelpText(); + } + + if (MakeReproPath != null) + { + // Create a repro package in the specified path + // This package will have the set of input files needed for compilation + // + the original command line arguments + // + a rsp file that should work to directly run out of the zip file + + Helpers.MakeReproPackage(MakeReproPath, OutputFilePath, args, argSyntax, new[] { "-r", "-u", "-m", "--inputbubbleref" }); + } + } + } +} diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs deleted file mode 100644 index 8569f9fbf1cb79..00000000000000 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ /dev/null @@ -1,376 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.CommandLine; -using System.CommandLine.Help; -using System.CommandLine.Parsing; -using System.IO; -using System.Runtime.InteropServices; - -using Internal.TypeSystem; - -namespace ILCompiler -{ - internal class Crossgen2RootCommand : RootCommand - { - public Argument> InputFilePaths { get; } = - new("input-file-path", result => Helpers.BuildPathDictionay(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; - public Option> UnrootedInputFilePaths { get; } = - new(new[] { "--unrooted-input-file-paths", "-u" }, result => Helpers.BuildPathDictionay(result.Tokens, true), true, SR.UnrootedInputFilesToCompile); - public Option> ReferenceFilePaths { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, SR.ReferenceFiles); - public Option InstructionSet { get; } = - new(new[] { "--instruction-set" }, SR.InstructionSets); - public Option MibcFilePaths { get; } = - new(new[] { "--mibc", "-m" }, Array.Empty, SR.MibcFiles); - public Option OutputFilePath { get; } = - new(new[] { "--out", "-o" }, SR.OutputFilePath); - public Option CompositeRootPath { get; } = - new(new[] { "--compositerootpath", "--crp" }, SR.CompositeRootPath); - public Option Optimize { get; } = - new(new[] { "--optimize", "-O" }, SR.EnableOptimizationsOption); - public Option OptimizeDisabled { get; } = - new(new[] { "--optimize-disabled", "-Od" }, SR.DisableOptimizationsOption); - public Option OptimizeSpace { get; } = - new(new[] { "--optimize-space", "-Os" }, SR.OptimizeSpaceOption); - public Option OptimizeTime { get; } = - new(new[] { "--optimize-time", "-Ot" }, SR.OptimizeSpeedOption); - public Option InputBubble { get; } = - new(new[] { "--inputbubble" }, SR.InputBubbleOption); - public Option> InputBubbleReferenceFilePaths { get; } = - new(new[] { "--inputbubbleref" }, result => Helpers.BuildPathDictionay(result.Tokens, false), true, SR.InputBubbleReferenceFiles); - public Option Composite { get; } = - new(new[] { "--composite" }, SR.CompositeBuildMode); - public Option CompositeKeyFile { get; } = - new(new[] { "--compositekeyfile" }, SR.CompositeKeyFile); - public Option CompileNoMethods { get; } = - new(new[] { "--compile-no-methods" }, SR.CompileNoMethodsOption); - public Option OutNearInput { get; } = - new(new[] { "--out-near-input" }, SR.OutNearInputOption); - public Option SingleFileCompilation { get; } = - new(new[] { "--single-file-compilation" }, SR.SingleFileCompilationOption); - public Option Partial { get; } = - new(new[] { "--partial" }, SR.PartialImageOption); - public Option CompileBubbleGenerics { get; } = - new(new[] { "--compilebubblegenerics" }, SR.BubbleGenericsOption); - public Option EmbedPgoData { get; } = - new(new[] { "--embed-pgo-data" }, SR.EmbedPgoDataOption); - public Option DgmlLogFileName { get; } = - new(new[] { "--dgmllog" }, SR.SaveDependencyLogOption); - public Option GenerateFullDgmlLog { get; } = - new(new[] { "--fulllog" }, SR.SaveDetailedLogOption); - public Option IsVerbose { get; } = - new(new[] { "--verbose" }, SR.VerboseLoggingOption); - public Option SystemModuleName { get; } = - new(new[] { "--systemmodule" }, () => Helpers.DefaultSystemModule, SR.SystemModuleOverrideOption); - public Option WaitForDebugger { get; } = - new(new[] { "--waitfordebugger" }, SR.WaitForDebuggerOption); - public Option CodegenOptions { get; } = - new(new[] { "--codegenopt" }, Array.Empty, SR.CodeGenOptions); - public Option SupportIbc { get; } = - new(new[] { "--support-ibc" }, SR.SupportIbc); - public Option Resilient { get; } = - new(new[] { "--resilient" }, SR.ResilientOption); - public Option ImageBase { get; } = - new(new[] { "--imagebase" }, SR.ImageBase); - public Option TargetArchitecture { get; } = - new(new[] { "--targetarch" }, result => - { - string firstToken = result.Tokens.Count > 0 ? result.Tokens[0].Value : null; - if (firstToken != null && firstToken.Equals("armel", StringComparison.OrdinalIgnoreCase)) - { - IsArmel = true; - return Internal.TypeSystem.TargetArchitecture.ARM; - } - - return Helpers.GetTargetArchitecture(firstToken); - }, true, SR.TargetArchOption) { Arity = ArgumentArity.OneOrMore }; - public Option TargetOS { get; } = - new(new[] { "--targetos" }, result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), true, SR.TargetOSOption); - public Option JitPath { get; } = - new(new[] { "--jitpath" }, SR.JitPathOption); - public Option PrintReproInstructions { get; } = - new(new[] { "--print-repro-instructions" }, SR.PrintReproInstructionsOption); - public Option SingleMethodTypeName { get; } = - new(new[] { "--singlemethodtypename" }, SR.SingleMethodTypeName); - public Option SingleMethodName { get; } = - new(new[] { "--singlemethodname" }, SR.SingleMethodMethodName); - public Option SingleMethodIndex { get; } = - new(new[] { "--singlemethodindex" }, SR.SingleMethodIndex); - public Option SingleMethodGenericArgs { get; } = - new(new[] { "--singlemethodgenericarg" }, SR.SingleMethodGenericArgs); - public Option Parallelism { get; } = - new(new[] { "--parallelism" }, result => - { - if (result.Tokens.Count > 0) - return int.Parse(result.Tokens[0].Value); - - // Limit parallelism to 24 wide at most by default, more parallelism is unlikely to improve compilation speed - // as many portions of the process are single threaded, and is known to use excessive memory. - var parallelism = Math.Min(24, Environment.ProcessorCount); - - // On 32bit platforms restrict it more, as virtual address space is quite limited - if (!Environment.Is64BitProcess) - parallelism = Math.Min(4, parallelism); - - return parallelism; - }, true, SR.ParalellismOption); - public Option CustomPESectionAlignment { get; } = - new(new[] { "--custom-pe-section-alignment" }, SR.CustomPESectionAlignmentOption); - public Option Map { get; } = - new(new[] { "--map" }, SR.MapFileOption); - public Option MapCsv { get; } = - new(new[] { "--mapcsv" }, SR.MapCsvFileOption); - public Option Pdb { get; } = - new(new[] { "--pdb" }, SR.PdbFileOption); - public Option PdbPath { get; } = - new(new[] { "--pdb-path" }, SR.PdbFilePathOption); - public Option PerfMap { get; } = - new(new[] { "--perfmap" }, SR.PerfMapFileOption); - public Option PerfMapPath { get; } = - new(new[] { "--perfmap-path" }, SR.PerfMapFilePathOption); - public Option PerfMapFormatVersion { get; } = - new(new[] { "--perfmap-format-version" }, () => 0, SR.PerfMapFormatVersionOption); - public Option CrossModuleInlining { get; } = - new(new[] { "--opt-cross-module" }, SR.CrossModuleInlining); - public Option AsyncMethodOptimization { get; } = - new(new[] { "--opt-async-methods" }, SR.AsyncModuleOptimization); - public Option NonLocalGenericsModule { get; } = - new(new[] { "--non-local-generics-module" }, () => string.Empty, SR.NonLocalGenericsModule); - public Option MethodLayout { get; } = - new(new[] { "--method-layout" }, result => - { - if (result.Tokens.Count == 0 ) - return ReadyToRunMethodLayoutAlgorithm.DefaultSort; - - return result.Tokens[0].Value.ToLowerInvariant() switch - { - "defaultsort" => ReadyToRunMethodLayoutAlgorithm.DefaultSort, - "exclusiveweight" => ReadyToRunMethodLayoutAlgorithm.ExclusiveWeight, - "hotcold" => ReadyToRunMethodLayoutAlgorithm.HotCold, - "hotwarmcold" => ReadyToRunMethodLayoutAlgorithm.HotWarmCold, - "callfrequency" => ReadyToRunMethodLayoutAlgorithm.CallFrequency, - "pettishansen" => ReadyToRunMethodLayoutAlgorithm.PettisHansen, - "random" => ReadyToRunMethodLayoutAlgorithm.Random, - _ => throw new CommandLineException(SR.InvalidMethodLayout) - }; - }, true, SR.MethodLayoutOption); - public Option FileLayout { get; } = - new(new[] { "--file-layout" }, result => - { - if (result.Tokens.Count == 0 ) - return ReadyToRunFileLayoutAlgorithm.DefaultSort; - - return result.Tokens[0].Value.ToLowerInvariant() switch - { - "defaultsort" => ReadyToRunFileLayoutAlgorithm.DefaultSort, - "methodorder" => ReadyToRunFileLayoutAlgorithm.MethodOrder, - _ => throw new CommandLineException(SR.InvalidFileLayout) - }; - }, true, SR.FileLayoutOption); - public Option VerifyTypeAndFieldLayout { get; } = - new(new[] { "--verify-type-and-field-layout" }, SR.VerifyTypeAndFieldLayoutOption); - public Option CallChainProfileFile { get; } = - new(new[] { "--callchain-profile" }, SR.CallChainProfileFile); - public Option MakeReproPath { get; } = - new(new[] { "--make-repro-path" }, "Path where to place a repro package"); - - public bool CompositeOrInputBubble { get; private set; } - public OptimizationMode OptimizationMode { get; private set; } - public ParseResult Result { get; private set; } - - public static bool IsArmel { get; private set; } - - public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) - { - AddArgument(InputFilePaths); - AddOption(UnrootedInputFilePaths); - AddOption(ReferenceFilePaths); - AddOption(InstructionSet); - AddOption(MibcFilePaths); - AddOption(OutputFilePath); - AddOption(CompositeRootPath); - AddOption(Optimize); - AddOption(OptimizeDisabled); - AddOption(OptimizeSpace); - AddOption(OptimizeTime); - AddOption(InputBubble); - AddOption(InputBubbleReferenceFilePaths); - AddOption(Composite); - AddOption(CompositeKeyFile); - AddOption(CompileNoMethods); - AddOption(OutNearInput); - AddOption(SingleFileCompilation); - AddOption(Partial); - AddOption(CompileBubbleGenerics); - AddOption(EmbedPgoData); - AddOption(DgmlLogFileName); - AddOption(GenerateFullDgmlLog); - AddOption(IsVerbose); - AddOption(SystemModuleName); - AddOption(WaitForDebugger); - AddOption(CodegenOptions); - AddOption(SupportIbc); - AddOption(Resilient); - AddOption(ImageBase); - AddOption(TargetArchitecture); - AddOption(TargetOS); - AddOption(JitPath); - AddOption(PrintReproInstructions); - AddOption(SingleMethodTypeName); - AddOption(SingleMethodName); - AddOption(SingleMethodIndex); - AddOption(SingleMethodGenericArgs); - AddOption(Parallelism); - AddOption(CustomPESectionAlignment); - AddOption(Map); - AddOption(MapCsv); - AddOption(Pdb); - AddOption(PdbPath); - AddOption(PerfMap); - AddOption(PerfMapPath); - AddOption(PerfMapFormatVersion); - AddOption(CrossModuleInlining); - AddOption(AsyncMethodOptimization); - AddOption(NonLocalGenericsModule); - AddOption(MethodLayout); - AddOption(FileLayout); - AddOption(VerifyTypeAndFieldLayout); - AddOption(CallChainProfileFile); - AddOption(MakeReproPath); - - this.SetHandler(context => - { - Result = context.ParseResult; - CompositeOrInputBubble = context.ParseResult.GetValueForOption(Composite) | context.ParseResult.GetValueForOption(InputBubble); - if (context.ParseResult.GetValueForOption(OptimizeSpace)) - { - OptimizationMode = OptimizationMode.PreferSize; - } - else if (context.ParseResult.GetValueForOption(OptimizeTime)) - { - OptimizationMode = OptimizationMode.PreferSpeed; - } - else if (context.ParseResult.GetValueForOption(Optimize)) - { - OptimizationMode = OptimizationMode.Blended; - } - else - { - OptimizationMode = OptimizationMode.None; - } - - try - { - int alignment = context.ParseResult.GetValueForOption(CustomPESectionAlignment); - if (alignment != 0) - { - // Must be a power of two and >= 4096 - if (alignment < 4096 || (alignment & (alignment - 1)) != 0) - throw new CommandLineException(SR.InvalidCustomPESectionAlignment); - } - - string makeReproPath = context.ParseResult.GetValueForOption(MakeReproPath); - if (makeReproPath != null) - { - // Create a repro package in the specified path - // This package will have the set of input files needed for compilation - // + the original command line arguments - // + a rsp file that should work to directly run out of the zip file - - Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValueForOption(OutputFilePath), args, - context.ParseResult, new[] { "r", "reference", "u", "unrooted-input-file-paths", "m", "mibc", "inputbubbleref" }); - } - - context.ExitCode = new Program(this).Run(); - } -#if DEBUG - catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) - { - throw new NotSupportedException(); // Unreachable - } -#else - catch (Exception e) - { - Console.ResetColor(); - Console.ForegroundColor = ConsoleColor.Red; - - Console.Error.WriteLine("Error: " + e.Message); - Console.Error.WriteLine(e.ToString()); - - Console.ResetColor(); - - context.ExitCode = 1; - } -#endif - }); - } - - public static IEnumerable GetExtendedHelp(HelpContext _) - { - foreach (HelpSectionDelegate sectionDelegate in HelpBuilder.Default.GetLayout()) - yield return sectionDelegate; - - yield return _ => - { - Console.WriteLine(SR.OptionPassingHelp); - Console.WriteLine(); - Console.WriteLine(SR.DashDashHelp); - Console.WriteLine(); - - string[] ValidArchitectures = new string[] {"arm", "armel", "arm64", "x86", "x64"}; - string[] ValidOS = new string[] {"windows", "linux", "osx"}; - - Console.WriteLine(String.Format(SR.SwitchWithDefaultHelp, "--targetos", String.Join("', '", ValidOS), Helpers.GetTargetOS(null).ToString().ToLowerInvariant())); - Console.WriteLine(); - Console.WriteLine(String.Format(SR.SwitchWithDefaultHelp, "--targetarch", String.Join("', '", ValidArchitectures), Helpers.GetTargetArchitecture(null).ToString().ToLowerInvariant())); - Console.WriteLine(); - - Console.WriteLine(SR.InstructionSetHelp); - foreach (string arch in ValidArchitectures) - { - Console.Write(arch); - Console.Write(": "); - - TargetArchitecture targetArch = Helpers.GetTargetArchitecture(arch); - bool first = true; - foreach (var instructionSet in Internal.JitInterface.InstructionSetFlags.ArchitectureToValidInstructionSets(targetArch)) - { - // Only instruction sets with are specifiable should be printed to the help text - if (instructionSet.Specifiable) - { - if (first) - { - first = false; - } - else - { - Console.Write(", "); - } - Console.Write(instructionSet.Name); - } - } - - Console.WriteLine(); - } - - Console.WriteLine(); - Console.WriteLine(SR.CpuFamilies); - Console.WriteLine(string.Join(", ", Internal.JitInterface.InstructionSetFlags.AllCpuNames)); - }; - } - -#if DEBUG - private static bool DumpReproArguments(CodeGenerationFailedException ex) - { - Console.WriteLine(SR.DumpReproInstructions); - - MethodDesc failingMethod = ex.Method; - Console.WriteLine(Program.CreateReproArgumentString(failingMethod)); - return false; - } -#endif - } -} diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index 9a407f9eeb0ae6..7c07d757d50156 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -3,8 +3,6 @@ using System; using System.Buffers.Binary; -using System.CommandLine; -using System.CommandLine.Parsing; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; @@ -14,6 +12,7 @@ using System.Runtime.InteropServices; using System.Text; +using Internal.CommandLine; using Internal.IL; using Internal.TypeSystem; using Internal.TypeSystem.Ecma; @@ -21,42 +20,224 @@ using ILCompiler.Reflection.ReadyToRun; using ILCompiler.DependencyAnalysis; using ILCompiler.IBC; +using System.Diagnostics; namespace ILCompiler { internal class Program { - private readonly Crossgen2RootCommand _command; + private const string DefaultSystemModule = "System.Private.CoreLib"; + + private CommandLineOptions _commandLineOptions; + public TargetOS _targetOS; + public TargetArchitecture _targetArchitecture; + private bool _armelAbi = false; + public OptimizationMode _optimizationMode; + private ulong _imageBase; + + // File names as strings in args + private Dictionary _inputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + private Dictionary _unrootedInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + private Dictionary _referenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); // Modules and their names after loading - private Dictionary _allInputFilePaths = new(); - private List _referenceableModules = new(); + private Dictionary _allInputFilePaths = new Dictionary(); + private List _referenceableModules = new List(); - private ReadyToRunCompilerContext _typeSystemContext; - private ulong _imageBase; + private Dictionary _inputbubblereferenceFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); + + private CompilerTypeSystemContext _typeSystemContext; + private ReadyToRunMethodLayoutAlgorithm _methodLayout; + private ReadyToRunFileLayoutAlgorithm _fileLayout; - private readonly bool _inputBubble; - private readonly bool _singleFileCompilation; - private readonly bool _outNearInput; - private readonly string _outputFilePath; - private readonly TargetArchitecture _targetArchitecture; - private readonly TargetOS _targetOS; + private Program() + { + } - public Program(Crossgen2RootCommand command) + public static void ComputeDefaultOptions(out TargetOS os, out TargetArchitecture arch) { - _command = command; - _inputBubble = Get(command.InputBubble); - _singleFileCompilation = Get(command.SingleFileCompilation); - _outNearInput = Get(command.OutNearInput); - _outputFilePath = Get(command.OutputFilePath); - _targetOS = Get(command.TargetOS); - _targetArchitecture = Get(command.TargetArchitecture); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + os = TargetOS.Windows; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + os = TargetOS.Linux; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + os = TargetOS.OSX; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + os = TargetOS.FreeBSD; + else + throw new NotImplementedException(); - if (command.Result.GetValueForOption(command.WaitForDebugger)) + switch (RuntimeInformation.ProcessArchitecture) { - Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue"); + case Architecture.X86: + arch = TargetArchitecture.X86; + break; + case Architecture.X64: + arch = TargetArchitecture.X64; + break; + case Architecture.Arm: + arch = TargetArchitecture.ARM; + break; + case Architecture.Arm64: + arch = TargetArchitecture.ARM64; + break; + case Architecture.LoongArch64: + arch = TargetArchitecture.LoongArch64; + break; + default: + throw new NotImplementedException(); + } + + } + + private void InitializeDefaultOptions() + { + ComputeDefaultOptions(out _targetOS, out _targetArchitecture); + } + + private void ProcessCommandLine(string[] args) + { + PerfEventSource.StartStopEvents.CommandLineProcessingStart(); + _commandLineOptions = new CommandLineOptions(args); + PerfEventSource.StartStopEvents.CommandLineProcessingStop(); + + if (_commandLineOptions.Help || _commandLineOptions.Version) + { + return; + } + + if (_commandLineOptions.WaitForDebugger) + { + Console.WriteLine(SR.WaitingForDebuggerAttach); Console.ReadLine(); } + + if (_commandLineOptions.CompileBubbleGenerics) + { + if (!_commandLineOptions.CompositeOrInputBubble) + { + Console.WriteLine(SR.WarningIgnoringBubbleGenerics); + _commandLineOptions.CompileBubbleGenerics = false; + } + } + + _optimizationMode = OptimizationMode.None; + if (_commandLineOptions.OptimizeDisabled) + { + if (_commandLineOptions.Optimize || _commandLineOptions.OptimizeSpace || _commandLineOptions.OptimizeTime) + Console.WriteLine(SR.WarningOverridingOptimize); + } + else if (_commandLineOptions.OptimizeSpace) + { + if (_commandLineOptions.OptimizeTime) + Console.WriteLine(SR.WarningOverridingOptimizeSpace); + _optimizationMode = OptimizationMode.PreferSize; + } + else if (_commandLineOptions.OptimizeTime) + _optimizationMode = OptimizationMode.PreferSpeed; + else if (_commandLineOptions.Optimize) + _optimizationMode = OptimizationMode.Blended; + + foreach (var input in _commandLineOptions.InputFilePaths) + Helpers.AppendExpandedPaths(_inputFilePaths, input, true); + + foreach (var input in _commandLineOptions.UnrootedInputFilePaths) + Helpers.AppendExpandedPaths(_unrootedInputFilePaths, input, true); + + foreach (var reference in _commandLineOptions.ReferenceFilePaths) + Helpers.AppendExpandedPaths(_referenceFilePaths, reference, false); + + foreach (var reference in _commandLineOptions.InputBubbleReferenceFilePaths) + Helpers.AppendExpandedPaths(_inputbubblereferenceFilePaths, reference, false); + + + int alignment = _commandLineOptions.CustomPESectionAlignment; + if (alignment != 0) + { + // Must be a power of two and >= 4096 + if (alignment < 4096 || (alignment & (alignment - 1)) != 0) + throw new CommandLineException(SR.InvalidCustomPESectionAlignment); + } + + if (_commandLineOptions.MethodLayout != null) + { + _methodLayout = _commandLineOptions.MethodLayout.ToLowerInvariant() switch + { + "defaultsort" => ReadyToRunMethodLayoutAlgorithm.DefaultSort, + "exclusiveweight" => ReadyToRunMethodLayoutAlgorithm.ExclusiveWeight, + "hotcold" => ReadyToRunMethodLayoutAlgorithm.HotCold, + "hotwarmcold" => ReadyToRunMethodLayoutAlgorithm.HotWarmCold, + "callfrequency" => ReadyToRunMethodLayoutAlgorithm.CallFrequency, + "pettishansen" => ReadyToRunMethodLayoutAlgorithm.PettisHansen, + "random" => ReadyToRunMethodLayoutAlgorithm.Random, + _ => throw new CommandLineException(SR.InvalidMethodLayout) + }; + } + + if (_commandLineOptions.FileLayout != null) + { + _fileLayout = _commandLineOptions.FileLayout.ToLowerInvariant() switch + { + "defaultsort" => ReadyToRunFileLayoutAlgorithm.DefaultSort, + "methodorder" => ReadyToRunFileLayoutAlgorithm.MethodOrder, + _ => throw new CommandLineException(SR.InvalidFileLayout) + }; + } + + } + + private string GetCompilerVersion() + { + return Assembly + .GetExecutingAssembly() + .GetCustomAttribute() + .InformationalVersion; + } + + public static TargetArchitecture GetTargetArchitectureFromArg(string archArg, out bool armelAbi) + { + armelAbi = false; + if (archArg.Equals("x86", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.X86; + else if (archArg.Equals("x64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.X64; + else if (archArg.Equals("arm", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.ARM; + else if (archArg.Equals("armel", StringComparison.OrdinalIgnoreCase)) + { + armelAbi = true; + return TargetArchitecture.ARM; + } + else if (archArg.Equals("arm64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.ARM64; + else if (archArg.Equals("loongarch64", StringComparison.OrdinalIgnoreCase)) + return TargetArchitecture.LoongArch64; + else + throw new CommandLineException(SR.TargetArchitectureUnsupported); + } + + private void ConfigureTarget() + { + // + // Set target Architecture and OS + // + if (_commandLineOptions.TargetArch != null) + { + _targetArchitecture = GetTargetArchitectureFromArg(_commandLineOptions.TargetArch, out _armelAbi); + } + if (_commandLineOptions.TargetOS != null) + { + if (_commandLineOptions.TargetOS.Equals("windows", StringComparison.OrdinalIgnoreCase)) + _targetOS = TargetOS.Windows; + else if (_commandLineOptions.TargetOS.Equals("linux", StringComparison.OrdinalIgnoreCase)) + _targetOS = TargetOS.Linux; + else if (_commandLineOptions.TargetOS.Equals("osx", StringComparison.OrdinalIgnoreCase)) + _targetOS = TargetOS.OSX; + else if (_commandLineOptions.TargetOS.Equals("freebsd", StringComparison.OrdinalIgnoreCase)) + _targetOS = TargetOS.FreeBSD; + else + throw new CommandLineException(SR.TargetOSUnsupported); + } } private InstructionSetSupport ConfigureInstructionSetSupport() @@ -81,13 +262,12 @@ private InstructionSetSupport ConfigureInstructionSetSupport() } } - string instructionSetArg = Get(_command.InstructionSet); - if (instructionSetArg != null) + if (_commandLineOptions.InstructionSet != null) { List instructionSetParams = new List(); // Normalize instruction set format to include implied +. - string[] instructionSetParamsInput = instructionSetArg.Split(","); + string[] instructionSetParamsInput = _commandLineOptions.InstructionSet.Split(","); for (int i = 0; i < instructionSetParamsInput.Length; i++) { string instructionSet = instructionSetParamsInput[i]; @@ -152,52 +332,66 @@ private InstructionSetSupport ConfigureInstructionSetSupport() optimisticInstructionSet.Add(supportedInstructionSet); return new InstructionSetSupport(supportedInstructionSet, - unsupportedInstructionSet, - optimisticInstructionSet, - InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), - _targetArchitecture); + unsupportedInstructionSet, + optimisticInstructionSet, + InstructionSetSupportBuilder.GetNonSpecifiableInstructionSetsForArch(_targetArchitecture), + _targetArchitecture); } private void ConfigureImageBase(TargetDetails targetDetails) { bool is64BitTarget = targetDetails.PointerSize == sizeof(long); - string imageBaseArg = Get(_command.ImageBase); - if (imageBaseArg != null) - _imageBase = is64BitTarget ? Convert.ToUInt64(imageBaseArg, 16) : Convert.ToUInt32(imageBaseArg, 16); + if (_commandLineOptions.ImageBase != null) + _imageBase = is64BitTarget ? Convert.ToUInt64(_commandLineOptions.ImageBase, 16) : Convert.ToUInt32(_commandLineOptions.ImageBase, 16); else _imageBase = is64BitTarget ? PEWriter.PE64HeaderConstants.DllImageBase : PEWriter.PE32HeaderConstants.ImageBase; } - public int Run() + private int Run(string[] args) { - if (_outputFilePath == null && !_outNearInput) + InitializeDefaultOptions(); + + ProcessCommandLine(args); + + if (_commandLineOptions.Help) + { + Console.WriteLine(_commandLineOptions.HelpText); + return 1; + } + + if (_commandLineOptions.Version) + { + string version = GetCompilerVersion(); + Console.WriteLine(version); + return 0; + } + + if (_commandLineOptions.OutputFilePath == null && !_commandLineOptions.OutNearInput) throw new CommandLineException(SR.MissingOutputFile); - if (_singleFileCompilation && !_outNearInput) + if (_commandLineOptions.SingleFileCompilation && !_commandLineOptions.OutNearInput) throw new CommandLineException(SR.MissingOutNearInput); + ConfigureTarget(); InstructionSetSupport instructionSetSupport = ConfigureInstructionSetSupport(); SharedGenericsMode genericsMode = SharedGenericsMode.CanonicalReferenceTypes; - var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, Crossgen2RootCommand.IsArmel ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); + var targetDetails = new TargetDetails(_targetArchitecture, _targetOS, _armelAbi ? TargetAbi.NativeAotArmel : TargetAbi.NativeAot, instructionSetSupport.GetVectorTSimdVector()); ConfigureImageBase(targetDetails); bool versionBubbleIncludesCoreLib = false; - Dictionary inputFilePathsArg = _command.Result.GetValueForArgument(_command.InputFilePaths); - Dictionary unrootedInputFilePathsArg = Get(_command.UnrootedInputFilePaths); - - if (_inputBubble) + if (_commandLineOptions.InputBubble) { versionBubbleIncludesCoreLib = true; } else { - if (!_singleFileCompilation) + if (!_commandLineOptions.SingleFileCompilation) { - foreach (var inputFile in inputFilePathsArg) + foreach (var inputFile in _inputFilePaths) { if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) { @@ -208,7 +402,7 @@ public int Run() } if (!versionBubbleIncludesCoreLib) { - foreach (var inputFile in unrootedInputFilePathsArg) + foreach (var inputFile in _unrootedInputFilePaths) { if (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0) { @@ -224,13 +418,12 @@ public int Run() // _typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, versionBubbleIncludesCoreLib); - string compositeRootPath = Get(_command.CompositeRootPath); + string compositeRootPath = _commandLineOptions.CompositeRootPath; // Collections for already loaded modules Dictionary inputFilePaths = new Dictionary(); Dictionary unrootedInputFilePaths = new Dictionary(); HashSet versionBubbleModulesHash = new HashSet(); - Dictionary referenceFilePaths = Get(_command.ReferenceFilePaths); using (PerfEventSource.StartStopEvents.LoadingEvents()) { @@ -244,7 +437,7 @@ public int Run() // typeSystemContext.InputFilePaths = inFilePaths; // - foreach (var inputFile in inputFilePathsArg) + foreach (var inputFile in _inputFilePaths) { try { @@ -269,7 +462,7 @@ public int Run() } } - foreach (var unrootedInputFile in unrootedInputFilePathsArg) + foreach (var unrootedInputFile in _unrootedInputFilePaths) { try { @@ -294,11 +487,11 @@ public int Run() CheckManagedCppInputFiles(_allInputFilePaths.Values); _typeSystemContext.InputFilePaths = _allInputFilePaths; - _typeSystemContext.ReferenceFilePaths = referenceFilePaths; + _typeSystemContext.ReferenceFilePaths = _referenceFilePaths; if (_typeSystemContext.InputFilePaths.Count == 0) { - if (inputFilePathsArg.Count > 0) + if (_commandLineOptions.InputFilePaths.Count > 0) { Console.WriteLine(SR.InputWasNotLoadable); return 2; @@ -306,8 +499,7 @@ public int Run() throw new CommandLineException(SR.NoInputFiles); } - Dictionary inputBubbleReferenceFilePaths = Get(_command.InputBubbleReferenceFilePaths); - foreach (var referenceFile in referenceFilePaths.Values) + foreach (var referenceFile in _referenceFilePaths.Values) { try { @@ -316,7 +508,7 @@ public int Run() continue; _referenceableModules.Add(module); - if (_inputBubble && inputBubbleReferenceFilePaths.Count == 0) + if (_commandLineOptions.InputBubble && _inputbubblereferenceFilePaths.Count == 0) { // In large version bubble mode add reference paths to the compilation group // Consider bubble as large if no explicit bubble references were passed @@ -326,9 +518,9 @@ public int Run() catch { } // Ignore non-managed pe files } - if (_inputBubble) + if (_commandLineOptions.InputBubble) { - foreach (var referenceFile in inputBubbleReferenceFilePaths.Values) + foreach (var referenceFile in _inputbubblereferenceFilePaths.Values) { try { @@ -344,11 +536,11 @@ public int Run() } } - string systemModuleName = Get(_command.SystemModuleName) ?? Helpers.DefaultSystemModule; + string systemModuleName = _commandLineOptions.SystemModule ?? DefaultSystemModule; _typeSystemContext.SetSystemModule((EcmaModule)_typeSystemContext.GetModuleForSimpleName(systemModuleName)); CompilerTypeSystemContext typeSystemContext = _typeSystemContext; - if (_singleFileCompilation) + if (_commandLineOptions.SingleFileCompilation) { var singleCompilationInputFilePaths = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -360,13 +552,13 @@ public int Run() singleCompilationInputFilePaths.Add(inputFile.Key, inputFile.Value); typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; - if (!_inputBubble) + if (!_commandLineOptions.InputBubble) { bool singleCompilationVersionBubbleIncludesCoreLib = versionBubbleIncludesCoreLib || (String.Compare(inputFile.Key, "System.Private.CoreLib", StringComparison.OrdinalIgnoreCase) == 0); typeSystemContext = new ReadyToRunCompilerContext(targetDetails, genericsMode, singleCompilationVersionBubbleIncludesCoreLib, _typeSystemContext); typeSystemContext.InputFilePaths = singleCompilationInputFilePaths; - typeSystemContext.ReferenceFilePaths = referenceFilePaths; + typeSystemContext.ReferenceFilePaths = _referenceFilePaths; typeSystemContext.SetSystemModule((EcmaModule)typeSystemContext.GetModuleForSimpleName(systemModuleName)); } @@ -375,7 +567,7 @@ public int Run() // In case of inputbubble ni.dll are created as ni.dll.tmp in order to not interfere with crossgen2, move them all to ni.dll // See https://github.com/dotnet/runtime/issues/55663#issuecomment-898161751 for more details - if (_inputBubble) + if (_commandLineOptions.InputBubble) { foreach (var inputFile in inputFilePaths) { @@ -399,25 +591,21 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru // // Initialize output filename // - var e = inFilePaths.GetEnumerator(); - e.MoveNext(); - string inFilePath = e.Current.Value; + string inFilePath = inFilePaths.First().Value; string inputFileExtension = Path.GetExtension(inFilePath); string nearOutFilePath = inputFileExtension switch { ".dll" => Path.ChangeExtension(inFilePath, - _singleFileCompilation&& _inputBubble + _commandLineOptions.SingleFileCompilation && _commandLineOptions.InputBubble ? ".ni.dll.tmp" : ".ni.dll"), ".exe" => Path.ChangeExtension(inFilePath, - _singleFileCompilation && _inputBubble + _commandLineOptions.SingleFileCompilation && _commandLineOptions.InputBubble ? ".ni.exe.tmp" : ".ni.exe"), _ => throw new CommandLineException(string.Format(SR.UnsupportedInputFileExtension, inputFileExtension)) }; - - string outFile = _outNearInput ? nearOutFilePath : _outputFilePath; - string dgmlLogFileName = Get(_command.DgmlLogFileName); + string outFile = _commandLineOptions.OutNearInput ? nearOutFilePath : _commandLineOptions.OutputFilePath; using (PerfEventSource.StartStopEvents.CompilationEvents()) { @@ -436,7 +624,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru versionBubbleModulesHash.Add(module); - if (!_command.CompositeOrInputBubble) + if (!_commandLineOptions.CompositeOrInputBubble) { break; } @@ -449,17 +637,16 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru versionBubbleModulesHash.Add(module); } - string[] crossModuleInlining = Get(_command.CrossModuleInlining); - if (crossModuleInlining.Length > 0) + if (_commandLineOptions.CrossModuleInlining != null) { - foreach (var crossModulePgoAssemblyName in crossModuleInlining) + foreach (var crossModulePgoAssemblyName in _commandLineOptions.CrossModuleInlining) { foreach (var module in _referenceableModules) { if (!versionBubbleModulesHash.Contains(module)) { if (crossModulePgoAssemblyName == "*" || - (String.Compare(crossModulePgoAssemblyName, module.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase) == 0)) + (String.Compare(crossModulePgoAssemblyName, module.Assembly.GetName().Name, StringComparison.OrdinalIgnoreCase) == 0)) { crossModuleInlineableCode.Add((EcmaModule)module); } @@ -475,45 +662,44 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru // Single method mode? MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext); - var logger = new Logger(Console.Out, Get(_command.IsVerbose)); + var logger = new Logger(Console.Out, _commandLineOptions.Verbose); List mibcFiles = new List(); - foreach (var file in Get(_command.MibcFilePaths)) + foreach (var file in _commandLineOptions.MibcFilePaths) { mibcFiles.Add(file); } List versionBubbleModules = new List(versionBubbleModulesHash); - bool composite = Get(_command.Composite); - if (!composite && inputModules.Count != 1) + + if (!_commandLineOptions.Composite && inputModules.Count != 1) { throw new Exception(string.Format(SR.ErrorMultipleInputFilesCompositeModeOnly, string.Join("; ", inputModules))); } - bool compileBubbleGenerics = Get(_command.CompileBubbleGenerics); + ReadyToRunCompilationModuleGroupBase compilationGroup; List compilationRoots = new List(); ReadyToRunCompilationModuleGroupConfig groupConfig = new ReadyToRunCompilationModuleGroupConfig(); groupConfig.Context = typeSystemContext; - groupConfig.IsCompositeBuildMode = composite; - groupConfig.IsInputBubble = _inputBubble; + groupConfig.IsCompositeBuildMode = _commandLineOptions.Composite; + groupConfig.IsInputBubble = _commandLineOptions.InputBubble; groupConfig.CompilationModuleSet = inputModules; groupConfig.VersionBubbleModuleSet = versionBubbleModules; - groupConfig.CompileGenericDependenciesFromVersionBubbleModuleSet = compileBubbleGenerics; + groupConfig.CompileGenericDependenciesFromVersionBubbleModuleSet = _commandLineOptions.CompileBubbleGenerics; groupConfig.CrossModuleGenericCompilation = crossModuleInlineableCode.Count > 0; groupConfig.CrossModuleInlining = groupConfig.CrossModuleGenericCompilation; // Currently we set these flags to the same values groupConfig.CrossModuleInlineable = crossModuleInlineableCode; groupConfig.CompileAllPossibleCrossModuleCode = false; // Handle non-local generics command line option - ModuleDesc nonLocalGenericsHome = compileBubbleGenerics ? inputModules[0] : null; - string nonLocalGenericsModule = Get(_command.NonLocalGenericsModule); - if (nonLocalGenericsModule == "*") + ModuleDesc nonLocalGenericsHome = _commandLineOptions.CompileBubbleGenerics ? inputModules[0] : null; + if (_commandLineOptions.NonLocalGenericsModule == "*") { groupConfig.CompileAllPossibleCrossModuleCode = true; nonLocalGenericsHome = inputModules[0]; } - else if (nonLocalGenericsModule == "") + else if (_commandLineOptions.NonLocalGenericsModule == "") { // Nothing was specified } @@ -522,11 +708,11 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru bool matchFound = false; // Allow module to be specified by assembly name or by filename - if (nonLocalGenericsModule.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) - nonLocalGenericsModule = Path.GetFileNameWithoutExtension(nonLocalGenericsModule); + if (_commandLineOptions.NonLocalGenericsModule.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + _commandLineOptions.NonLocalGenericsModule = Path.GetFileNameWithoutExtension(_commandLineOptions.NonLocalGenericsModule); foreach (var module in inputModules) { - if (String.Compare(module.Assembly.GetName().Name, nonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) + if (String.Compare(module.Assembly.GetName().Name, _commandLineOptions.NonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) { matchFound = true; nonLocalGenericsHome = module; @@ -539,7 +725,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru { foreach (var module in _referenceableModules) { - if (String.Compare(module.Assembly.GetName().Name, nonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) + if (String.Compare(module.Assembly.GetName().Name, _commandLineOptions.NonLocalGenericsModule, StringComparison.OrdinalIgnoreCase) == 0) { matchFound = true; break; @@ -548,12 +734,11 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru if (!matchFound) { - throw new CommandLineException(string.Format(SR.ErrorNonLocalGenericsModule, nonLocalGenericsModule)); + throw new CommandLineException(string.Format(SR.ErrorNonLocalGenericsModule, _commandLineOptions.NonLocalGenericsModule)); } } } - bool compileNoMethods = Get(_command.CompileNoMethods); if (singleMethod != null) { // Compiling just a single method @@ -562,7 +747,7 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru singleMethod); compilationRoots.Add(new SingleMethodRootProvider(singleMethod)); } - else if (compileNoMethods) + else if (_commandLineOptions.CompileNoMethods) { compilationGroup = new NoMethodsCompilationModuleGroup(groupConfig); } @@ -574,10 +759,10 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru // Load any profiles generated by method call chain analyis CallChainProfile jsonProfile = null; - string callChainProfileFile = Get(_command.CallChainProfileFile); - if (!string.IsNullOrEmpty(callChainProfileFile)) + + if (!string.IsNullOrEmpty(_commandLineOptions.CallChainProfileFile)) { - jsonProfile = new CallChainProfile(callChainProfileFile, typeSystemContext, _referenceableModules); + jsonProfile = new CallChainProfile(_commandLineOptions.CallChainProfileFile, typeSystemContext, _referenceableModules); } // Examine profile guided information as appropriate @@ -603,14 +788,13 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru jsonProfile, typeSystemContext, compilationGroup, - Get(_command.EmbedPgoData), - Get(_command.SupportIbc), + _commandLineOptions.EmbedPgoData, + _commandLineOptions.SupportIbc, crossModuleInlineableCode.Count == 0 ? compilationGroup.VersionsWithMethodBody : compilationGroup.CrossModuleInlineable); - bool partial = Get(_command.Partial); - compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, partial); + compilationGroup.ApplyProfileGuidedOptimizationData(profileDataManager, _commandLineOptions.Partial); - if ((singleMethod == null) && !compileNoMethods) + if ((singleMethod == null) && !_commandLineOptions.CompileNoMethods) { // For normal compilations add compilation roots. foreach (var module in rootingModules) @@ -618,9 +802,9 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru compilationRoots.Add(new ReadyToRunRootProvider( module, profileDataManager, - profileDrivenPartialNGen: partial)); + profileDrivenPartialNGen: _commandLineOptions.Partial)); - if (!_command.CompositeOrInputBubble) + if (!_commandLineOptions.CompositeOrInputBubble) { break; } @@ -628,18 +812,18 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru } // In single-file compilation mode, use the assembly's DebuggableAttribute to determine whether to optimize // or produce debuggable code if an explicit optimization level was not specified on the command line - OptimizationMode optimizationMode = _command.OptimizationMode; - if (optimizationMode == OptimizationMode.None && !Get(_command.OptimizeDisabled) && !composite) + OptimizationMode optimizationMode = _optimizationMode; + if (optimizationMode == OptimizationMode.None && !_commandLineOptions.OptimizeDisabled && !_commandLineOptions.Composite) { System.Diagnostics.Debug.Assert(inputModules.Count == 1); optimizationMode = ((EcmaAssembly)inputModules[0].Assembly).HasOptimizationsDisabled() ? OptimizationMode.None : OptimizationMode.Blended; } CompositeImageSettings compositeImageSettings = new CompositeImageSettings(); - string compositeKeyFile = Get(_command.CompositeKeyFile); - if (compositeKeyFile != null) + + if (_commandLineOptions.CompositeKeyFile != null) { - byte[] compositeStrongNameKey = File.ReadAllBytes(compositeKeyFile); + byte[] compositeStrongNameKey = File.ReadAllBytes(_commandLineOptions.CompositeKeyFile); if (!IsValidPublicKey(compositeStrongNameKey)) { throw new Exception(string.Format(SR.ErrorCompositeKeyFileNotPublicKey)); @@ -659,38 +843,38 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru ILProvider ilProvider = new ReadyToRunILProvider(compilationGroup); - DependencyTrackingLevel trackingLevel = dgmlLogFileName == null ? - DependencyTrackingLevel.None : (Get(_command.GenerateFullDgmlLog) ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); + DependencyTrackingLevel trackingLevel = _commandLineOptions.DgmlLogFileName == null ? + DependencyTrackingLevel.None : (_commandLineOptions.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First); NodeFactoryOptimizationFlags nodeFactoryFlags = new NodeFactoryOptimizationFlags(); - nodeFactoryFlags.OptimizeAsyncMethods = Get(_command.AsyncMethodOptimization); + nodeFactoryFlags.OptimizeAsyncMethods = _commandLineOptions.AsyncMethodOptimization; builder - .UseMapFile(Get(_command.Map)) - .UseMapCsvFile(Get(_command.MapCsv)) - .UsePdbFile(Get(_command.Pdb), Get(_command.PdbPath)) - .UsePerfMapFile(Get(_command.PerfMap), Get(_command.PerfMapPath), Get(_command.PerfMapFormatVersion)) + .UseMapFile(_commandLineOptions.Map) + .UseMapCsvFile(_commandLineOptions.MapCsv) + .UsePdbFile(_commandLineOptions.Pdb, _commandLineOptions.PdbPath) + .UsePerfMapFile(_commandLineOptions.PerfMap, _commandLineOptions.PerfMapPath, _commandLineOptions.PerfMapFormatVersion) .UseProfileFile(jsonProfile != null) .UseProfileData(profileDataManager) .UseNodeFactoryOptimizationFlags(nodeFactoryFlags) - .FileLayoutAlgorithms(Get(_command.MethodLayout), Get(_command.FileLayout)) + .FileLayoutAlgorithms(_methodLayout, _fileLayout) .UseCompositeImageSettings(compositeImageSettings) - .UseJitPath(Get(_command.JitPath)) + .UseJitPath(_commandLineOptions.JitPath) .UseInstructionSetSupport(instructionSetSupport) - .UseCustomPESectionAlignment(Get(_command.CustomPESectionAlignment)) - .UseVerifyTypeAndFieldLayout(Get(_command.VerifyTypeAndFieldLayout)) + .UseCustomPESectionAlignment(_commandLineOptions.CustomPESectionAlignment) + .UseVerifyTypeAndFieldLayout(_commandLineOptions.VerifyTypeAndFieldLayout) .GenerateOutputFile(outFile) .UseImageBase(_imageBase) .UseILProvider(ilProvider) - .UseBackendOptions(Get(_command.CodegenOptions)) + .UseBackendOptions(_commandLineOptions.CodegenOptions) .UseLogger(logger) - .UseParallelism(Get(_command.Parallelism)) - .UseResilience(Get(_command.Resilient)) + .UseParallelism(_commandLineOptions.Parallelism) + .UseResilience(_commandLineOptions.Resilient) .UseDependencyTracking(trackingLevel) .UseCompilationRoots(compilationRoots) .UseOptimizationMode(optimizationMode); - if (Get(_command.PrintReproInstructions)) + if (_commandLineOptions.PrintReproInstructions) builder.UsePrintReproInstructions(CreateReproArgumentString); compilation = builder.ToCompilation(); @@ -698,8 +882,8 @@ private void RunSingleCompilation(Dictionary inFilePaths, Instru } compilation.Compile(outFile); - if (dgmlLogFileName != null) - compilation.WriteDependencyLog(dgmlLogFileName); + if (_commandLineOptions.DgmlLogFileName != null) + compilation.WriteDependencyLog(_commandLineOptions.DgmlLogFileName); compilation.Dispose(); } @@ -734,16 +918,13 @@ private TypeDesc FindType(CompilerTypeSystemContext context, string typeName) private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context) { - string[] singleMethodGenericArgs = Get(_command.SingleMethodGenericArgs); - string singleMethodName = Get(_command.SingleMethodName); - string singleMethodTypeName = Get(_command.SingleMethodTypeName); - if (singleMethodName == null && singleMethodTypeName == null && singleMethodGenericArgs.Length == 0) + if (_commandLineOptions.SingleMethodName == null && _commandLineOptions.SingleMethodTypeName == null && _commandLineOptions.SingleMethodGenericArg == null) return null; - if (singleMethodName == null || singleMethodTypeName == null) + if (_commandLineOptions.SingleMethodName == null || _commandLineOptions.SingleMethodTypeName == null) throw new CommandLineException(SR.TypeAndMethodNameNeeded); - TypeDesc owningType = FindType(context, singleMethodTypeName); + TypeDesc owningType = FindType(context, _commandLineOptions.SingleMethodTypeName); // TODO: allow specifying signature to distinguish overloads MethodDesc method = null; @@ -751,13 +932,13 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont int curIndex = 0; foreach (var searchMethod in owningType.GetMethods()) { - if (searchMethod.Name != singleMethodName) + if (searchMethod.Name != _commandLineOptions.SingleMethodName) continue; curIndex++; - if (Get(_command.SingleMethodIndex) != 0) + if (_commandLineOptions.SingleMethodIndex != 0) { - if (curIndex == Get(_command.SingleMethodIndex)) + if (curIndex == _commandLineOptions.SingleMethodIndex) { method = searchMethod; break; @@ -781,7 +962,7 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont curIndex = 0; foreach (var searchMethod in owningType.GetMethods()) { - if (searchMethod.Name != singleMethodName) + if (searchMethod.Name != _commandLineOptions.SingleMethodName) continue; curIndex++; @@ -791,19 +972,19 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont } if (method == null) - throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, singleMethodName, singleMethodTypeName)); + throw new CommandLineException(string.Format(SR.MethodNotFoundOnType, _commandLineOptions.SingleMethodName, _commandLineOptions.SingleMethodTypeName)); - if (method.HasInstantiation != (singleMethodGenericArgs != null) || - (method.HasInstantiation && (method.Instantiation.Length != singleMethodGenericArgs.Length))) + if (method.HasInstantiation != (_commandLineOptions.SingleMethodGenericArg != null) || + (method.HasInstantiation && (method.Instantiation.Length != _commandLineOptions.SingleMethodGenericArg.Count))) { throw new CommandLineException( - string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, singleMethodName, singleMethodTypeName)); + string.Format(SR.GenericArgCountMismatch, method.Instantiation.Length, _commandLineOptions.SingleMethodName, _commandLineOptions.SingleMethodTypeName)); } if (method.HasInstantiation) { List genericArguments = new List(); - foreach (var argString in singleMethodGenericArgs) + foreach (var argString in _commandLineOptions.SingleMethodGenericArg) genericArguments.Add(FindType(context, argString)); method = method.MakeInstantiatedMethod(genericArguments.ToArray()); } @@ -811,7 +992,7 @@ private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemCont return method; } - internal static string CreateReproArgumentString(MethodDesc method) + private static string CreateReproArgumentString(MethodDesc method) { StringBuilder sb = new StringBuilder(); @@ -970,14 +1151,31 @@ internal static bool IsValidPublicKey(byte[] blob) return true; } - private T Get(Option option) => _command.Result.GetValueForOption(option); - private static int Main(string[] args) => - new CommandLineBuilder(new Crossgen2RootCommand(args)) - .UseVersionOption("-v") - .UseHelp(context => context.HelpBuilder.CustomizeLayout(Crossgen2RootCommand.GetExtendedHelp)) - .UseParseErrorReporting() - .Build() - .Invoke(args); + private static int Main(string[] args) + { +#if DEBUG + try + { + return new Program().Run(args); + } + catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) + { + throw new NotSupportedException(); // Unreachable + } +#else + try + { + return new Program().Run(args); + } + catch (Exception e) + { + Console.Error.WriteLine(string.Format(SR.ProgramError, e.Message)); + Console.Error.WriteLine(e.ToString()); + return 1; + } +#endif + + } } } diff --git a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx index 2a3ca76475437b..b76655992d1d83 100644 --- a/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx +++ b/src/coreclr/tools/aot/crossgen2/Properties/Resources.resx @@ -393,7 +393,4 @@ Enable processing of Ibc data embedded in the input assembly. - - .NET Crossgen2 Compiler - - + \ No newline at end of file diff --git a/src/coreclr/tools/aot/crossgen2/crossgen2.props b/src/coreclr/tools/aot/crossgen2/crossgen2.props index dee32ed5ba401d..4a98176cabcbcc 100644 --- a/src/coreclr/tools/aot/crossgen2/crossgen2.props +++ b/src/coreclr/tools/aot/crossgen2/crossgen2.props @@ -28,10 +28,6 @@ - - - - @@ -39,7 +35,21 @@ - + + + + + + + + + + + + + + +