diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9097e7ec72d4d63a147dcef8a1da9243f8b9c47d..e0d0e309c8071a93d5c32e68b4d4e7ab25480380 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -76,13 +76,13 @@ https://github.com/dotnet/llvm-project 76f334f354eb653a7b409a5319b591ea09df5a43 - + https://github.com/dotnet/command-line-api - 8374d5fca634a93458c84414b1604c12f765d1ab + 02fe27cd6a9b001c8feb7938e6ef4b3799745759 - + https://github.com/dotnet/command-line-api - 8374d5fca634a93458c84414b1604c12f765d1ab + 02fe27cd6a9b001c8feb7938e6ef4b3799745759b diff --git a/eng/Versions.props b/eng/Versions.props index 13372c13bdcb5e8fc1381bcda1b45bb9238e9d39..5f81f25b9c5a8e83645ad37d95852ddcf5cf1a5d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -161,7 +161,7 @@ 1.0.0-prerelease.23326.3 16.11.27-beta1.23180.1 - 2.0.0-beta4.22564.1 + 2.0.0-beta4.23307.1 3.0.3 2.1.0 2.0.3 diff --git a/src/coreclr/tools/Common/CommandLineHelpers.cs b/src/coreclr/tools/Common/CommandLineHelpers.cs index 2475d2a64213d899fd586a6199b4c40ba334dfae..205592c1c91dca3560145688cbfcf09d7d807c48 100644 --- a/src/coreclr/tools/Common/CommandLineHelpers.cs +++ b/src/coreclr/tools/Common/CommandLineHelpers.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.CommandLine.Help; using System.Collections.Generic; -using System.CommandLine.Binding; using System.CommandLine.Parsing; using System.IO; using System.IO.Compression; @@ -24,11 +24,11 @@ internal static partial class Helpers { public const string DefaultSystemModule = "System.Private.CoreLib"; - public static Dictionary BuildPathDictionary(IReadOnlyList tokens, bool strict) + public static Dictionary BuildPathDictionary(IReadOnlyList tokens, bool strict) { Dictionary dictionary = new(StringComparer.OrdinalIgnoreCase); - foreach (Token token in tokens) + foreach (CliToken token in tokens) { AppendExpandedPaths(dictionary, token.Value, strict); } @@ -36,11 +36,11 @@ internal static partial class Helpers return dictionary; } - public static List BuildPathList(IReadOnlyList tokens) + public static List BuildPathList(IReadOnlyList tokens) { List paths = new(); Dictionary dictionary = new(StringComparer.OrdinalIgnoreCase); - foreach (Token token in tokens) + foreach (CliToken token in tokens) { AppendExpandedPaths(dictionary, token.Value, false); foreach (string file in dictionary.Values) @@ -113,6 +113,36 @@ public static TargetArchitecture GetTargetArchitecture(string token) } } + public static CliRootCommand UseVersion(this CliRootCommand command) + { + for (int i = 0; i < command.Options.Count; i++) + { + if (command.Options[i] is VersionOption) + { + command.Options[i] = new VersionOption("--version", "-v"); + break; + } + } + + return command; + } + + public static CliRootCommand UseExtendedHelp(this CliRootCommand command, Func>> customizer) + { + foreach (CliOption option in command.Options) + { + if (option is HelpOption helpOption) + { + HelpBuilder builder = new(); + builder.CustomizeLayout(customizer); + helpOption.Action = new HelpAction { Builder = builder }; + break; + } + } + + return command; + } + public static void MakeReproPackage(string makeReproPath, string outputFilePath, string[] args, ParseResult res, IEnumerable inputOptions, IEnumerable outputOptions = null) { Directory.CreateDirectory(makeReproPath); @@ -177,16 +207,16 @@ public static void MakeReproPackage(string makeReproPath, string outputFilePath, Dictionary outputToReproPackageFileName = new(); List rspFile = new List(); - foreach (var option in res.CommandResult.Command.Options) + foreach (CliOption option in res.CommandResult.Command.Options) { - if (!res.HasOption(option) || option.Name == "make-repro-path") + OptionResult optionResult = res.GetResult(option); + if (optionResult is null || option.Name == "make-repro-path") { continue; } - IValueDescriptor descriptor = option; - object val = res.CommandResult.GetValue(option); - if (val is not null && !(descriptor.HasDefaultValue && descriptor.GetDefaultValue().Equals(val))) + object val = optionResult.GetValueOrDefault(); + if (val is not null && !optionResult.Implicit) { if (val is IEnumerable || val is IDictionary) { @@ -234,9 +264,15 @@ public static void MakeReproPackage(string makeReproPath, string outputFilePath, } } - foreach (var argument in res.CommandResult.Command.Arguments) + foreach (CliArgument argument in res.CommandResult.Command.Arguments) { - object val = res.CommandResult.GetValue(argument); + ArgumentResult argumentResult = res.GetResult(argument); + if (argumentResult is null) + { + continue; + } + + object val = argumentResult.GetValueOrDefault(); if (val is IEnumerable || val is IDictionary) { if (val is not IEnumerable values) diff --git a/src/coreclr/tools/ILVerify/ILVerifyRootCommand.cs b/src/coreclr/tools/ILVerify/ILVerifyRootCommand.cs index af9f26c0293fe4759454b844cde24b655c56d7b9..34723086ec979817383c5937c30b94588df1c555 100644 --- a/src/coreclr/tools/ILVerify/ILVerifyRootCommand.cs +++ b/src/coreclr/tools/ILVerify/ILVerifyRootCommand.cs @@ -8,61 +8,61 @@ namespace ILVerify { - internal sealed class ILVerifyRootCommand : RootCommand + internal sealed class ILVerifyRootCommand : CliRootCommand { - public Argument> InputFilePath { get; } = - new("input-file-path", result => Helpers.BuildPathDictionary(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; - public Option> Reference { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionary(result.Tokens, false), false, "Reference metadata from the specified assembly"); - public Option SystemModule { get; } = - new(new[] { "--system-module", "-s" }, "System module name (default: mscorlib)"); - public Option SanityChecks { get; } = - new(new[] { "--sanity-checks", "-c" }, "Check for valid constructs that are likely mistakes"); - public Option Include { get; } = - new(new[] { "--include", "-i" }, "Use only methods/types/namespaces, which match the given regular expression(s)"); - public Option IncludeFile { get; } = - new Option(new[] { "--include-file" }, "Same as --include, but the regular expression(s) are declared line by line in the specified file.").AcceptExistingOnly(); - public Option Exclude { get; } = - new(new[] { "--exclude", "-e" }, "Skip methods/types/namespaces, which match the given regular expression(s)"); - public Option ExcludeFile { get; } = - new Option(new[] { "--exclude-file" }, "Same as --exclude, but the regular expression(s) are declared line by line in the specified file.").AcceptExistingOnly(); - public Option IgnoreError { get; } = - new(new[] { "--ignore-error", "-g" }, "Ignore errors, which match the given regular expression(s)"); - public Option IgnoreErrorFile { get; } = - new Option(new[] { "--ignore-error-file" }, "Same as --ignore-error, but the regular expression(s) are declared line by line in the specified file.").AcceptExistingOnly(); - public Option Statistics { get; } = - new(new[] { "--statistics" }, "Print verification statistics"); - public Option Verbose { get; } = - new(new[] { "--verbose" }, "Verbose output"); - public Option Tokens { get; } = - new(new[] { "--tokens", "-t" }, "Include metadata tokens in error messages"); + public CliArgument> InputFilePath { get; } = + new("input-file-path") { CustomParser = result => Helpers.BuildPathDictionary(result.Tokens, true), Description = "Input file(s)", Arity = ArgumentArity.OneOrMore }; + public CliOption> Reference { get; } = + new("--reference", "-r") { CustomParser = result => Helpers.BuildPathDictionary(result.Tokens, false), Description = "Reference metadata from the specified assembly" }; + public CliOption SystemModule { get; } = + new("--system-module", "-s") { Description = "System module name (default: mscorlib)" }; + public CliOption SanityChecks { get; } = + new("--sanity-checks", "-c") { Description = "Check for valid constructs that are likely mistakes" }; + public CliOption Include { get; } = + new("--include", "-i") { Description = "Use only methods/types/namespaces, which match the given regular expression(s)" }; + public CliOption IncludeFile { get; } = + new CliOption("--include-file") { Description = "Same as --include, but the regular expression(s) are declared line by line in the specified file." }.AcceptExistingOnly(); + public CliOption Exclude { get; } = + new("--exclude", "-e") { Description = "Skip methods/types/namespaces, which match the given regular expression(s)" }; + public CliOption ExcludeFile { get; } = + new CliOption("--exclude-file") { Description = "Same as --exclude, but the regular expression(s) are declared line by line in the specified file." }.AcceptExistingOnly(); + public CliOption IgnoreError { get; } = + new("--ignore-error", "-g") { Description = "Ignore errors, which match the given regular expression(s)" }; + public CliOption IgnoreErrorFile { get; } = + new CliOption("--ignore-error-file") { Description = "Same as --ignore-error, but the regular expression(s) are declared line by line in the specified file." }.AcceptExistingOnly(); + public CliOption Statistics { get; } = + new("--statistics") { Description = "Print verification statistics" }; + public CliOption Verbose { get; } = + new("--verbose") { Description = "Verbose output" }; + public CliOption Tokens { get; } = + new("--tokens", "-t") { Description = "Include metadata tokens in error messages" }; public ParseResult Result; public ILVerifyRootCommand() : base("Tool for verifying MSIL code based on ECMA-335.") { - AddArgument(InputFilePath); - AddOption(Reference); - AddOption(SystemModule); - AddOption(SanityChecks); - AddOption(Include); - AddOption(IncludeFile); - AddOption(Exclude); - AddOption(ExcludeFile); - AddOption(IgnoreError); - AddOption(IgnoreErrorFile); - AddOption(Statistics); - AddOption(Verbose); - AddOption(Tokens); + Arguments.Add(InputFilePath); + Options.Add(Reference); + Options.Add(SystemModule); + Options.Add(SanityChecks); + Options.Add(Include); + Options.Add(IncludeFile); + Options.Add(Exclude); + Options.Add(ExcludeFile); + Options.Add(IgnoreError); + Options.Add(IgnoreErrorFile); + Options.Add(Statistics); + Options.Add(Verbose); + Options.Add(Tokens); - this.SetHandler(context => + this.SetAction(result => { - Result = context.ParseResult; + Result = result; try { - context.ExitCode = new Program(this).Run(); + return new Program(this).Run(); } catch (Exception e) { @@ -73,9 +73,9 @@ public ILVerifyRootCommand() Console.Error.WriteLine(e.ToString()); Console.ResetColor(); - - context.ExitCode = 1; } + + return 1; }); } } diff --git a/src/coreclr/tools/ILVerify/Program.cs b/src/coreclr/tools/ILVerify/Program.cs index 634fc00c4bfb6a62d5529b0c358c357711641a3c..19ee834470ca837ecf70af11079bc0929c3b271d 100644 --- a/src/coreclr/tools/ILVerify/Program.cs +++ b/src/coreclr/tools/ILVerify/Program.cs @@ -450,16 +450,14 @@ public PEReader Resolve(string simpleName) return null; } - private T Get(Option option) => _command.Result.GetValue(option); - private T Get(Argument argument) => _command.Result.GetValue(argument); + private T Get(CliOption option) => _command.Result.GetValue(option); + private T Get(CliArgument argument) => _command.Result.GetValue(argument); private static int Main(string[] args) => - new CommandLineBuilder(new ILVerifyRootCommand()) - .UseTokenReplacer(Helpers.TryReadResponseFile) - .UseVersionOption("--version", "-v") - .UseHelp() - .UseParseErrorReporting() - .Build() - .Invoke(args); + new CliConfiguration(new ILVerifyRootCommand().UseVersion()) + { + ResponseFileTokenReplacer = Helpers.TryReadResponseFile, + EnableParseErrorReporting = true + }.Invoke(args); } } diff --git a/src/coreclr/tools/InjectResource/Program.cs b/src/coreclr/tools/InjectResource/Program.cs index f9a73fdf11b9e8ef192fb93b1772d5cf90895b93..2fcf8cf5e166b33c02f041dcda1721a9f76b5d38 100644 --- a/src/coreclr/tools/InjectResource/Program.cs +++ b/src/coreclr/tools/InjectResource/Program.cs @@ -1,29 +1,21 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. + using System.CommandLine; using System.IO; -var binOption = new Option( - name: "--bin", - description: "Binary data to attach to the image"); -var imageOption = new Option( - name: "--image", - description: "PE image to add the binary resource into"); -var nameOption = new Option( - name: "--name", - description: "Resource name"); -var rootCommand = new RootCommand("Inject native resources into a Portable Executable image"); -rootCommand.AddOption(binOption); -rootCommand.AddOption(imageOption); -rootCommand.AddOption(nameOption); +CliOption binOption = new("--bin") { Description = "Binary data to attach to the image" }; +CliOption imageOption = new("--image") { Description = "PE image to add the binary resource into" }; +CliOption nameOption = new("--name") { Description = "Resource name" }; +CliRootCommand rootCommand = new("Inject native resources into a Portable Executable image"); +rootCommand.Options.Add(binOption); +rootCommand.Options.Add(imageOption); +rootCommand.Options.Add(nameOption); -rootCommand.SetHandler((FileInfo binaryData, FileInfo peImage, string name) => - { - using ResourceUpdater updater = new(peImage); - updater.AddBinaryResource(name, File.ReadAllBytes(binaryData.FullName)); - }, - binOption, - imageOption, - nameOption); +rootCommand.SetAction(result => +{ + using ResourceUpdater updater = new(result.GetValue(imageOption)!); + updater.AddBinaryResource(result.GetValue(nameOption)!, File.ReadAllBytes(result.GetValue(binOption)!.FullName)); +}); -return rootCommand.Invoke(args); +return new CliConfiguration(rootCommand).Invoke(args); diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs index e5c9c8736d80aaf50a86d203f3a7e585645ad24a..66405cd85c8b40bb7f3e14edefeea57bd5a163b2 100644 --- a/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs +++ b/src/coreclr/tools/aot/ILCompiler/ILCompilerRootCommand.cs @@ -5,257 +5,244 @@ using System.Collections.Generic; using System.CommandLine; using System.CommandLine.Help; +using System.CommandLine.Parsing; using Internal.TypeSystem; namespace ILCompiler { - internal sealed class ILCompilerRootCommand : RootCommand + internal sealed class ILCompilerRootCommand : CliRootCommand { - public Argument> InputFilePaths { get; } = - new("input-file-path", result => Helpers.BuildPathDictionary(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; - public Option> ReferenceFiles { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionary(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 SatelliteFilePaths { get; } = - new(new[] { "--satellite" }, Array.Empty, "Satellite assemblies associated with inputs/references"); - 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 SplitExeInitialization { get; } = - new(new[] { "--splitinit" }, "Split initialization of an executable between the library entrypoint and a main entrypoint"); - 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 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 NoInlineTls { get; } = - new(new[] { "--noinlinetls" }, "Do not generate inline thread local statics"); - 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 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 RuntimeKnobs { get; } = - new(new[] { "--runtimeknob" }, Array.Empty, "Runtime knobs 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 MaxVectorTBitWidth { get; } = - new(new[] { "--max-vectort-bitwidth" }, "Maximum width, in bits, that Vector is allowed to be"); - public Option Guard { get; } = - new(new[] { "--guard" }, "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)"); - public Option Dehydrate { get; } = - new(new[] { "--dehydrate" }, "Dehydrate runtime data structures"); - 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 MaxGenericCycleDepth { get; } = - new(new[] { "--maxgenericcycle" }, () => CompilerTypeSystemContext.DefaultGenericCycleDepthCutoff, "Max depth of generic cycle"); - public Option MaxGenericCycleBreadth { get; } = - new(new[] { "--maxgenericcyclebreadth" }, () => CompilerTypeSystemContext.DefaultGenericCycleBreadthCutoff, "Max breadth of generic cycle expansion"); - public Option RootedAssemblies { get; } = - new(new[] { "--root" }, Array.Empty, "Fully generate given assembly"); - public Option ConditionallyRootedAssemblies { get; } = - new(new[] { "--conditionalroot" }, Array.Empty, "Fully generate given assembly if it's used"); - public Option TrimmedAssemblies { get; } = - new(new[] { "--trim" }, Array.Empty, "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 Option UnmanagedEntryPointsAssemblies { get; } = - new(new[] { "--generateunmanagedentrypoints" }, Array.Empty, "Generate unmanaged entrypoints for a given assembly"); + public CliArgument> InputFilePaths { get; } = + new("input-file-path") { CustomParser = result => Helpers.BuildPathDictionary(result.Tokens, true), Description = "Input file(s)", Arity = ArgumentArity.OneOrMore }; + public CliOption> ReferenceFiles { get; } = + new("--reference", "-r") { CustomParser = result => Helpers.BuildPathDictionary(result.Tokens, false), DefaultValueFactory = result => Helpers.BuildPathDictionary(result.Tokens, false), Description = "Reference file(s) for compilation" }; + public CliOption OutputFilePath { get; } = + new("--out", "-o") { Description = "Output file path" }; + public CliOption Optimize { get; } = + new("--optimize", "-O") { Description = "Enable optimizations" }; + public CliOption OptimizeSpace { get; } = + new("--optimize-space", "--Os") { Description = "Enable optimizations, favor code space" }; + public CliOption OptimizeTime { get; } = + new("--optimize-time", "--Ot") { Description = "Enable optimizations, favor code speed" }; + public CliOption MibcFilePaths { get; } = + new("--mibc", "-m") { DefaultValueFactory = _ => Array.Empty(), Description = "Mibc file(s) for profile guided optimization" }; + public CliOption SatelliteFilePaths { get; } = + new("--satellite") { DefaultValueFactory = _ => Array.Empty(), Description = "Satellite assemblies associated with inputs/references" }; + public CliOption EnableDebugInfo { get; } = + new("--debug", "-g") { Description = "Emit debugging information" }; + public CliOption UseDwarf5 { get; } = + new("--gdwarf-5") { Description = "Generate source-level debug information with dwarf version 5" }; + public CliOption NativeLib { get; } = + new("--nativelib") { Description = "Compile as static or shared library" }; + public CliOption SplitExeInitialization { get; } = + new("--splitinit") { Description = "Split initialization of an executable between the library entrypoint and a main entrypoint" }; + public CliOption ExportsFile { get; } = + new("--exportsfile") { Description = "File to write exported method definitions" }; + public CliOption DgmlLogFileName { get; } = + new("--dgmllog") { Description = "Save result of dependency analysis as DGML" }; + public CliOption GenerateFullDgmlLog { get; } = + new("--fulllog") { Description = "Save detailed log of dependency analysis" }; + public CliOption ScanDgmlLogFileName { get; } = + new("--scandgmllog") { Description = "Save result of scanner dependency analysis as DGML" }; + public CliOption GenerateFullScanDgmlLog { get; } = + new("--scanfulllog") { Description = "Save detailed log of scanner dependency analysis" }; + public CliOption IsVerbose { get; } = + new("--verbose") { Description = "Enable verbose logging" }; + public CliOption SystemModuleName { get; } = + new("--systemmodule") { DefaultValueFactory = _ => Helpers.DefaultSystemModule, Description = "System module name (default: System.Private.CoreLib)" }; + public CliOption MultiFile { get; } = + new("--multifile") { Description = "Compile only input files (do not compile referenced assemblies)" }; + public CliOption WaitForDebugger { get; } = + new("--waitfordebugger") { Description = "Pause to give opportunity to attach debugger" }; + public CliOption Resilient { get; } = + new("--resilient") { Description = "Ignore unresolved types, methods, and assemblies. Defaults to false" }; + public CliOption CodegenOptions { get; } = + new("--codegenopt") { DefaultValueFactory = _ => Array.Empty(), Description = "Define a codegen option" }; + public CliOption RdXmlFilePaths { get; } = + new("--rdxml") { DefaultValueFactory = _ => Array.Empty(), Description = "RD.XML file(s) for compilation" }; + public CliOption LinkTrimFilePaths { get; } = + new("--descriptor") { DefaultValueFactory = _ => Array.Empty(), Description = "ILLinkTrim.Descriptor file(s) for compilation" }; + public CliOption MapFileName { get; } = + new("--map") { Description = "Generate a map file" }; + public CliOption MstatFileName { get; } = + new("--mstat") { Description = "Generate an mstat file" }; + public CliOption MetadataLogFileName { get; } = + new("--metadatalog") { Description = "Generate a metadata log file" }; + public CliOption CompleteTypesMetadata { get; } = + new("--completetypemetadata") { Description = "Generate complete metadata for types" }; + public CliOption ReflectionData { get; } = + new("--reflectiondata") { Description = "Reflection data to generate (one of: all, none)" }; + public CliOption ScanReflection { get; } = + new("--scanreflection") { Description = "Scan IL for reflection patterns" }; + public CliOption UseScanner { get; } = + new("--scan") { Description = "Use IL scanner to generate optimized code (implied by -O)" }; + public CliOption NoScanner { get; } = + new("--noscan") { Description = "Do not use IL scanner to generate optimized code" }; + public CliOption IlDump { get; } = + new("--ildump") { Description = "Dump IL assembly listing for compiler-generated IL" }; + public CliOption NoInlineTls { get; } = + new("--noinlinetls") { Description = "Do not generate inline thread local statics" }; + public CliOption EmitStackTraceData { get; } = + new("--stacktracedata") { Description = "Emit data to support generating stack trace strings at runtime" }; + public CliOption MethodBodyFolding { get; } = + new("--methodbodyfolding") { Description = "Fold identical method bodies" }; + public CliOption InitAssemblies { get; } = + new("--initassembly") { DefaultValueFactory = _ => Array.Empty(), Description = "Assembly(ies) with a library initializer" }; + public CliOption FeatureSwitches { get; } = + new("--feature") { DefaultValueFactory = _ => Array.Empty(), Description = "Feature switches to apply (format: 'Namespace.Name=[true|false]'" }; + public CliOption RuntimeOptions { get; } = + new("--runtimeopt") { DefaultValueFactory = _ => Array.Empty(), Description = "Runtime options to set" }; + public CliOption RuntimeKnobs { get; } = + new("--runtimeknob") { DefaultValueFactory = _ => Array.Empty(), Description = "Runtime knobs to set" }; + public CliOption Parallelism { get; } = + new("--parallelism") { CustomParser = MakeParallelism, DefaultValueFactory = MakeParallelism, Description = "Maximum number of threads to use during compilation" }; + public CliOption InstructionSet { get; } = + new("--instruction-set") { Description = "Instruction set to allow or disallow" }; + public CliOption MaxVectorTBitWidth { get; } = + new("--max-vectort-bitwidth") { Description = "Maximum width, in bits, that Vector is allowed to be" }; + public CliOption Guard { get; } = + new("--guard") { Description = "Enable mitigations. Options: 'cf': CFG (Control Flow Guard, Windows only)" }; + public CliOption Dehydrate { get; } = + new("--dehydrate") { Description = "Dehydrate runtime data structures" }; + public CliOption PreinitStatics { get; } = + new("--preinitstatics") { Description = "Interpret static constructors at compile time if possible (implied by -O)" }; + public CliOption NoPreinitStatics { get; } = + new("--nopreinitstatics") { Description = "Do not interpret static constructors at compile time" }; + public CliOption SuppressedWarnings { get; } = + new("--nowarn") { DefaultValueFactory = _ => Array.Empty(), Description = "Disable specific warning messages" }; + public CliOption SingleWarn { get; } = + new("--singlewarn") { Description = "Generate single AOT/trimming warning per assembly" }; + public CliOption NoTrimWarn { get; } = + new("--notrimwarn") { Description = "Disable warnings related to trimming" }; + public CliOption NoAotWarn { get; } = + new("--noaotwarn") { Description = "Disable warnings related to AOT" }; + public CliOption SingleWarnEnabledAssemblies { get; } = + new("--singlewarnassembly") { DefaultValueFactory = _ => Array.Empty(), Description = "Generate single AOT/trimming warning for given assembly" }; + public CliOption SingleWarnDisabledAssemblies { get; } = + new("--nosinglewarnassembly") { DefaultValueFactory = _ => Array.Empty(), Description = "Expand AOT/trimming warnings for given assembly" }; + public CliOption DirectPInvokes { get; } = + new("--directpinvoke") { DefaultValueFactory = _ => Array.Empty(), Description = "PInvoke to call directly" }; + public CliOption DirectPInvokeLists { get; } = + new("--directpinvokelist") { DefaultValueFactory = _ => Array.Empty(), Description = "File with list of PInvokes to call directly" }; + public CliOption RootedAssemblies { get; } = + new("--root") { DefaultValueFactory = _ => Array.Empty(), Description = "Fully generate given assembly" }; + public CliOption ConditionallyRootedAssemblies { get; } = + new("--conditionalroot") { DefaultValueFactory = _ => Array.Empty(), Description = "Fully generate given assembly if it's used" }; + public CliOption TrimmedAssemblies { get; } = + new("--trim") { DefaultValueFactory = _ => Array.Empty(), Description = "Trim the specified assembly" }; + public CliOption RootDefaultAssemblies { get; } = + new("--defaultrooting") { Description = "Root assemblies that are not marked [IsTrimmable]" }; + public CliOption TargetArchitecture { get; } = + new("--targetarch") { CustomParser = result => Helpers.GetTargetArchitecture(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), DefaultValueFactory = result => Helpers.GetTargetArchitecture(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), Description = "Target architecture for cross compilation" }; + public CliOption TargetOS { get; } = + new("--targetos") { CustomParser = result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), DefaultValueFactory = result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), Description = "Target OS for cross compilation" }; + public CliOption JitPath { get; } = + new("--jitpath") { Description = "Path to JIT compiler library" }; + public CliOption SingleMethodTypeName { get; } = + new("--singlemethodtypename") { Description = "Single method compilation: assembly-qualified name of the owning type" }; + public CliOption SingleMethodName { get; } = + new("--singlemethodname") { Description = "Single method compilation: name of the method" }; + public CliOption MaxGenericCycleDepth { get; } = + new("--maxgenericcycle") { DefaultValueFactory = _ => CompilerTypeSystemContext.DefaultGenericCycleDepthCutoff, Description = "Max depth of generic cycle" }; + public CliOption MaxGenericCycleBreadth { get; } = + new("--maxgenericcyclebreadth") { DefaultValueFactory = _ => CompilerTypeSystemContext.DefaultGenericCycleBreadthCutoff, Description = "Max breadth of generic cycle expansion" }; + public CliOption SingleMethodGenericArgs { get; } = + new("--singlemethodgenericarg") { Description = "Single method compilation: generic arguments to the method" }; + public CliOption MakeReproPath { get; } = + new("--make-repro-path") { Description = "Path where to place a repro package" }; + public CliOption UnmanagedEntryPointsAssemblies { get; } = + new("--generateunmanagedentrypoints") { DefaultValueFactory = _ => Array.Empty(), Description = "Generate unmanaged entrypoints for a given assembly" }; 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(SatelliteFilePaths); - AddOption(EnableDebugInfo); - AddOption(UseDwarf5); - AddOption(NativeLib); - AddOption(SplitExeInitialization); - 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(CompleteTypesMetadata); - AddOption(ReflectionData); - AddOption(ScanReflection); - AddOption(UseScanner); - AddOption(NoScanner); - AddOption(NoInlineTls); - AddOption(IlDump); - AddOption(EmitStackTraceData); - AddOption(MethodBodyFolding); - AddOption(InitAssemblies); - AddOption(FeatureSwitches); - AddOption(RuntimeOptions); - AddOption(RuntimeKnobs); - AddOption(Parallelism); - AddOption(InstructionSet); - AddOption(MaxVectorTBitWidth); - AddOption(Guard); - AddOption(Dehydrate); - AddOption(PreinitStatics); - AddOption(NoPreinitStatics); - AddOption(SuppressedWarnings); - AddOption(SingleWarn); - AddOption(NoTrimWarn); - AddOption(NoAotWarn); - AddOption(SingleWarnEnabledAssemblies); - AddOption(SingleWarnDisabledAssemblies); - AddOption(DirectPInvokes); - AddOption(DirectPInvokeLists); - AddOption(MaxGenericCycleDepth); - AddOption(MaxGenericCycleBreadth); - AddOption(RootedAssemblies); - AddOption(ConditionallyRootedAssemblies); - AddOption(TrimmedAssemblies); - AddOption(RootDefaultAssemblies); - AddOption(TargetArchitecture); - AddOption(TargetOS); - AddOption(JitPath); - AddOption(SingleMethodTypeName); - AddOption(SingleMethodName); - AddOption(SingleMethodGenericArgs); - AddOption(MakeReproPath); - AddOption(UnmanagedEntryPointsAssemblies); - - this.SetHandler(context => + Arguments.Add(InputFilePaths); + Options.Add(ReferenceFiles); + Options.Add(OutputFilePath); + Options.Add(Optimize); + Options.Add(OptimizeSpace); + Options.Add(OptimizeTime); + Options.Add(MibcFilePaths); + Options.Add(SatelliteFilePaths); + Options.Add(EnableDebugInfo); + Options.Add(UseDwarf5); + Options.Add(NativeLib); + Options.Add(SplitExeInitialization); + Options.Add(ExportsFile); + Options.Add(DgmlLogFileName); + Options.Add(GenerateFullDgmlLog); + Options.Add(ScanDgmlLogFileName); + Options.Add(GenerateFullScanDgmlLog); + Options.Add(IsVerbose); + Options.Add(SystemModuleName); + Options.Add(MultiFile); + Options.Add(WaitForDebugger); + Options.Add(Resilient); + Options.Add(CodegenOptions); + Options.Add(RdXmlFilePaths); + Options.Add(LinkTrimFilePaths); + Options.Add(MapFileName); + Options.Add(MstatFileName); + Options.Add(MetadataLogFileName); + Options.Add(CompleteTypesMetadata); + Options.Add(ReflectionData); + Options.Add(ScanReflection); + Options.Add(UseScanner); + Options.Add(NoScanner); + Options.Add(NoInlineTls); + Options.Add(IlDump); + Options.Add(EmitStackTraceData); + Options.Add(MethodBodyFolding); + Options.Add(InitAssemblies); + Options.Add(FeatureSwitches); + Options.Add(RuntimeOptions); + Options.Add(RuntimeKnobs); + Options.Add(Parallelism); + Options.Add(InstructionSet); + Options.Add(MaxVectorTBitWidth); + Options.Add(Guard); + Options.Add(Dehydrate); + Options.Add(PreinitStatics); + Options.Add(NoPreinitStatics); + Options.Add(SuppressedWarnings); + Options.Add(SingleWarn); + Options.Add(NoTrimWarn); + Options.Add(NoAotWarn); + Options.Add(SingleWarnEnabledAssemblies); + Options.Add(SingleWarnDisabledAssemblies); + Options.Add(DirectPInvokes); + Options.Add(DirectPInvokeLists); + Options.Add(MaxGenericCycleDepth); + Options.Add(MaxGenericCycleBreadth); + Options.Add(RootedAssemblies); + Options.Add(ConditionallyRootedAssemblies); + Options.Add(TrimmedAssemblies); + Options.Add(RootDefaultAssemblies); + Options.Add(TargetArchitecture); + Options.Add(TargetOS); + Options.Add(JitPath); + Options.Add(SingleMethodTypeName); + Options.Add(SingleMethodName); + Options.Add(SingleMethodGenericArgs); + Options.Add(MakeReproPath); + Options.Add(UnmanagedEntryPointsAssemblies); + + this.SetAction(result => { - Result = context.ParseResult; + Result = result; - if (context.ParseResult.GetValue(OptimizeSpace)) + if (result.GetValue(OptimizeSpace)) { OptimizationMode = OptimizationMode.PreferSize; } - else if (context.ParseResult.GetValue(OptimizeTime)) + else if (result.GetValue(OptimizeTime)) { OptimizationMode = OptimizationMode.PreferSpeed; } - else if (context.ParseResult.GetValue(Optimize)) + else if (result.GetValue(Optimize)) { OptimizationMode = OptimizationMode.Blended; } @@ -266,7 +253,7 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler") try { - string makeReproPath = context.ParseResult.GetValue(MakeReproPath); + string makeReproPath = result.GetValue(MakeReproPath); if (makeReproPath != null) { // Create a repro package in the specified path @@ -275,13 +262,13 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler") // + a rsp file that should work to directly run out of the zip file #pragma warning disable CA1861 // Avoid constant arrays as arguments. Only executed once during the execution of the program. - Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValue(OutputFilePath), args, context.ParseResult, + Helpers.MakeReproPackage(makeReproPath, result.GetValue(OutputFilePath), args, result, inputOptions : new[] { "r", "reference", "m", "mibc", "rdxml", "directpinvokelist", "descriptor" }, outputOptions : new[] { "o", "out", "exportsfile" }); #pragma warning restore CA1861 // Avoid constant arrays as arguments } - context.ExitCode = new Program(this).Run(); + return new Program(this).Run(); } #if DEBUG catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) @@ -298,9 +285,9 @@ public ILCompilerRootCommand(string[] args) : base(".NET Native IL Compiler") Console.Error.WriteLine(e.ToString()); Console.ResetColor(); - - context.ExitCode = 1; } + + return 1; #endif }); } @@ -363,6 +350,22 @@ public static IEnumerable> GetExtendedHelp(HelpContext _) }; } + private static int MakeParallelism(ArgumentResult 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; + } + #if DEBUG private static bool DumpReproArguments(CodeGenerationFailedException ex) { diff --git a/src/coreclr/tools/aot/ILCompiler/Program.cs b/src/coreclr/tools/aot/ILCompiler/Program.cs index de835762f27ec196a4d319f3e598e6eaaccd1f36..db1160afc40228cac3badfe470049ea456be0aa6 100644 --- a/src/coreclr/tools/aot/ILCompiler/Program.cs +++ b/src/coreclr/tools/aot/ILCompiler/Program.cs @@ -698,15 +698,15 @@ private static IEnumerable ProcessWarningCodes(IEnumerable warningC } } - private T Get(Option option) => _command.Result.GetValue(option); + private T Get(CliOption option) => _command.Result.GetValue(option); private static int Main(string[] args) => - new CommandLineBuilder(new ILCompilerRootCommand(args)) - .UseTokenReplacer(Helpers.TryReadResponseFile) - .UseVersionOption("--version", "-v") - .UseHelp(context => context.HelpBuilder.CustomizeLayout(ILCompilerRootCommand.GetExtendedHelp)) - .UseParseErrorReporting() - .Build() - .Invoke(args); + new CliConfiguration(new ILCompilerRootCommand(args) + .UseVersion() + .UseExtendedHelp(ILCompilerRootCommand.GetExtendedHelp)) + { + ResponseFileTokenReplacer = Helpers.TryReadResponseFile, + EnableParseErrorReporting = true + }.Invoke(args); } } diff --git a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs index fba6d4276fef953f8ce09a9465b81bca3f326be4..66d407abde48267afe66291f8abdf1350ae24f18 100644 --- a/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs +++ b/src/coreclr/tools/aot/crossgen2/Crossgen2RootCommand.cs @@ -13,186 +13,135 @@ namespace ILCompiler { - internal class Crossgen2RootCommand : RootCommand + internal class Crossgen2RootCommand : CliRootCommand { - public Argument> InputFilePaths { get; } = - new("input-file-path", result => Helpers.BuildPathDictionary(result.Tokens, true), false, "Input file(s)") { Arity = ArgumentArity.OneOrMore }; - public Option> UnrootedInputFilePaths { get; } = - new(new[] { "--unrooted-input-file-paths", "-u" }, result => Helpers.BuildPathDictionary(result.Tokens, true), true, SR.UnrootedInputFilesToCompile); - public Option> ReferenceFilePaths { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathDictionary(result.Tokens, false), true, SR.ReferenceFiles); - public Option InstructionSet { get; } = - new(new[] { "--instruction-set" }, SR.InstructionSets); - public Option MaxVectorTBitWidth { get; } = - new(new[] { "--max-vectort-bitwidth" }, SR.MaxVectorTBitWidths); - 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 TypeValidation { get; } = - new(new[] { "--type-validation"}, () => TypeValidationRule.Automatic, SR.TypeValidation); - public Option InputBubble { get; } = - new(new[] { "--inputbubble" }, SR.InputBubbleOption); - public Option> InputBubbleReferenceFilePaths { get; } = - new(new[] { "--inputbubbleref" }, result => Helpers.BuildPathDictionary(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 EnableGenericCycleDetection { get; } = - new(new[] { "--enable-generic-cycle-detection" }, SR.EnableGenericCycleDetection); - public Option GenericCycleDepthCutoff { get; } = - new(new[] { "--maxgenericcycle" }, () => ReadyToRunCompilerContext.DefaultGenericCycleDepthCutoff, SR.GenericCycleDepthCutoff); - public Option GenericCycleBreadthCutoff { get; } = - new(new[] { "--maxgenericcyclebreadth" }, () => ReadyToRunCompilerContext.DefaultGenericCycleBreadthCutoff, SR.GenericCycleBreadthCutoff); - 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; + public CliArgument> InputFilePaths { get; } = + new("input-file-path") { CustomParser = result => Helpers.BuildPathDictionary(result.Tokens, true), Description = "Input file(s)", Arity = ArgumentArity.OneOrMore }; + public CliOption> UnrootedInputFilePaths { get; } = + new("--unrooted-input-file-paths", "-u") { CustomParser = result => Helpers.BuildPathDictionary(result.Tokens, true), DefaultValueFactory = result => Helpers.BuildPathDictionary(result.Tokens, true), Description = SR.UnrootedInputFilesToCompile }; + public CliOption> ReferenceFilePaths { get; } = + new("--reference", "-r") { CustomParser = result => Helpers.BuildPathDictionary(result.Tokens, false), DefaultValueFactory = result => Helpers.BuildPathDictionary(result.Tokens, false), Description = SR.ReferenceFiles }; + public CliOption InstructionSet { get; } = + new("--instruction-set") { Description = SR.InstructionSets }; + public CliOption MaxVectorTBitWidth { get; } = + new("--max-vectort-bitwidth") { Description = SR.MaxVectorTBitWidths }; + public CliOption MibcFilePaths { get; } = + new("--mibc", "-m") { DefaultValueFactory = _ => Array.Empty(), Description = SR.MibcFiles }; + public CliOption OutputFilePath { get; } = + new("--out", "-o") { Description = SR.OutputFilePath }; + public CliOption CompositeRootPath { get; } = + new("--compositerootpath", "--crp") { Description = SR.CompositeRootPath }; + public CliOption Optimize { get; } = + new("--optimize", "-O") { Description = SR.EnableOptimizationsOption }; + public CliOption OptimizeDisabled { get; } = + new("--optimize-disabled", "--Od") { Description = SR.DisableOptimizationsOption }; + public CliOption OptimizeSpace { get; } = + new("--optimize-space", "--Os") { Description = SR.OptimizeSpaceOption }; + public CliOption OptimizeTime { get; } = + new("--optimize-time", "--Ot") { Description = SR.OptimizeSpeedOption }; + public CliOption TypeValidation { get; } = + new("--type-validation") { DefaultValueFactory = _ => TypeValidationRule.Automatic, Description = SR.TypeValidation }; + public CliOption InputBubble { get; } = + new("--inputbubble") { Description = SR.InputBubbleOption }; + public CliOption> InputBubbleReferenceFilePaths { get; } = + new("--inputbubbleref") { CustomParser = result => Helpers.BuildPathDictionary(result.Tokens, false), DefaultValueFactory = result => Helpers.BuildPathDictionary(result.Tokens, false), Description = SR.InputBubbleReferenceFiles }; + public CliOption Composite { get; } = + new("--composite") { Description = SR.CompositeBuildMode }; + public CliOption CompositeKeyFile { get; } = + new("--compositekeyfile") { Description = SR.CompositeKeyFile }; + public CliOption CompileNoMethods { get; } = + new("--compile-no-methods") { Description = SR.CompileNoMethodsOption }; + public CliOption OutNearInput { get; } = + new("--out-near-input") { Description = SR.OutNearInputOption }; + public CliOption SingleFileCompilation { get; } = + new("--single-file-compilation") { Description = SR.SingleFileCompilationOption }; + public CliOption Partial { get; } = + new("--partial") { Description = SR.PartialImageOption }; + public CliOption CompileBubbleGenerics { get; } = + new("--compilebubblegenerics") { Description = SR.BubbleGenericsOption }; + public CliOption EmbedPgoData { get; } = + new("--embed-pgo-data") { Description = SR.EmbedPgoDataOption }; + public CliOption DgmlLogFileName { get; } = + new("--dgmllog") { Description = SR.SaveDependencyLogOption }; + public CliOption GenerateFullDgmlLog { get; } = + new("--fulllog") { Description = SR.SaveDetailedLogOption }; + public CliOption IsVerbose { get; } = + new("--verbose") { Description = SR.VerboseLoggingOption }; + public CliOption SystemModuleName { get; } = + new("--systemmodule") { DefaultValueFactory = _ => Helpers.DefaultSystemModule, Description = SR.SystemModuleOverrideOption }; + public CliOption WaitForDebugger { get; } = + new("--waitfordebugger") { Description = SR.WaitForDebuggerOption }; + public CliOption CodegenOptions { get; } = + new("--codegenopt") { DefaultValueFactory = _ => Array.Empty(), Description = SR.CodeGenOptions }; + public CliOption SupportIbc { get; } = + new("--support-ibc") { Description = SR.SupportIbc }; + public CliOption Resilient { get; } = + new("--resilient") { Description = SR.ResilientOption }; + public CliOption ImageBase { get; } = + new("--imagebase") { Description = SR.ImageBase }; + public CliOption TargetArchitecture { get; } = + new("--targetarch") { CustomParser = MakeTargetArchitecture, DefaultValueFactory = MakeTargetArchitecture, Description = SR.TargetArchOption, Arity = ArgumentArity.OneOrMore }; + public CliOption EnableGenericCycleDetection { get; } = + new("--enable-generic-cycle-detection") { Description = SR.EnableGenericCycleDetection }; + public CliOption GenericCycleDepthCutoff { get; } = + new("--maxgenericcycle") { DefaultValueFactory = _ => ReadyToRunCompilerContext.DefaultGenericCycleDepthCutoff, Description = SR.GenericCycleDepthCutoff }; + public CliOption GenericCycleBreadthCutoff { get; } = + new("--maxgenericcyclebreadth") { DefaultValueFactory = _ => ReadyToRunCompilerContext.DefaultGenericCycleBreadthCutoff, Description = SR.GenericCycleBreadthCutoff }; + public CliOption TargetOS { get; } = + new("--targetos") { CustomParser = result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), DefaultValueFactory = result => Helpers.GetTargetOS(result.Tokens.Count > 0 ? result.Tokens[0].Value : null), Description = SR.TargetOSOption }; + public CliOption JitPath { get; } = + new("--jitpath") { Description = SR.JitPathOption }; + public CliOption PrintReproInstructions { get; } = + new("--print-repro-instructions") { Description = SR.PrintReproInstructionsOption }; + public CliOption SingleMethodTypeName { get; } = + new("--singlemethodtypename") { Description = SR.SingleMethodTypeName }; + public CliOption SingleMethodName { get; } = + new("--singlemethodname") { Description = SR.SingleMethodMethodName }; + public CliOption SingleMethodIndex { get; } = + new("--singlemethodindex") { Description = SR.SingleMethodIndex }; + public CliOption SingleMethodGenericArgs { get; } = + new("--singlemethodgenericarg") { Description = SR.SingleMethodGenericArgs }; + public CliOption Parallelism { get; } = + new("--parallelism") { CustomParser = MakeParallelism, DefaultValueFactory = MakeParallelism, Description = SR.ParalellismOption }; + public CliOption CustomPESectionAlignment { get; } = + new("--custom-pe-section-alignment") { Description = SR.CustomPESectionAlignmentOption }; + public CliOption Map { get; } = + new("--map") { Description = SR.MapFileOption }; + public CliOption MapCsv { get; } = + new("--mapcsv") { Description = SR.MapCsvFileOption }; + public CliOption Pdb { get; } = + new("--pdb") { Description = SR.PdbFileOption }; + public CliOption PdbPath { get; } = + new("--pdb-path") { Description = SR.PdbFilePathOption }; + public CliOption PerfMap { get; } = + new("--perfmap") { Description = SR.PerfMapFileOption }; + public CliOption PerfMapPath { get; } = + new("--perfmap-path") { Description = SR.PerfMapFilePathOption }; + public CliOption PerfMapFormatVersion { get; } = + new("--perfmap-format-version") { DefaultValueFactory = _ => 0, Description = SR.PerfMapFormatVersionOption }; + public CliOption CrossModuleInlining { get; } = + new("--opt-cross-module") { Description = SR.CrossModuleInlining }; + public CliOption AsyncMethodOptimization { get; } = + new("--opt-async-methods") { Description = SR.AsyncModuleOptimization }; + public CliOption NonLocalGenericsModule { get; } = + new("--non-local-generics-module") { DefaultValueFactory = _ => string.Empty, Description = SR.NonLocalGenericsModule }; + public CliOption MethodLayout { get; } = + new("--method-layout") { CustomParser = MakeReadyToRunMethodLayoutAlgorithm, DefaultValueFactory = MakeReadyToRunMethodLayoutAlgorithm, Description = SR.MethodLayoutOption }; + public CliOption FileLayout { get; } = + new("--file-layout") { CustomParser = MakeReadyToRunFileLayoutAlgorithm, DefaultValueFactory = MakeReadyToRunFileLayoutAlgorithm, Description = SR.FileLayoutOption }; + public CliOption VerifyTypeAndFieldLayout { get; } = + new("--verify-type-and-field-layout") { Description = SR.VerifyTypeAndFieldLayoutOption }; + public CliOption CallChainProfileFile { get; } = + new("--callchain-profile") { Description = SR.CallChainProfileFile }; + public CliOption MakeReproPath { get; } = + new("--make-repro-path") { Description = "Path where to place a repro package" }; + public CliOption HotColdSplitting { get; } = + new("--hot-cold-splitting") { Description = SR.HotColdSplittingOption }; + public CliOption SynthesizeRandomMibc { get; } = + new("--synthesize-random-mibc"); - 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 Option HotColdSplitting { get; } = - new(new[] { "--hot-cold-splitting" }, SR.HotColdSplittingOption); - public Option SynthesizeRandomMibc { get; } = - new(new[] { "--synthesize-random-mibc" }); - - public Option DeterminismStress { get; } = - new(new[] { "--determinism-stress" }); + public CliOption DeterminismStress { get; } = + new("--determinism-stress"); public bool CompositeOrInputBubble { get; private set; } public OptimizationMode OptimizationMode { get; private set; } @@ -202,83 +151,83 @@ internal class Crossgen2RootCommand : RootCommand public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) { - AddArgument(InputFilePaths); - AddOption(UnrootedInputFilePaths); - AddOption(ReferenceFilePaths); - AddOption(InstructionSet); - AddOption(MaxVectorTBitWidth); - AddOption(MibcFilePaths); - AddOption(OutputFilePath); - AddOption(CompositeRootPath); - AddOption(Optimize); - AddOption(OptimizeDisabled); - AddOption(OptimizeSpace); - AddOption(OptimizeTime); - AddOption(TypeValidation); - 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(EnableGenericCycleDetection); - AddOption(GenericCycleDepthCutoff); - AddOption(GenericCycleBreadthCutoff); - 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); - AddOption(HotColdSplitting); - AddOption(SynthesizeRandomMibc); - AddOption(DeterminismStress); - - this.SetHandler(context => + Arguments.Add(InputFilePaths); + Options.Add(UnrootedInputFilePaths); + Options.Add(ReferenceFilePaths); + Options.Add(InstructionSet); + Options.Add(MaxVectorTBitWidth); + Options.Add(MibcFilePaths); + Options.Add(OutputFilePath); + Options.Add(CompositeRootPath); + Options.Add(Optimize); + Options.Add(OptimizeDisabled); + Options.Add(OptimizeSpace); + Options.Add(OptimizeTime); + Options.Add(TypeValidation); + Options.Add(InputBubble); + Options.Add(InputBubbleReferenceFilePaths); + Options.Add(Composite); + Options.Add(CompositeKeyFile); + Options.Add(CompileNoMethods); + Options.Add(OutNearInput); + Options.Add(SingleFileCompilation); + Options.Add(Partial); + Options.Add(CompileBubbleGenerics); + Options.Add(EmbedPgoData); + Options.Add(DgmlLogFileName); + Options.Add(GenerateFullDgmlLog); + Options.Add(IsVerbose); + Options.Add(SystemModuleName); + Options.Add(WaitForDebugger); + Options.Add(CodegenOptions); + Options.Add(SupportIbc); + Options.Add(Resilient); + Options.Add(ImageBase); + Options.Add(EnableGenericCycleDetection); + Options.Add(GenericCycleDepthCutoff); + Options.Add(GenericCycleBreadthCutoff); + Options.Add(TargetArchitecture); + Options.Add(TargetOS); + Options.Add(JitPath); + Options.Add(PrintReproInstructions); + Options.Add(SingleMethodTypeName); + Options.Add(SingleMethodName); + Options.Add(SingleMethodIndex); + Options.Add(SingleMethodGenericArgs); + Options.Add(Parallelism); + Options.Add(CustomPESectionAlignment); + Options.Add(Map); + Options.Add(MapCsv); + Options.Add(Pdb); + Options.Add(PdbPath); + Options.Add(PerfMap); + Options.Add(PerfMapPath); + Options.Add(PerfMapFormatVersion); + Options.Add(CrossModuleInlining); + Options.Add(AsyncMethodOptimization); + Options.Add(NonLocalGenericsModule); + Options.Add(MethodLayout); + Options.Add(FileLayout); + Options.Add(VerifyTypeAndFieldLayout); + Options.Add(CallChainProfileFile); + Options.Add(MakeReproPath); + Options.Add(HotColdSplitting); + Options.Add(SynthesizeRandomMibc); + Options.Add(DeterminismStress); + + this.SetAction(result => { - Result = context.ParseResult; - CompositeOrInputBubble = context.ParseResult.GetValue(Composite) | context.ParseResult.GetValue(InputBubble); - if (context.ParseResult.GetValue(OptimizeSpace)) + Result = result; + CompositeOrInputBubble = result.GetValue(Composite) | result.GetValue(InputBubble); + if (result.GetValue(OptimizeSpace)) { OptimizationMode = OptimizationMode.PreferSize; } - else if (context.ParseResult.GetValue(OptimizeTime)) + else if (result.GetValue(OptimizeTime)) { OptimizationMode = OptimizationMode.PreferSpeed; } - else if (context.ParseResult.GetValue(Optimize)) + else if (result.GetValue(Optimize)) { OptimizationMode = OptimizationMode.Blended; } @@ -289,7 +238,7 @@ public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) try { - int alignment = context.ParseResult.GetValue(CustomPESectionAlignment); + int alignment = result.GetValue(CustomPESectionAlignment); if (alignment != 0) { // Must be a power of two and >= 4096 @@ -297,7 +246,7 @@ public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) throw new CommandLineException(SR.InvalidCustomPESectionAlignment); } - string makeReproPath = context.ParseResult.GetValue(MakeReproPath); + string makeReproPath = result.GetValue(MakeReproPath); if (makeReproPath != null) { // Create a repro package in the specified path @@ -305,11 +254,11 @@ public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) // + the original command line arguments // + a rsp file that should work to directly run out of the zip file - Helpers.MakeReproPackage(makeReproPath, context.ParseResult.GetValue(OutputFilePath), args, - context.ParseResult, new[] { "r", "reference", "u", "unrooted-input-file-paths", "m", "mibc", "inputbubbleref" }); + Helpers.MakeReproPackage(makeReproPath, result.GetValue(OutputFilePath), args, + result, new[] { "r", "reference", "u", "unrooted-input-file-paths", "m", "mibc", "inputbubbleref" }); } - context.ExitCode = new Program(this).Run(); + return new Program(this).Run(); } #if DEBUG catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex)) @@ -326,9 +275,9 @@ public Crossgen2RootCommand(string[] args) : base(SR.Crossgen2BannerText) Console.Error.WriteLine(e.ToString()); Console.ResetColor(); - - context.ExitCode = 1; } + + return 1; #endif }); } @@ -387,6 +336,65 @@ public static IEnumerable> GetExtendedHelp(HelpContext _) }; } + private static TargetArchitecture MakeTargetArchitecture(ArgumentResult 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); + } + + private static int MakeParallelism(ArgumentResult 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; + } + + private static ReadyToRunMethodLayoutAlgorithm MakeReadyToRunMethodLayoutAlgorithm(ArgumentResult 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) + }; + } + + private static ReadyToRunFileLayoutAlgorithm MakeReadyToRunFileLayoutAlgorithm(ArgumentResult 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) + }; + } + #if DEBUG private static bool DumpReproArguments(CodeGenerationFailedException ex) { diff --git a/src/coreclr/tools/aot/crossgen2/Program.cs b/src/coreclr/tools/aot/crossgen2/Program.cs index ba63968f5925fcec769f69fdef1953cd408839aa..f8e7239292ec3a38eeb62fd2cd93e97ea7c6019f 100644 --- a/src/coreclr/tools/aot/crossgen2/Program.cs +++ b/src/coreclr/tools/aot/crossgen2/Program.cs @@ -908,15 +908,15 @@ internal static bool IsValidPublicKey(byte[] blob) return true; } - private T Get(Option option) => _command.Result.GetValue(option); + private T Get(CliOption option) => _command.Result.GetValue(option); private static int Main(string[] args) => - new CommandLineBuilder(new Crossgen2RootCommand(args)) - .UseTokenReplacer(Helpers.TryReadResponseFile) - .UseVersionOption("--version", "-v") - .UseHelp(context => context.HelpBuilder.CustomizeLayout(Crossgen2RootCommand.GetExtendedHelp)) - .UseParseErrorReporting() - .Build() - .Invoke(args); + new CliConfiguration(new Crossgen2RootCommand(args) + .UseVersion() + .UseExtendedHelp(Crossgen2RootCommand.GetExtendedHelp)) + { + ResponseFileTokenReplacer = Helpers.TryReadResponseFile, + EnableParseErrorReporting = true + }.Invoke(args); } } diff --git a/src/coreclr/tools/dotnet-pgo/PgoRootCommand.cs b/src/coreclr/tools/dotnet-pgo/PgoRootCommand.cs index d3a40bfd46b58cb0c1341446724b19c420f7cca1..06f63db826e4e809943e563da2da64f105b2bfaa 100644 --- a/src/coreclr/tools/dotnet-pgo/PgoRootCommand.cs +++ b/src/coreclr/tools/dotnet-pgo/PgoRootCommand.cs @@ -13,76 +13,57 @@ namespace Microsoft.Diagnostics.Tools.Pgo { - internal sealed class PgoRootCommand : RootCommand + internal sealed class PgoRootCommand : CliRootCommand { - public Option> InputFilesToMerge { get; } = - new(new[] { "--input", "-i" }, result => Helpers.BuildPathList(result.Tokens), false, "Input .mibc files to be merged. Multiple input arguments are specified as --input file1.mibc --input file2.mibc") { IsRequired = true, Arity = ArgumentArity.OneOrMore }; - public Option InputFilesToCompare { get; } = - new(new[] { "--input", "-i" }, "The input .mibc files to be compared. Specify as --input file1.mibc --input file2.mibc") { IsRequired = true, Arity = new ArgumentArity(2, 2) /* exactly two */ }; - public Option InputFileToDump { get; } = - new(new[] { "--input", "-i" }, "Name of the input mibc file to dump") { IsRequired = true, Arity = ArgumentArity.ExactlyOne }; - public Option TraceFilePath { get; } = - new(new[] { "--trace", "-t" }, "Specify the trace file to be parsed"); - public Option OutputFilePath { get; } = - new(new[] { "--output", "-o" }, "Specify the output filename to be created"); - public Option PreciseDebugInfoFile { get; } = - new(new[] { "--precise-debug-info-file" }, "Name of file of newline separated JSON objects containing precise debug info"); - public Option Pid { get; } = - new(new[] { "--pid" }, "The pid within the trace of the process to examine. If this is a multi-process trace, at least one of --pid or --process-name must be specified"); - public Option ProcessName { get; } = - new(new[] { "--process-name" }, "The process name within the trace of the process to examine. If this is a multi-process trace, at least one of --pid or --process-name must be specified"); - public Option> Reference = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathList(result.Tokens), true, "If a reference is not located on disk at the same location as used in the process, it may be specified with a --reference parameter. Multiple --reference parameters may be specified. The wild cards * and ? are supported by this option"); - public Option ClrInstanceId { get; } = - new("--clr-instance-id", "If the process contains multiple .NET runtimes, the instance ID must be specified"); - public Option Spgo { get; } = - new("--spgo", "Base profile on samples in the input. Uses last branch records if available and otherwise raw IP samples"); - public Option SpgoMinSamples { get; } = - new("--spgo-min-samples", () => 50, "The minimum number of total samples a function must have before generating profile data for it with SPGO. Default: 50"); - public Option IncludeFullGraphs { get; } = - new("--include-full-graphs", "Include all blocks and edges in the written .mibc file, regardless of profile counts"); - public Option ExcludeEventsBefore { get; } = - new("--exclude-events-before", () => Double.MinValue, "Exclude data from events before specified time. Time is specified as milliseconds from the start of the trace"); - public Option ExcludeEventsAfter { get; } = - new("--exclude-events-after", () => Double.MaxValue, "Exclude data from events after specified time. Time is specified as milliseconds from the start of the trace"); - public Option Compressed { get; } = - new("--compressed", () => true, "Generate compressed mibc"); - public Option DumpWorstOverlapGraphs { get; } = - new("--dump-worst-overlap-graphs", () => -1, "Number of graphs to dump to .dot format in dump-worst-overlap-graphs-to directory"); - public Option DumpWorstOverlapGraphsTo { get; } = - new("--dump-worst-overlap-graphs-to", "Number of graphs to dump to .dot format in dump-worst-overlap-graphs-to directory"); - public Option AutomaticReferences { get; } = - new("--automatic-references", () => true, "Attempt to find references by using paths embedded in the trace file. Defaults to true"); - public Option IncludedAssemblies { get; } = - new("--include-reference", result => - { - if (result.Tokens.Count > 0) - { - var includedAssemblies = new List(); - foreach (Token token in result.Tokens) - { - try - { - includedAssemblies.Add(new AssemblyName(token.Value)); - } - catch - { - throw new FormatException($"Unable to parse '{token.Value}' as an Assembly Name."); - } - } - } - - return Array.Empty(); - }, true, "If specified, include in Mibc file only references to the specified assemblies. Assemblies are specified as assembly names, not filenames. For instance, `System.Private.CoreLib` not `System.Private.CoreLib.dll`. Multiple --include-reference options may be specified."); - - private Option _includeReadyToRun { get; } = - new("--includeReadyToRun", "Include ReadyToRun methods in the trace file"); - private Option _verbosity { get; } = - new(new[] { "--verbose" }, () => Verbosity.normal, "Adjust verbosity level. Supported levels are minimal, normal, detailed, and diagnostic"); - private Option _isSorted { get; } = - new("--sorted", "Generate sorted output."); - private Option _showTimestamp { get; } = - new("--showtimestamp", "Show timestamps in output"); + public CliOption> InputFilesToMerge { get; } = + new("--input", "-i") { CustomParser = result => Helpers.BuildPathList(result.Tokens), Description = "Input .mibc files to be merged. Multiple input arguments are specified as --input file1.mibc --input file2.mibc", Required = true, Arity = ArgumentArity.OneOrMore }; + public CliOption InputFilesToCompare { get; } = + new("--input", "-i") { Description = "The input .mibc files to be compared. Specify as --input file1.mibc --input file2.mibc", Required = true, Arity = new ArgumentArity(2, 2) /* exactly two */ }; + public CliOption InputFileToDump { get; } = + new("--input", "-i") { Description = "Name of the input mibc file to dump", Required = true, Arity = ArgumentArity.ExactlyOne }; + public CliOption TraceFilePath { get; } = + new("--trace", "-t") { Description = "Specify the trace file to be parsed" }; + public CliOption OutputFilePath { get; } = + new("--output", "-o") { Description = "Specify the output filename to be created" }; + public CliOption PreciseDebugInfoFile { get; } = + new("--precise-debug-info-file") { Description = "Name of file of newline separated JSON objects containing precise debug info" }; + public CliOption Pid { get; } = + new("--pid") { Description = "The pid within the trace of the process to examine. If this is a multi-process trace, at least one of --pid or --process-name must be specified" }; + public CliOption ProcessName { get; } = + new("--process-name") { Description = "The process name within the trace of the process to examine. If this is a multi-process trace, at least one of --pid or --process-name must be specified" }; + public CliOption> Reference = + new("--reference", "-r") { CustomParser = result => Helpers.BuildPathList(result.Tokens), DefaultValueFactory = result => Helpers.BuildPathList(result.Tokens), Description = "If a reference is not located on disk at the same location as used in the process, it may be specified with a --reference parameter. Multiple --reference parameters may be specified. The wild cards * and ? are supported by this option" }; + public CliOption ClrInstanceId { get; } = + new("--clr-instance-id") { Description = "If the process contains multiple .NET runtimes, the instance ID must be specified" }; + public CliOption Spgo { get; } = + new("--spgo") { Description = "Base profile on samples in the input. Uses last branch records if available and otherwise raw IP samples" }; + public CliOption SpgoMinSamples { get; } = + new("--spgo-min-samples") { DefaultValueFactory = _ => 50, Description = "The minimum number of total samples a function must have before generating profile data for it with SPGO. Default: 50" }; + public CliOption IncludeFullGraphs { get; } = + new("--include-full-graphs") { Description = "Include all blocks and edges in the written .mibc file, regardless of profile counts" }; + public CliOption ExcludeEventsBefore { get; } = + new("--exclude-events-before") { DefaultValueFactory = _ => Double.MinValue, Description = "Exclude data from events before specified time. Time is specified as milliseconds from the start of the trace" }; + public CliOption ExcludeEventsAfter { get; } = + new("--exclude-events-after") { DefaultValueFactory = _ => Double.MaxValue, Description = "Exclude data from events after specified time. Time is specified as milliseconds from the start of the trace" }; + public CliOption Compressed { get; } = + new("--compressed") { DefaultValueFactory = _ => true, Description = "Generate compressed mibc" }; + public CliOption DumpWorstOverlapGraphs { get; } = + new("--dump-worst-overlap-graphs") { DefaultValueFactory = _ => -1, Description = "Number of graphs to dump to .dot format in dump-worst-overlap-graphs-to directory" }; + public CliOption DumpWorstOverlapGraphsTo { get; } = + new("--dump-worst-overlap-graphs-to") { Description = "Number of graphs to dump to .dot format in dump-worst-overlap-graphs-to directory" }; + public CliOption AutomaticReferences { get; } = + new("--automatic-references") { DefaultValueFactory = _ => true, Description = "Attempt to find references by using paths embedded in the trace file. Defaults to true" }; + public CliOption IncludedAssemblies { get; } = + new("--include-reference") { CustomParser = MakeAssemblyNameArray, DefaultValueFactory = MakeAssemblyNameArray, Description = "If specified, include in Mibc file only references to the specified assemblies. Assemblies are specified as assembly names, not filenames. For instance, `System.Private.CoreLib` not `System.Private.CoreLib.dll`. Multiple --include-reference options may be specified." }; + + private CliOption _includeReadyToRun { get; } = + new("--includeReadyToRun") { Description = "Include ReadyToRun methods in the trace file" }; + private CliOption _verbosity { get; } = + new("--verbose") { DefaultValueFactory = _ => Verbosity.normal, Description = "Adjust verbosity level. Supported levels are minimal, normal, detailed, and diagnostic" }; + private CliOption _isSorted { get; } = + new("--sorted") { Description = "Generate sorted output." }; + private CliOption _showTimestamp { get; } = + new("--showtimestamp") { Description = "Show timestamps in output" }; public PgoFileType? FileType; public bool ProcessJitEvents; @@ -108,7 +89,7 @@ private enum Verbosity public PgoRootCommand(string[] args) : base(".NET PGO Tool") { - Command createMbicCommand = new("create-mibc", "Transform a trace file into a Mibc profile data file") + CliCommand createMbicCommand = new("create-mibc", "Transform a trace file into a Mibc profile data file") { TraceFilePath, OutputFilePath, @@ -127,7 +108,7 @@ public PgoRootCommand(string[] args) : base(".NET PGO Tool") IncludeFullGraphs }; - createMbicCommand.SetHandler(context => + createMbicCommand.SetAction(result => { FileType = PgoFileType.mibc; GenerateCallGraph = true; @@ -139,14 +120,14 @@ public PgoRootCommand(string[] args) : base(".NET PGO Tool") ValidateOutputFile = false; #endif - TryExecuteWithContext(context, true); + return ExecuteWithContext(result, true); }); - AddCommand(createMbicCommand); + Subcommands.Add(createMbicCommand); JitTraceOptions = JitTraceOptions.none; #if DEBUG - Command createJitTraceCommand = new("create-jittrace","Transform a trace file into a jittrace runtime file") + CliCommand createJitTraceCommand = new("create-jittrace","Transform a trace file into a jittrace runtime file") { TraceFilePath, OutputFilePath, @@ -163,30 +144,30 @@ public PgoRootCommand(string[] args) : base(".NET PGO Tool") _includeReadyToRun }; - createJitTraceCommand.SetHandler(context => + createJitTraceCommand.SetAction(result => { FileType = PgoFileType.jittrace; ProcessJitEvents = true; ValidateOutputFile = false; - ProcessR2REvents = context.ParseResult.GetValue(_includeReadyToRun); + ProcessR2REvents = result.GetValue(_includeReadyToRun); - if (context.ParseResult.GetValue(_isSorted)) + if (result.GetValue(_isSorted)) { JitTraceOptions |= JitTraceOptions.sorted; } - if (context.ParseResult.GetValue(_showTimestamp)) + if (result.GetValue(_showTimestamp)) { JitTraceOptions |= JitTraceOptions.showtimestamp; } - TryExecuteWithContext(context, true); + return ExecuteWithContext(result, true); }); - AddCommand(createJitTraceCommand); + Subcommands.Add(createJitTraceCommand); #endif - Command mergeCommand = new("merge", "Merge multiple Mibc profile data files into one file") + CliCommand mergeCommand = new("merge", "Merge multiple Mibc profile data files into one file") { InputFilesToMerge, OutputFilePath, @@ -195,7 +176,7 @@ public PgoRootCommand(string[] args) : base(".NET PGO Tool") Compressed }; - mergeCommand.SetHandler(context => + mergeCommand.SetAction(result => { #if DEBUG ValidateOutputFile = true; @@ -203,44 +184,44 @@ public PgoRootCommand(string[] args) : base(".NET PGO Tool") ValidateOutputFile = false; #endif - TryExecuteWithContext(context, true); + return ExecuteWithContext(result, true); }); - AddCommand(mergeCommand); + Subcommands.Add(mergeCommand); - Command dumpCommand = new("dump", "Dump the contents of a Mibc file") + CliCommand dumpCommand = new("dump", "Dump the contents of a Mibc file") { _verbosity, InputFileToDump, OutputFilePath, }; - dumpCommand.SetHandler(context => + dumpCommand.SetAction(result => { DumpMibc = true; - TryExecuteWithContext(context, true); + return ExecuteWithContext(result, true); }); - AddCommand(dumpCommand); + Subcommands.Add(dumpCommand); - Command compareMbicCommand = new Command("compare-mibc", "Compare two .mibc files") + CliCommand compareMbicCommand = new("compare-mibc", "Compare two .mibc files") { InputFilesToCompare, DumpWorstOverlapGraphs, DumpWorstOverlapGraphsTo }; - compareMbicCommand.SetHandler(context => TryExecuteWithContext(context, false)); + compareMbicCommand.SetAction(result => ExecuteWithContext(result, false)); - AddCommand(compareMbicCommand); + Subcommands.Add(compareMbicCommand); - void TryExecuteWithContext(InvocationContext context, bool setVerbosity) + int ExecuteWithContext(ParseResult result, bool setVerbosity) { - Result = context.ParseResult; + Result = result; if (setVerbosity) { - Verbosity verbosity = context.ParseResult.GetValue(_verbosity); + Verbosity verbosity = Result.GetValue(_verbosity); BasicProgressMessages = (int)verbosity >= (int)Verbosity.normal; Warnings = (int)verbosity >= (int)Verbosity.normal; VerboseWarnings = (int)verbosity >= (int)Verbosity.detailed; @@ -250,7 +231,7 @@ void TryExecuteWithContext(InvocationContext context, bool setVerbosity) try { - context.ExitCode = new Program(this).Run(); + return new Program(this).Run(); } catch (Exception e) { @@ -261,9 +242,9 @@ void TryExecuteWithContext(InvocationContext context, bool setVerbosity) Console.Error.WriteLine(e.ToString()); Console.ResetColor(); - - context.ExitCode = 1; } + + return 1; } } @@ -290,5 +271,27 @@ public static IEnumerable> GetExtendedHelp(HelpContext conte }; } } + + private static AssemblyName[] MakeAssemblyNameArray(ArgumentResult result) + { + if (result.Tokens.Count > 0) + { + var includedAssemblies = new List(); + foreach (CliToken token in result.Tokens) + { + try + { + includedAssemblies.Add(new AssemblyName(token.Value)); + } + catch + { + throw new FormatException($"Unable to parse '{token.Value}' as an Assembly Name."); + } + } + return includedAssemblies.ToArray(); + } + + return Array.Empty(); + } } } diff --git a/src/coreclr/tools/dotnet-pgo/Program.cs b/src/coreclr/tools/dotnet-pgo/Program.cs index 52dd7de2f96eeaafd33c7334fe1bd43fca8cc2dd..c65c1d22c86a86264e03a486355f7500a3cfe610 100644 --- a/src/coreclr/tools/dotnet-pgo/Program.cs +++ b/src/coreclr/tools/dotnet-pgo/Program.cs @@ -155,18 +155,18 @@ public Program(PgoRootCommand command) _inputFilesToCompare = Get(command.InputFilesToCompare); } - private T Get(Option option) => _command.Result.GetValue(option); - private T Get(Argument argument) => _command.Result.GetValue(argument); - private bool IsSet(Option option) => _command.Result.FindResultFor(option) != null; + private T Get(CliOption option) => _command.Result.GetValue(option); + private T Get(CliArgument argument) => _command.Result.GetValue(argument); + private bool IsSet(CliOption option) => _command.Result.GetResult(option) != null; private static int Main(string[] args) => - new CommandLineBuilder(new PgoRootCommand(args)) - .UseTokenReplacer(Helpers.TryReadResponseFile) - .UseVersionOption("--version", "-v") - .UseHelp(context => context.HelpBuilder.CustomizeLayout(PgoRootCommand.GetExtendedHelp)) - .UseParseErrorReporting() - .Build() - .Invoke(args); + new CliConfiguration(new PgoRootCommand(args) + .UseVersion() + .UseExtendedHelp(PgoRootCommand.GetExtendedHelp)) + { + ResponseFileTokenReplacer = Helpers.TryReadResponseFile, + EnableParseErrorReporting = true + }.Invoke(args); public static void PrintWarning(string warning) { diff --git a/src/coreclr/tools/r2rdump/Program.cs b/src/coreclr/tools/r2rdump/Program.cs index 2e6ef121f8e70d7e356d1455505ab1ac5f4a210f..efd1942a6224c5c0e7de45a641281460cfc93cb0 100644 --- a/src/coreclr/tools/r2rdump/Program.cs +++ b/src/coreclr/tools/r2rdump/Program.cs @@ -494,15 +494,13 @@ public int Run() return 0; } - private T Get(Option option) => _command.Result.GetValue(option); + private T Get(CliOption option) => _command.Result.GetValue(option); public static int Main(string[] args) => - new CommandLineBuilder(new R2RDumpRootCommand()) - .UseTokenReplacer(Helpers.TryReadResponseFile) - .UseVersionOption("--version", "-v") - .UseHelp() - .UseParseErrorReporting() - .Build() - .Invoke(args); + new CliConfiguration(new R2RDumpRootCommand().UseVersion()) + { + ResponseFileTokenReplacer = Helpers.TryReadResponseFile, + EnableParseErrorReporting = true + }.Invoke(args); } } diff --git a/src/coreclr/tools/r2rdump/R2RDumpRootCommand.cs b/src/coreclr/tools/r2rdump/R2RDumpRootCommand.cs index 7541fa0d6b08bdc18e7ffa6a9559fb4957b5edb1..818db56a31266312c75c7b6e2008f979d4f96cc1 100644 --- a/src/coreclr/tools/r2rdump/R2RDumpRootCommand.cs +++ b/src/coreclr/tools/r2rdump/R2RDumpRootCommand.cs @@ -8,127 +8,127 @@ namespace R2RDump { - internal sealed class R2RDumpRootCommand : RootCommand + internal sealed class R2RDumpRootCommand : CliRootCommand { - public Option> In { get; } = - new(new[] { "--in", "-i" }, result => Helpers.BuildPathList(result.Tokens), true, "Input file(s) to dump. Expects them to by ReadyToRun images"); - public Option Out { get; } = - new(new[] { "--out", "-o" }, "Output file path. Dumps everything to the specified file except for help message and exception messages"); - public Option Raw { get; } = - new(new[] { "--raw" }, "Dump the raw bytes of each section or runtime function"); - public Option Header { get; } = - new(new[] { "--header" }, "Dump R2R header"); - public Option Disasm { get; } = - new(new[] { "--disasm", "-d" }, "Show disassembly of methods or runtime functions"); - public Option Naked { get; } = - new(new[] { "--naked" }, "Naked dump suppresses most compilation details like placement addresses"); - public Option HideOffsets { get; } = - new(new[] { "--hide-offsets", "--ho" }, "Hide offsets in naked disassembly"); - - public Option Query { get; } = - new(new[] { "--query", "-q" }, "Query method by exact name, signature, row ID or token"); - public Option Keyword { get; } = - new(new[] { "--keyword", "-k" }, "Search method by keyword"); - public Option RuntimeFunction { get; } = - new(new[] { "--runtimefunction", "-f" }, "Get one runtime function by id or relative virtual address"); - public Option Section { get; } = - new(new[] { "--section", "-s" }, "Get section by keyword"); - - public Option Unwind { get; } = - new(new[] { "--unwind" }, "Dump unwindInfo"); - public Option GC { get; } = - new(new[] { "--gc" }, "Dump gcInfo and slot table"); - public Option Pgo { get; } = - new(new[] { "--pgo" }, "Dump embedded pgo instrumentation data"); - public Option SectionContents { get; } = - new(new[] { "--sectionContents", "--sc" }, "Dump section contents"); - public Option EntryPoints { get; } = - new(new[] { "--entrypoints", "-e" }, "Dump list of method / instance entrypoints in the R2R file"); - public Option Normalize { get; } = - new(new[] { "--normalize", "-n" }, "Normalize dump by sorting the various tables and methods (default = unsorted i.e. file order)"); - public Option HideTransitions { get; } = - new(new[] { "--hide-transitions", "--ht" }, "Don't include GC transitions in disassembly output"); - public Option Verbose { get; } = - new(new[] { "--verbose" }, "Dump disassembly, unwindInfo, gcInfo and sectionContents"); - public Option Diff { get; } = - new(new[] { "--diff" }, "Compare two R2R images"); - public Option DiffHideSameDisasm { get; } = - new(new[] { "--diff-hide-same-disasm" }, "In matching method diff dump, hide functions with identical disassembly"); - - public Option CreatePDB { get; } = - new(new[] { "--create-pdb" }, "Create PDB"); - public Option PdbPath { get; } = - new(new[] { "--pdb-path" }, "PDB output path for --create-pdb"); - - public Option CreatePerfmap { get; } = - new(new[] { "--create-perfmap" }, "Create PerfMap"); - public Option PerfmapPath { get; } = - new(new[] { "--perfmap-path" }, "PerfMap output path for --create-perfmap"); - public Option PerfmapFormatVersion { get; } = - new(new[] { "--perfmap-format-version" }, () => ILCompiler.Diagnostics.PerfMapWriter.CurrentFormatVersion, "PerfMap format version for --create-perfmap"); - - public Option> Reference { get; } = - new(new[] { "--reference", "-r" }, result => Helpers.BuildPathList(result.Tokens), true, "Explicit reference assembly files"); - public Option ReferencePath { get; } = - new(new[] { "--referencePath", "--rp" }, "Search paths for reference assemblies"); - - public Option SignatureBinary { get; } = - new(new[] { "--signatureBinary", "--sb" }, "Append signature binary to its textual representation"); - public Option InlineSignatureBinary { get; } = - new(new[] { "--inlineSignatureBinary", "--isb" }, "Embed binary signature into its textual representation"); - public Option ValidateDebugInfo { get; } = - new(new[] { "--validateDebugInfo", "--val" }, "Validate functions reported debug info."); + public CliOption> In { get; } = + new("--in", "-i") { CustomParser = result => Helpers.BuildPathList(result.Tokens), DefaultValueFactory = result => Helpers.BuildPathList(result.Tokens), Description = "Input file(s) to dump. Expects them to by ReadyToRun images" }; + public CliOption Out { get; } = + new("--out", "-o") { Description = "Output file path. Dumps everything to the specified file except for help message and exception messages" }; + public CliOption Raw { get; } = + new("--raw") { Description = "Dump the raw bytes of each section or runtime function" }; + public CliOption Header { get; } = + new("--header") { Description = "Dump R2R header" }; + public CliOption Disasm { get; } = + new("--disasm", "-d") { Description = "Show disassembly of methods or runtime functions" }; + public CliOption Naked { get; } = + new("--naked") { Description = "Naked dump suppresses most compilation details like placement addresses" }; + public CliOption HideOffsets { get; } = + new("--hide-offsets", "--ho") { Description = "Hide offsets in naked disassembly" }; + + public CliOption Query { get; } = + new("--query", "-q") { Description = "Query method by exact name, signature, row ID or token" }; + public CliOption Keyword { get; } = + new("--keyword", "-k") { Description = "Search method by keyword" }; + public CliOption RuntimeFunction { get; } = + new("--runtimefunction", "-f") { Description = "Get one runtime function by id or relative virtual address" }; + public CliOption Section { get; } = + new("--section", "-s") { Description = "Get section by keyword" }; + + public CliOption Unwind { get; } = + new("--unwind") { Description = "Dump unwindInfo" }; + public CliOption GC { get; } = + new("--gc") { Description = "Dump gcInfo and slot table" }; + public CliOption Pgo { get; } = + new("--pgo") { Description = "Dump embedded pgo instrumentation data" }; + public CliOption SectionContents { get; } = + new("--sectionContents", "--sc") { Description = "Dump section contents" }; + public CliOption EntryPoints { get; } = + new("--entrypoints", "-e") { Description = "Dump list of method / instance entrypoints in the R2R file" }; + public CliOption Normalize { get; } = + new("--normalize", "-n") { Description = "Normalize dump by sorting the various tables and methods (default = unsorted i.e. file order)" }; + public CliOption HideTransitions { get; } = + new("--hide-transitions", "--ht") { Description = "Don't include GC transitions in disassembly output" }; + public CliOption Verbose { get; } = + new("--verbose") { Description = "Dump disassembly, unwindInfo, gcInfo and sectionContents" }; + public CliOption Diff { get; } = + new("--diff") { Description = "Compare two R2R images" }; + public CliOption DiffHideSameDisasm { get; } = + new("--diff-hide-same-disasm") { Description = "In matching method diff dump, hide functions with identical disassembly" }; + + public CliOption CreatePDB { get; } = + new("--create-pdb") { Description = "Create PDB" }; + public CliOption PdbPath { get; } = + new("--pdb-path") { Description = "PDB output path for --create-pdb" }; + + public CliOption CreatePerfmap { get; } = + new("--create-perfmap") { Description = "Create PerfMap" }; + public CliOption PerfmapPath { get; } = + new("--perfmap-path") { Description = "PerfMap output path for --create-perfmap" }; + public CliOption PerfmapFormatVersion { get; } = + new("--perfmap-format-version") { DefaultValueFactory = _ => ILCompiler.Diagnostics.PerfMapWriter.CurrentFormatVersion, Description = "PerfMap format version for --create-perfmap" }; + + public CliOption> Reference { get; } = + new("--reference", "-r") { CustomParser = result => Helpers.BuildPathList(result.Tokens), DefaultValueFactory = result => Helpers.BuildPathList(result.Tokens), Description = "Explicit reference assembly files" }; + public CliOption ReferencePath { get; } = + new("--referencePath", "--rp") { Description = "Search paths for reference assemblies" }; + + public CliOption SignatureBinary { get; } = + new("--signatureBinary", "--sb") { Description = "Append signature binary to its textual representation" }; + public CliOption InlineSignatureBinary { get; } = + new("--inlineSignatureBinary", "--isb") { Description = "Embed binary signature into its textual representation" }; + public CliOption ValidateDebugInfo { get; } = + new("--validateDebugInfo", "--val") { Description = "Validate functions reported debug info." }; public ParseResult Result; public R2RDumpRootCommand() : base("Parses and outputs the contents of a ReadyToRun image") { - AddOption(In); - AddOption(Out); - AddOption(Raw); - AddOption(Header); - AddOption(Disasm); - AddOption(Naked); - AddOption(HideOffsets); - - AddOption(Query); - AddOption(Keyword); - AddOption(RuntimeFunction); - AddOption(Section); - - AddOption(Unwind); - AddOption(GC); - AddOption(Pgo); - AddOption(SectionContents); - AddOption(EntryPoints); - AddOption(Normalize); - AddOption(HideTransitions); - AddOption(Verbose); - AddOption(Diff); - AddOption(DiffHideSameDisasm); - - AddOption(CreatePDB); - AddOption(PdbPath); - - AddOption(CreatePerfmap); - AddOption(PerfmapPath); - AddOption(PerfmapFormatVersion); - - AddOption(Reference); - AddOption(ReferencePath); - - AddOption(SignatureBinary); - AddOption(InlineSignatureBinary); - AddOption(ValidateDebugInfo); - - this.SetHandler(context => + Options.Add(In); + Options.Add(Out); + Options.Add(Raw); + Options.Add(Header); + Options.Add(Disasm); + Options.Add(Naked); + Options.Add(HideOffsets); + + Options.Add(Query); + Options.Add(Keyword); + Options.Add(RuntimeFunction); + Options.Add(Section); + + Options.Add(Unwind); + Options.Add(GC); + Options.Add(Pgo); + Options.Add(SectionContents); + Options.Add(EntryPoints); + Options.Add(Normalize); + Options.Add(HideTransitions); + Options.Add(Verbose); + Options.Add(Diff); + Options.Add(DiffHideSameDisasm); + + Options.Add(CreatePDB); + Options.Add(PdbPath); + + Options.Add(CreatePerfmap); + Options.Add(PerfmapPath); + Options.Add(PerfmapFormatVersion); + + Options.Add(Reference); + Options.Add(ReferencePath); + + Options.Add(SignatureBinary); + Options.Add(InlineSignatureBinary); + Options.Add(ValidateDebugInfo); + + SetAction(parseResult => { - Result = context.ParseResult; + Result = parseResult; try { - context.ExitCode = new Program(this).Run(); + return new Program(this).Run(); } catch (Exception e) { @@ -140,7 +140,7 @@ public R2RDumpRootCommand() Console.ResetColor(); - context.ExitCode = 1; + return 1; } }); } diff --git a/src/coreclr/tools/r2rtest/CommandLineOptions.cs b/src/coreclr/tools/r2rtest/CommandLineOptions.cs index 6c1bae6412b6bda00a1ec27fdc49e98840f8bc74..f807fd7d7eb4eebdd21784387455cd0c1e96f578 100644 --- a/src/coreclr/tools/r2rtest/CommandLineOptions.cs +++ b/src/coreclr/tools/r2rtest/CommandLineOptions.cs @@ -8,26 +8,27 @@ namespace R2RTest { - public class R2RTestRootCommand : RootCommand + public class R2RTestRootCommand : CliRootCommand { - void CreateCommand(string name, string description, Option[] options, Func action) + void CreateCommand(string name, string description, CliOption[] options, Func action) { - Command command = new Command(name, description); + CliCommand command = new(name, description); foreach (var option in GetCommonOptions()) - command.AddOption(option); + command.Options.Add(option); foreach (var option in options) - command.AddOption(option); - command.SetHandler(context => - context.ExitCode = action(new BuildOptions(this, context.ParseResult))); - AddCommand(command); + command.Options.Add(option); + command.SetAction(result => action(new BuildOptions(this, result))); + Subcommands.Add(command); } - Option[] GetCommonOptions() => new Option[] { CoreRootDirectory, DotNetCli }; + CliOption[] GetCommonOptions() => new CliOption[] { CoreRootDirectory, DotNetCli }; R2RTestRootCommand() { + OutputDirectory.AcceptLegalFilePathsOnly(); + CreateCommand("compile-directory", "Compile all assemblies in directory", - new Option[] + new CliOption[] { InputDirectory, OutputDirectory, @@ -66,7 +67,7 @@ void CreateCommand(string name, string description, Option[] options, Func InputDirectory { get; } = - new Option(new[] { "--input-directory", "-in" }, "Folder containing assemblies to optimize").AcceptExistingOnly(); + public CliOption InputDirectory { get; } = + new CliOption("--input-directory", "-in") { Description = "Folder containing assemblies to optimize" }.AcceptExistingOnly(); - public Option OutputDirectory { get; } = - new Option(new[] { "--output-directory", "-out" }, "Folder to emit compiled assemblies").AcceptLegalFilePathsOnly(); + public CliOption OutputDirectory { get; } = + new CliOption("--output-directory", "-out") { Description = "Folder to emit compiled assemblies" }; - public Option CoreRootDirectory { get; } = - new Option(new[] { "--core-root-directory", "-cr" }, "Location of the CoreCLR CORE_ROOT folder") - { Arity = ArgumentArity.ExactlyOne }.AcceptExistingOnly(); + public CliOption CoreRootDirectory { get; } = + new CliOption("--core-root-directory", "-cr") { Description = "Location of the CoreCLR CORE_ROOT folder", Arity = ArgumentArity.ExactlyOne }.AcceptExistingOnly(); - public Option ReferencePath { get; } = - new Option(new[] { "--reference-path", "-r" }, "Folder containing assemblies to reference during compilation") - { Arity = ArgumentArity.ZeroOrMore }.AcceptExistingOnly(); + public CliOption ReferencePath { get; } = + new CliOption("--reference-path", "-r") { Description = "Folder containing assemblies to reference during compilation", Arity = ArgumentArity.ZeroOrMore }.AcceptExistingOnly(); - public Option MibcPath { get; } = - new Option(new[] { "--mibc-path", "-m" }, "Mibc files to use in compilation") - { Arity = ArgumentArity.ZeroOrMore }.AcceptExistingOnly(); + public CliOption MibcPath { get; } = + new CliOption("--mibc-path", "-m") { Description = "Mibc files to use in compilation", Arity = ArgumentArity.ZeroOrMore }.AcceptExistingOnly(); - public Option Crossgen2Path { get; } = - new Option(new[] { "--crossgen2-path", "-c2p" }, "Explicit Crossgen2 path (useful for cross-targeting)").AcceptExistingOnly(); + public CliOption Crossgen2Path { get; } = + new CliOption("--crossgen2-path", "-c2p") { Description = "Explicit Crossgen2 path (useful for cross-targeting)" }.AcceptExistingOnly(); - public Option VerifyTypeAndFieldLayout { get; } = - new(new[] { "--verify-type-and-field-layout" }, "Verify that struct type layout and field offsets match between compile time and runtime. Use only for diagnostic purposes."); + public CliOption VerifyTypeAndFieldLayout { get; } = + new("--verify-type-and-field-layout") { Description = "Verify that struct type layout and field offsets match between compile time and runtime. Use only for diagnostic purposes." }; - public Option NoJit { get; } = - new(new[] { "--nojit" }, "Don't run tests in JITted mode"); + public CliOption NoJit { get; } = + new("--nojit") { Description = "Don't run tests in JITted mode" }; - public Option NoCrossgen2 { get; } = - new(new[] { "--nocrossgen2" }, "Don't run tests in Crossgen2 mode"); + public CliOption NoCrossgen2 { get; } = + new("--nocrossgen2") { Description = "Don't run tests in Crossgen2 mode" }; - public Option Exe { get; } = - new(new[] { "--exe" }, "Don't compile tests, just execute them"); + public CliOption Exe { get; } = + new("--exe") { Description = "Don't compile tests, just execute them" }; - public Option NoExe { get; } = - new(new[] { "--noexe" }, "Compilation-only mode (don't execute the built apps)"); + public CliOption NoExe { get; } = + new("--noexe") { Description = "Compilation-only mode (don't execute the built apps)" }; - public Option NoEtw { get; } = - new(new[] { "--noetw" }, "Don't capture jitted methods using ETW"); + public CliOption NoEtw { get; } = + new("--noetw") { Description = "Don't capture jitted methods using ETW" }; - public Option NoCleanup { get; } = - new(new[] { "--nocleanup" }, "Don't clean up compilation artifacts after test runs"); + public CliOption NoCleanup { get; } = + new("--nocleanup") { Description = "Don't clean up compilation artifacts after test runs" }; - public Option Map { get; } = - new(new[] { "--map" }, "Generate a map file (Crossgen2)"); + public CliOption Map { get; } = + new("--map") { Description = "Generate a map file (Crossgen2)" }; - public Option Pdb { get; } = - new(new[] { "--pdb" }, "Generate PDB symbol information (Crossgen2 / Windows only)"); + public CliOption Pdb { get; } = + new("--pdb") { Description = "Generate PDB symbol information (Crossgen2 / Windows only)" }; - public Option Perfmap { get; } = - new(new[] { "--perfmap" }, "Generate perfmap symbol information"); + public CliOption Perfmap { get; } = + new("--perfmap") { Description = "Generate perfmap symbol information" }; - public Option PerfmapFormatVersion { get; } = - new(new[] { "--perfmap-format-version" }, () => 1, "Perfmap format version to generate"); + public CliOption PerfmapFormatVersion { get; } = + new("--perfmap-format-version") { DefaultValueFactory = _ => 1, Description = "Perfmap format version to generate" }; - public Option DegreeOfParallelism { get; } = - new(new[] { "--degree-of-parallelism", "-dop" }, "Override default compilation / execution DOP (default = logical processor count)"); + public CliOption DegreeOfParallelism { get; } = + new("--degree-of-parallelism", "-dop") { Description = "Override default compilation / execution DOP (default = logical processor count)" }; - public Option Sequential { get; } = - new(new[] { "--sequential" }, "Run tests sequentially"); + public CliOption Sequential { get; } = + new("--sequential") { Description = "Run tests sequentially" }; - public Option Iterations { get; } = - new(new[] { "--iterations" }, () => 1, "Number of iterations for each test execution"); + public CliOption Iterations { get; } = + new("--iterations") { DefaultValueFactory = _ => 1, Description = "Number of iterations for each test execution" }; - public Option Framework { get; } = - new(new[] { "--framework" }, "Precompile and use native framework"); + public CliOption Framework { get; } = + new("--framework") { Description = "Precompile and use native framework" }; - public Option UseFramework { get; } = - new(new[] { "--use-framework" }, "Use native framework (don't precompile, assume previously compiled)"); + public CliOption UseFramework { get; } = + new("--use-framework") { Description = "Use native framework (don't precompile, assume previously compiled)" }; - public Option Release { get; } = - new(new[] { "--release" }, "Build the tests in release mode"); + public CliOption Release { get; } = + new("--release") { Description = "Build the tests in release mode" }; - public Option LargeBubble { get; } = - new(new[] { "--large-bubble" }, "Assume all input files as part of one version bubble"); + public CliOption LargeBubble { get; } = + new("--large-bubble") { Description = "Assume all input files as part of one version bubble" }; - public Option Composite { get; } = - new(new[] { "--composite" }, "Compile tests in composite R2R mode"); + public CliOption Composite { get; } = + new("--composite") { Description = "Compile tests in composite R2R mode" }; - public Option Crossgen2Parallelism { get; } = - new(new[] { "--crossgen2-parallelism" }, "Max number of threads to use in Crossgen2 (default = logical processor count)"); + public CliOption Crossgen2Parallelism { get; } = + new("--crossgen2-parallelism") { Description = "Max number of threads to use in Crossgen2 (default = logical processor count)" }; - public Option Crossgen2JitPath { get; } = - new(new[] { "--crossgen2-jitpath" }, "Jit path to use for crossgen2"); + public CliOption Crossgen2JitPath { get; } = + new("--crossgen2-jitpath") { Description = "Jit path to use for crossgen2" }; - public Option IssuesPath { get; } = - new Option(new[] { "--issues-path", "-ip" }, "Path to issues.targets") - { Arity = ArgumentArity.ZeroOrMore }; + public CliOption IssuesPath { get; } = + new("--issues-path", "-ip") { Description = "Path to issues.targets", Arity = ArgumentArity.ZeroOrMore }; - public Option CompilationTimeoutMinutes { get; } = - new(new[] { "--compilation-timeout-minutes", "-ct" }, "Compilation timeout (minutes)"); + public CliOption CompilationTimeoutMinutes { get; } = + new("--compilation-timeout-minutes", "-ct") { Description = "Compilation timeout (minutes)" }; - public Option ExecutionTimeoutMinutes { get; } = - new(new[] { "--execution-timeout-minutes", "-et" }, "Execution timeout (minutes)"); + public CliOption ExecutionTimeoutMinutes { get; } = + new("--execution-timeout-minutes", "-et") { Description = "Execution timeout (minutes)" }; - public Option R2RDumpPath { get; } = - new Option(new[] { "--r2r-dump-path" }, "Path to R2RDump.exe/dll").AcceptExistingOnly(); + public CliOption R2RDumpPath { get; } = + new CliOption("--r2r-dump-path") { Description = "Path to R2RDump.exe/dll" }.AcceptExistingOnly(); - public Option MeasurePerf { get; } = - new(new[] { "--measure-perf" }, "Print out compilation time"); + public CliOption MeasurePerf { get; } = + new("--measure-perf") { Description = "Print out compilation time" }; - public Option InputFileSearchString { get; } = - new(new[] { "--input-file-search-string", "-input-file" }, "Search string for input files in the input directory"); + public CliOption InputFileSearchString { get; } = + new("--input-file-search-string", "-input-file") { Description = "Search string for input files in the input directory" }; - public Option GCStress { get; } = - new(new[] { "--gcstress" }, "Run tests with the specified GC stress level enabled (the argument value is in hex)"); + public CliOption GCStress { get; } = + new("--gcstress") { Description = "Run tests with the specified GC stress level enabled (the argument value is in hex)" }; - public Option DotNetCli { get; } = - new(new [] { "--dotnet-cli", "-cli" }, "For dev box testing, point at .NET 5 dotnet.exe or /dotnet.cmd."); + public CliOption DotNetCli { get; } = + new("--dotnet-cli", "-cli") { Description = "For dev box testing, point at .NET 5 dotnet.exe or /dotnet.cmd." }; - public Option TargetArch { get; } = - new(new[] { "--target-arch" }, "Target architecture for crossgen2"); + public CliOption TargetArch { get; } = + new("--target-arch") { Description = "Target architecture for crossgen2" }; // // compile-nuget specific options // - public Option PackageList { get; } = - new Option(new[] { "--package-list", "-pl" }, "Text file containing a package name on each line").AcceptExistingOnly(); + public CliOption PackageList { get; } = + new CliOption("--package-list", "-pl") { Description = "Text file containing a package name on each line" }.AcceptExistingOnly(); // // compile-serp specific options // - public Option AspNetPath { get; } = - new Option(new[] { "--asp-net-path", "-asp" }, "Path to SERP's ASP.NET Core folder").AcceptExistingOnly(); + public CliOption AspNetPath { get; } = + new CliOption("--asp-net-path", "-asp") { Description = "Path to SERP's ASP.NET Core folder" }.AcceptExistingOnly(); private static int Main(string[] args) => - new CommandLineBuilder(new R2RTestRootCommand()) - .UseVersionOption("--version", "-v") - .UseHelp() - .UseParseErrorReporting() - .Build() - .Invoke(args); + new CliConfiguration(new R2RTestRootCommand().UseVersion()) + { + EnableParseErrorReporting = true + }.Invoke(args); } public partial class BuildOptions diff --git a/src/coreclr/tools/r2rtest/R2RTest.csproj b/src/coreclr/tools/r2rtest/R2RTest.csproj index 245d062b6a46942dec2da00c66a674d655deb3dd..c304c492e66ccb437e546a926b3302238dad2bc0 100644 --- a/src/coreclr/tools/r2rtest/R2RTest.csproj +++ b/src/coreclr/tools/r2rtest/R2RTest.csproj @@ -11,17 +11,16 @@ - - 16.0.461 - - - 16.0.461 - - - $(TraceEventVersion) - - - $(SystemCommandLineVersion) - + + + + + + + + + + +