Update to latest System.CommandLine package (#66929)
authorJan Kotas <jkotas@microsoft.com>
Mon, 21 Mar 2022 22:36:51 +0000 (15:36 -0700)
committerGitHub <noreply@github.com>
Mon, 21 Mar 2022 22:36:51 +0000 (15:36 -0700)
eng/Version.Details.xml
eng/Versions.props
src/coreclr/tools/ILVerify/Program.cs
src/coreclr/tools/r2rdump/CommandLineOptions.cs
src/coreclr/tools/r2rdump/R2RDump.cs
src/coreclr/tools/r2rtest/BuildOptions.cs
src/coreclr/tools/r2rtest/CommandLineOptions.cs
src/coreclr/tools/r2rtest/Program.cs [deleted file]

index 8a6a809..5a8f02b 100644 (file)
       <Uri>https://github.com/dotnet/llvm-project</Uri>
       <Sha>f17917e8d6a2e3ba069cddeab873554706d502a5</Sha>
     </Dependency>
+    <Dependency Name="System.CommandLine" Version="2.0.0-beta3.22151.2">
+      <Uri>https://github.com/dotnet/command-line-api</Uri>
+      <Sha>021ec68a4cb510c2cc125c6ebb78b9cfd4e3847a</Sha>
+    </Dependency>
   </ProductDependencies>
   <ToolsetDependencies>
     <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="7.0.0-beta.22166.1">
index 9006c1f..d4c3214 100644 (file)
     <optimizationPGOCoreCLRVersion>1.0.0-prerelease.22121.2</optimizationPGOCoreCLRVersion>
     <!-- Not auto-updated. -->
     <MicrosoftDiaSymReaderNativeVersion>16.9.0-beta1.21055.5</MicrosoftDiaSymReaderNativeVersion>
-    <SystemCommandLineVersion>2.0.0-beta1.20253.1</SystemCommandLineVersion>
+    <SystemCommandLineVersion>2.0.0-beta3.22114.1</SystemCommandLineVersion>
     <TraceEventVersion>2.0.65</TraceEventVersion>
-    <CommandLineParserVersion>2.2.0</CommandLineParserVersion>
     <NETStandardLibraryRefVersion>2.1.0</NETStandardLibraryRefVersion>
     <NetStandardLibraryVersion>2.0.3</NetStandardLibraryVersion>
     <MicrosoftDiagnosticsToolsRuntimeClientVersion>1.0.4-preview6.19326.1</MicrosoftDiagnosticsToolsRuntimeClientVersion>
index 44759ea..851f9d9 100644 (file)
@@ -4,7 +4,9 @@
 using System;
 using System.Collections.Generic;
 using System.CommandLine;
+using System.CommandLine.Builder;
 using System.CommandLine.Invocation;
+using System.CommandLine.Parsing;
 using System.IO;
 using System.Linq;
 using System.Reflection;
@@ -43,42 +45,6 @@ namespace ILVerify
             return patternList;
         }
 
-        public class Options
-        {
-            public string[] InputFilePath { get; set; }
-            public string[] Reference { get; set; }
-            public string SystemModule { get; set; }
-            public bool SanityChecks { get; set; }
-            public string[] Include { get; set; }
-            public FileInfo IncludeFile { get; set; }
-            public string[] Exclude { get; set; }
-            public FileInfo ExcludeFile { get; set; }
-            public string[] IgnoreError { get; set; }
-            public FileInfo IgnoreErrorFile { get; set; }
-            public bool Statistics { get; set; }
-            public bool Verbose { get; set; }
-            public bool Tokens { get; set; }
-        }
-
-        public static RootCommand RootCommand()
-        {
-            RootCommand command = new RootCommand();
-            command.AddArgument(new Argument<string[]>("input-file-path", "Input file(s)") { Arity = new ArgumentArity(1, Int32.MaxValue) });
-            command.AddOption(new Option<string[]>(new[] { "--reference", "-r" }, "Reference metadata from the specified assembly"));
-            command.AddOption(new Option<string>(new[] { "--system-module", "-s" }, "System module name (default: mscorlib)"));
-            command.AddOption(new Option<bool>(new[] { "--sanity-checks", "-c" }, "Check for valid constructs that are likely mistakes"));
-            command.AddOption(new Option<string[]>(new[] { "--include", "-i" }, "Use only methods/types/namespaces, which match the given regular expression(s)"));
-            command.AddOption(new Option<FileInfo>(new[] { "--include-file" }, "Same as --include, but the regular expression(s) are declared line by line in the specified file.").ExistingOnly());
-            command.AddOption(new Option<string[]>(new[] { "--exclude", "-e" }, "Skip methods/types/namespaces, which match the given regular expression(s)"));
-            command.AddOption(new Option<FileInfo>(new[] { "--exclude-file" }, "Same as --exclude, but the regular expression(s) are declared line by line in the specified file.").ExistingOnly());
-            command.AddOption(new Option<string[]>(new[] { "--ignore-error", "-g" }, "Ignore errors, which match the given regular expression(s)"));
-            command.AddOption(new Option<FileInfo>(new[] { "--ignore-error-file" }, "Same as --ignore-error, but the regular expression(s) are declared line by line in the specified file.").ExistingOnly());
-            command.AddOption(new Option<bool>(new[] { "--statistics" }, "Print verification statistics"));
-            command.AddOption(new Option<bool>(new[] { "--verbose", "-v" }, "Verbose output"));
-            command.AddOption(new Option<bool>(new[] { "--tokens", "-t" }, "Include metadata tokens in error messages"));
-            return command;
-        }
-
         private Program(Options options)
         {
             _options = options;
@@ -489,29 +455,118 @@ namespace ILVerify
             return null;
         }
 
-        private static int Run(Options options)
+        //
+        // Command line parsing
+        //
+
+        private class ILVerifyRootCommand : RootCommand
         {
-            try
-            {
-                return new Program(options).Run();
-            }
-            catch (CommandLineException e)
-            {
-                Console.WriteLine("Error: " + e.Message);
-                return 1;
-            }
-            catch (Exception e)
+            public Argument<string[]> InputFilePath { get; } =
+                new("input-file-path", "Input file(s)") { Arity = ArgumentArity.OneOrMore };
+            public Option<string[]> Reference { get; } =
+                new(new[] { "--reference", "-r" }, "Reference metadata from the specified assembly");
+            public Option<string> SystemModule { get; } =
+                new(new[] { "--system-module", "-s" }, "System module name (default: mscorlib)");
+            public Option<bool> SanityChecks { get; } =
+                new(new[] { "--sanity-checks", "-c" }, "Check for valid constructs that are likely mistakes");
+            public Option<string[]> Include { get; } =
+                new(new[] { "--include", "-i" }, "Use only methods/types/namespaces, which match the given regular expression(s)");
+            public Option<FileInfo> IncludeFile { get; } =
+                new Option<FileInfo>(new[] { "--include-file" }, "Same as --include, but the regular expression(s) are declared line by line in the specified file.").ExistingOnly();
+            public Option<string[]> Exclude { get; } =
+                new(new[] { "--exclude", "-e" }, "Skip methods/types/namespaces, which match the given regular expression(s)");
+            public Option<FileInfo> ExcludeFile { get; } =
+                new Option<FileInfo>(new[] { "--exclude-file" }, "Same as --exclude, but the regular expression(s) are declared line by line in the specified file.").ExistingOnly();
+            public Option<string[]> IgnoreError { get; } =
+                new(new[] { "--ignore-error", "-g" }, "Ignore errors, which match the given regular expression(s)");
+            public Option<FileInfo> IgnoreErrorFile { get; } =
+                new Option<FileInfo>(new[] { "--ignore-error-file" }, "Same as --ignore-error, but the regular expression(s) are declared line by line in the specified file.").ExistingOnly();
+            public Option<bool> Statistics { get; } =
+                new(new[] { "--statistics" }, "Print verification statistics");
+            public Option<bool> Verbose { get; } =
+                new(new[] { "--verbose", "-v" }, "Verbose output");
+            public Option<bool> Tokens { get; } =
+                new(new[] { "--tokens", "-t" }, "Include metadata tokens in error messages");
+
+            public ILVerifyRootCommand()
+                : base("Tool for verifying MSIL code based on ECMA-335.")
             {
-                Console.WriteLine("Error: " + e.ToString());
-                return 1;
+                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);
+
+                this.SetHandler<InvocationContext>((InvocationContext context) =>
+                {
+                    try
+                    {
+                        context.ExitCode = new Program(new Options(this, context.ParseResult)).Run();
+                    }
+                    catch (Exception e)
+                    {
+                        Console.ResetColor();
+                        Console.ForegroundColor = ConsoleColor.Red;
+
+                        // Omit the stacktrace from the error (different from the default System.CommandLine exception handler)
+                        Console.Error.WriteLine("Error: " + e.Message);
+
+                        Console.ResetColor();
+
+                        context.ExitCode = 1;
+                    }
+                });
             }
         }
 
-        private static async Task<int> Main(string[] args)
+        private class Options
         {
-            var command = RootCommand();
-            command.Handler = CommandHandler.Create<Options>(Run);
-            return await command.InvokeAsync(args);
+            public Options(ILVerifyRootCommand cmd, ParseResult res)
+            {
+                InputFilePath = res.GetValueForArgument(cmd.InputFilePath);
+                Reference = res.GetValueForOption(cmd.Reference);
+                SystemModule = res.GetValueForOption(cmd.SystemModule);
+                SanityChecks = res.GetValueForOption(cmd.SanityChecks);
+                Include = res.GetValueForOption(cmd.Include);
+                IncludeFile = res.GetValueForOption(cmd.IncludeFile);
+                Exclude = res.GetValueForOption(cmd.Exclude);
+                ExcludeFile = res.GetValueForOption(cmd.ExcludeFile);
+                IgnoreError = res.GetValueForOption(cmd.IgnoreError);
+                IgnoreErrorFile = res.GetValueForOption(cmd.IgnoreErrorFile);
+                Statistics = res.GetValueForOption(cmd.Statistics);
+                Verbose = res.GetValueForOption(cmd.Verbose);
+                Tokens = res.GetValueForOption(cmd.Tokens);
+            }
+
+            public string[] InputFilePath { get; }
+            public string[] Reference { get; }
+            public string SystemModule { get; }
+            public bool SanityChecks { get; }
+            public string[] Include { get; }
+            public FileInfo IncludeFile { get; }
+            public string[] Exclude { get; }
+            public FileInfo ExcludeFile { get; }
+            public string[] IgnoreError { get; }
+            public FileInfo IgnoreErrorFile { get; }
+            public bool Statistics { get; }
+            public bool Verbose { get; }
+            public bool Tokens { get; }
         }
+
+        private static int Main(string[] args) =>
+            new CommandLineBuilder(new ILVerifyRootCommand())
+                .UseVersionOption()
+                .UseHelp()
+                .UseParseErrorReporting()
+                .Build()
+                .Invoke(args);
     }
 }
index 9a1296b..f0b6f71 100644 (file)
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.CommandLine;
+using System.CommandLine.Invocation;
+using System.CommandLine.Parsing;
 using System.IO;
 
 namespace R2RDump
 {
-    internal static class CommandLineOptions
+    public class R2RDumpRootCommand : RootCommand
     {
-        public static RootCommand RootCommand()
+        public Option<FileInfo[]> In { get; } =
+            new(new[] { "--in", "-i" }, "Input file(s) to dump. Expects them to by ReadyToRun images");
+        public Option<FileInfo> Out { get; } =
+            new(new[] { "--out", "-o" }, "Output file path. Dumps everything to the specified file except for help message and exception messages");
+        public Option<bool> Raw { get; } =
+            new(new[] { "--raw" }, "Dump the raw bytes of each section or runtime function");
+        public Option<bool> Header { get; } =
+            new(new[] { "--header" }, "Dump R2R header");
+        public Option<bool> Disasm { get; } =
+            new(new[] { "--disasm", "-d" }, "Show disassembly of methods or runtime functions");
+        public Option<bool> Naked { get; } =
+            new(new[] { "--naked" }, "Naked dump suppresses most compilation details like placement addresses");
+        public Option<bool> HideOffsets { get; } =
+            new(new[] { "--hide-offsets", "--ho" }, "Hide offsets in naked disassembly");
+
+        public Option<string[]> Query { get; } =
+            new(new[] { "--query", "-q" }, "Query method by exact name, signature, row ID or token");
+        public Option<string[]> Keyword { get; } =
+            new(new[] { "--keyword", "-k" }, "Search method by keyword");
+        public Option<string[]> RuntimeFunction { get; } =
+            new(new[] { "--runtimefunction", "-f" }, "Get one runtime function by id or relative virtual address");
+        public Option<string[]> Section { get; } =
+            new(new[] { "--section", "-s" }, "Get section by keyword");
+
+        public Option<bool> Unwind { get; } =
+            new(new[] { "--unwind" }, "Dump unwindInfo");
+        public Option<bool> GC { get; } =
+            new(new[] { "--gc" }, "Dump gcInfo and slot table");
+        public Option<bool> Pgo { get; } =
+            new(new[] { "--pgo" }, "Dump embedded pgo instrumentation data");
+        public Option<bool> SectionContents { get; } =
+            new(new[] { "--sectionContents", "--sc" }, "Dump section contents");
+        public Option<bool> EntryPoints { get; } =
+            new(new[] { "--entrypoints", "-e" }, "Dump list of method / instance entrypoints in the R2R file");
+        public Option<bool> Normalize { get; } =
+            new(new[] { "--normalize", "-n" }, "Normalize dump by sorting the various tables and methods (default = unsorted i.e. file order)");
+        public Option<bool> HideTransitions { get; } =
+            new(new[] { "--hide-transitions", "--ht" }, "Don't include GC transitions in disassembly output");
+        public Option<bool> Verbose { get; } =
+            new(new[] { "--verbose", "-v" }, "Dump disassembly, unwindInfo, gcInfo and sectionContents");
+        public Option<bool> Diff { get; } =
+            new(new[] { "--diff" }, "Compare two R2R images");
+        public Option<bool> DiffHideSameDisasm { get; } =
+            new(new[] { "--diff-hide-same-disasm" }, "In matching method diff dump, hide functions with identical disassembly");
+
+        public Option<bool> CreatePDB { get; } =
+            new(new[] { "--create-pdb" }, "Create PDB");
+        public Option<string> PdbPath { get; } =
+            new(new[] { "--pdb-path" }, "PDB output path for --create-pdb");
+
+        public Option<bool> CreatePerfmap { get; } =
+            new(new[] { "--create-perfmap" }, "Create PerfMap");
+        public Option<string> PerfmapPath { get; } =
+            new(new[] { "--perfmap-path" }, "PerfMap output path for --create-perfmap");
+        public Option<int> PerfmapFormatVersion { get; } =
+            new(new[] { "--perfmap-format-version" }, () => ILCompiler.Diagnostics.PerfMapWriter.CurrentFormatVersion, "PerfMap format version for --create-perfmap");
+
+        public Option<FileInfo[]> Reference { get; } =
+            new(new[] { "--reference", "-r" }, "Explicit reference assembly files");
+        public Option<DirectoryInfo[]> ReferencePath { get; } =
+            new(new[] { "--referencePath", "--rp" }, "Search paths for reference assemblies");
+
+        public Option<bool> SignatureBinary { get; } =
+            new(new[] { "--signatureBinary", "--sb" }, "Append signature binary to its textual representation");
+        public Option<bool> InlineSignatureBinary { get; } =
+            new(new[] { "--inlineSignatureBinary", "--isb" }, "Embed binary signature into its textual representation");
+
+        public R2RDumpRootCommand()
+            : base("Parses and outputs the contents of a ReadyToRun image")
         {
-            RootCommand command = new RootCommand();
-            command.AddOption(new Option<FileInfo[]>(new[] { "--in", "-i" }, "Input file(s) to dump. Expects them to by ReadyToRun images"));
-            command.AddOption(new Option<FileInfo>(new[] { "--out", "-o" }, "Output file path. Dumps everything to the specified file except for help message and exception messages"));
-            command.AddOption(new Option<bool>(new[] { "--raw" }, "Dump the raw bytes of each section or runtime function"));
-            command.AddOption(new Option<bool>(new[] { "--header" }, "Dump R2R header"));
-            command.AddOption(new Option<bool>(new[] { "--disasm", "-d" }, "Show disassembly of methods or runtime functions"));
-            command.AddOption(new Option<bool>(new[] { "--naked" }, "Naked dump suppresses most compilation details like placement addresses"));
-            command.AddOption(new Option<bool>(new[] { "--hide-offsets", "--ho" }, "Hide offsets in naked disassembly"));
-            command.AddOption(new Option<string[]>(new[] { "--query", "-q" }, "Query method by exact name, signature, row ID or token"));
-            command.AddOption(new Option<string[]>(new[] { "--keyword", "-k" }, "Search method by keyword"));
-            command.AddOption(new Option<string[]>(new[] { "--runtimefunction", "-f" }, "Get one runtime function by id or relative virtual address"));
-            command.AddOption(new Option<string[]>(new[] { "--section", "-s" }, "Get section by keyword"));
-            command.AddOption(new Option<bool>(new[] { "--unwind" }, "Dump unwindInfo"));
-            command.AddOption(new Option<bool>(new[] { "--gc" }, "Dump gcInfo and slot table"));
-            command.AddOption(new Option<bool>(new[] { "--pgo" }, "Dump embedded pgo instrumentation data"));
-            command.AddOption(new Option<bool>(new[] { "--sectionContents", "--sc" }, "Dump section contents"));
-            command.AddOption(new Option<bool>(new[] { "--entrypoints", "-e" }, "Dump list of method / instance entrypoints in the R2R file"));
-            command.AddOption(new Option<bool>(new[] { "--normalize", "-n" }, "Normalize dump by sorting the various tables and methods (default = unsorted i.e. file order)"));
-            command.AddOption(new Option<bool>(new[] { "--hide-transitions", "--ht" }, "Don't include GC transitions in disassembly output"));
-            command.AddOption(new Option<bool>(new[] { "--verbose", "-v" }, "Dump disassembly, unwindInfo, gcInfo and sectionContents"));
-            command.AddOption(new Option<bool>(new[] { "--diff" }, "Compare two R2R images"));
-            command.AddOption(new Option<bool>(new[] { "--diff-hide-same-disasm" }, "In matching method diff dump, hide functions with identical disassembly"));
-            command.AddOption(new Option<FileInfo[]>(new[] { "--reference", "-r" }, "Explicit reference assembly files"));
-            command.AddOption(new Option<DirectoryInfo[]>(new[] { "--referencePath", "--rp" }, "Search paths for reference assemblies"));
-            command.AddOption(new Option<bool>(new[] { "--inlineSignatureBinary", "--isb" }, "Embed binary signature into its textual representation"));
-            command.AddOption(new Option<bool>(new[] { "--signatureBinary", "--sb" }, "Append signature binary to its textual representation"));
-            command.AddOption(new Option<bool>(new[] { "--create-pdb" }, "Create PDB"));
-            command.AddOption(new Option<string>(new[] { "--pdb-path" }, "PDB output path for --create-pdb"));
-            command.AddOption(new Option<bool>(new[] { "--create-perfmap" }, "Create PerfMap"));
-            command.AddOption(new Option<string>(new[] { "--perfmap-path" }, "PerfMap output path for --create-perfmap"));
-            command.AddOption(new Option<int>(new[] { "--perfmap-format-version" }, "PerfMap format version for --create-perfmap"));
-            return command;
+            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);
+
+            this.SetHandler<InvocationContext>((InvocationContext context) =>
+                context.ExitCode = new R2RDump(new DumpOptions(this, context.ParseResult)).Run());
+        }
+    }
+
+    public partial class DumpOptions
+    {
+        public FileInfo[] In { get; }
+        public FileInfo Out { get; }
+        public bool Raw { get; }
+        public bool Header { get; }
+        public bool Disasm { get; }
+        public bool Naked { get; }
+        public bool HideOffsets { get; }
+
+        public string[] Query { get; }
+        public string[] Keyword { get; }
+        public string[] RuntimeFunction { get; }
+        public string[] Section { get; }
+
+        public bool Unwind { get; }
+        public bool GC { get; }
+        public bool Pgo { get; }
+        public bool SectionContents { get; }
+        public bool EntryPoints { get; }
+        public bool Normalize { get; }
+        public bool HideTransitions { get; }
+        public bool Verbose { get; }
+        public bool Diff { get; }
+        public bool DiffHideSameDisasm { get; }
+
+        public bool CreatePDB { get; }
+        public string PdbPath { get; }
+
+        public bool CreatePerfmap { get; }
+        public string PerfmapPath { get; }
+        public int PerfmapFormatVersion { get; }
+
+        public FileInfo[] Reference { get; }
+        public DirectoryInfo[] ReferencePath { get; }
+
+        public bool SignatureBinary { get; }
+        public bool InlineSignatureBinary { get; }
+
+        public DumpOptions(R2RDumpRootCommand cmd, ParseResult res)
+        {
+            In = res.GetValueForOption(cmd.In);
+            Out = res.GetValueForOption(cmd.Out);
+            Raw = res.GetValueForOption(cmd.Raw);
+            Header = res.GetValueForOption(cmd.Header);
+            Disasm = res.GetValueForOption(cmd.Disasm);
+            Naked = res.GetValueForOption(cmd.Naked);
+            HideOffsets = res.GetValueForOption(cmd.HideOffsets);
+
+            Query = res.GetValueForOption(cmd.Query);
+            Keyword = res.GetValueForOption(cmd.Keyword);
+            RuntimeFunction = res.GetValueForOption(cmd.RuntimeFunction);
+            Section = res.GetValueForOption(cmd.Section);
+
+            Unwind = res.GetValueForOption(cmd.Unwind);
+            GC = res.GetValueForOption(cmd.GC);
+            Pgo = res.GetValueForOption(cmd.Pgo);
+            SectionContents = res.GetValueForOption(cmd.SectionContents);
+            EntryPoints = res.GetValueForOption(cmd.EntryPoints);
+            Normalize = res.GetValueForOption(cmd.Normalize);
+            HideTransitions = res.GetValueForOption(cmd.HideTransitions);
+            Verbose = res.GetValueForOption(cmd.Verbose);
+            Diff = res.GetValueForOption(cmd.Diff);
+            DiffHideSameDisasm = res.GetValueForOption(cmd.DiffHideSameDisasm);
+
+            CreatePDB = res.GetValueForOption(cmd.CreatePDB);
+            PdbPath = res.GetValueForOption(cmd.PdbPath);
+
+            CreatePerfmap = res.GetValueForOption(cmd.CreatePerfmap);
+            PerfmapPath = res.GetValueForOption(cmd.PerfmapPath);
+            PerfmapFormatVersion = res.GetValueForOption(cmd.PerfmapFormatVersion);
+
+            Reference = res.GetValueForOption(cmd.Reference);
+            ReferencePath = res.GetValueForOption(cmd.ReferencePath);
+
+            SignatureBinary = res.GetValueForOption(cmd.SignatureBinary);
+            InlineSignatureBinary = res.GetValueForOption(cmd.InlineSignatureBinary);
+
+            if (Verbose)
+            {
+                Disasm = true;
+                Unwind = true;
+                GC = true;
+                Pgo = true;
+                SectionContents = true;
+            }
         }
     }
 }
index 6b7d192..3a8e7f9 100644 (file)
@@ -5,7 +5,9 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Immutable;
 using System.CommandLine;
+using System.CommandLine.Builder;
 using System.CommandLine.Invocation;
+using System.CommandLine.Parsing;
 using System.IO;
 using System.Linq;
 using System.Reflection.Metadata;
@@ -26,54 +28,10 @@ using OperatingSystem = ILCompiler.Reflection.ReadyToRun.OperatingSystem;
 
 namespace R2RDump
 {
-    public class DumpOptions : IAssemblyResolver
+    public partial class DumpOptions : IAssemblyResolver
     {
-        public FileInfo[] In { get; set; }
-        public FileInfo Out { get; set; }
-        public bool Raw { get; set; }
-        public bool Header { get; set; }
-        public bool Disasm { get; set; }
-        public bool Naked { get; set; }
-        public bool HideOffsets { get; set; }
-
-        public string[] Query { get; set; }
-        public string[] Keyword { get; set; }
-        public string[] RuntimeFunction { get; set; }
-        public string[] Section { get; set; }
-
-        public bool Unwind { get; set; }
-        public bool GC { get; set; }
-        public bool Pgo { get; set; }
-        public bool SectionContents { get; set; }
-        public bool EntryPoints { get; set; }
-        public bool Normalize { get; set; }
-        public bool HideTransitions { get; set; }
-        public bool Verbose { get; set; }
-        public bool Diff { get; set; }
-        public bool DiffHideSameDisasm { get; set; }
-        public bool IgnoreSensitive { get; set; }
-
-        public bool CreatePDB { get; set; }
-        public string PdbPath { get; set; }
-
-        public bool CreatePerfmap { get; set; }
-        public string PerfmapPath { get; set; }
-        public int PerfmapFormatVersion { get; set; }
-
-
-        public FileInfo[] Reference { get; set; }
-        public DirectoryInfo[] ReferencePath { get; set; }
-
-        public bool SignatureBinary { get; set; }
-        public bool InlineSignatureBinary { get; set; }
-
         private SignatureFormattingOptions signatureFormattingOptions;
 
-        public DumpOptions()
-        {
-            PerfmapFormatVersion = PerfMapWriter.CurrentFormatVersion;
-        }
-
         /// <summary>
         /// Probing extensions to use when looking up assemblies under reference paths.
         /// </summary>
@@ -232,7 +190,7 @@ namespace R2RDump
         public Disassembler Disassembler => _disassembler;
     }
 
-    class R2RDump
+    public class R2RDump
     {
         private readonly DumpOptions _options;
         private readonly Dictionary<ReadyToRunSectionType, bool> _selectedSections = new Dictionary<ReadyToRunSectionType, bool>();
@@ -240,20 +198,11 @@ namespace R2RDump
         private readonly TextWriter _writer;
         private Dumper _dumper;
 
-        private R2RDump(DumpOptions options)
+        public R2RDump(DumpOptions options)
         {
             _options = options;
             _encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false);
 
-            if (_options.Verbose)
-            {
-                _options.Disasm = true;
-                _options.Unwind = true;
-                _options.GC = true;
-                _options.Pgo = true;
-                _options.SectionContents = true;
-            }
-
             if (_options.Out != null)
             {
                 _writer = new StreamWriter(_options.Out.FullName, append: false, _encoding);
@@ -606,7 +555,7 @@ namespace R2RDump
             return null;
         }
 
-        private int Run()
+        public int Run()
         {
             Disassembler disassembler = null;
 
@@ -678,11 +627,15 @@ namespace R2RDump
             return 0;
         }
 
-        public static async Task<int> Main(string[] args)
-        {
-            var command = CommandLineOptions.RootCommand();
-            command.Handler = CommandHandler.Create<DumpOptions>((DumpOptions options) => new R2RDump(options).Run());
-            return await command.InvokeAsync(args);
-        }
+        //
+        // Command line parsing
+        //
+
+        public static int Main(string[] args) =>
+            new CommandLineBuilder(new R2RDumpRootCommand())
+                    .UseHelp()
+                    .UseParseErrorReporting()
+                    .Build()
+                    .Invoke(args);
     }
 }
index 5c3dd68..9c7deff 100644 (file)
@@ -8,50 +8,10 @@ using System.IO;
 
 namespace R2RTest
 {
-    public class BuildOptions
+    public partial class BuildOptions
     {
-        public DirectoryInfo InputDirectory { get; set; }
-        public DirectoryInfo OutputDirectory { get; set; }
-        public DirectoryInfo CoreRootDirectory { get; set; }
-        public FileInfo Crossgen2Path { get; set; }
-        public bool VerifyTypeAndFieldLayout { get; set; }
-        public string TargetArch { get; set; }
-        public bool Exe { get; set; }
-        public bool NoJit { get; set; }
-        public bool NoCrossgen2 { get; set; }
-        public bool NoExe { get; set; }
-        public bool NoEtw { get; set; }
-        public bool NoCleanup { get; set; }
-        public bool Map { get; set; }
-        public bool Pdb { get; set; }
-
-        public bool Perfmap { get; set; }
-        public int PerfmapFormatVersion { get; set; } = 1;
-        public FileInfo PackageList { get; set; }
-        public int DegreeOfParallelism { get; set; }
-        public bool Sequential { get; set; }
-        public int Iterations { get; set; } = 1;
-        public bool Framework { get; set; }
-        public bool UseFramework { get; set; }
-        public bool Release { get; set; }
-        public bool LargeBubble { get; set; }
-        public bool Composite { get; set; }
-        public int Crossgen2Parallelism { get; set; }
-        public FileInfo Crossgen2JitPath { get; set; }
-        public int CompilationTimeoutMinutes { get; set; }
-        public int ExecutionTimeoutMinutes { get; set; }
-        public DirectoryInfo[] ReferencePath { get; set; }
-        public FileInfo[] IssuesPath { get; set; }
-        public FileInfo R2RDumpPath { get; set; }
-        public FileInfo CrossgenResponseFile { get; set; }
-        public DirectoryInfo[] RewriteOldPath { get; set; }
-        public DirectoryInfo[] RewriteNewPath { get; set; }
-        public DirectoryInfo AspNetPath { get; set; }
-        public bool MeasurePerf { get; set; }
-        public string InputFileSearchString { get; set; }
         public string ConfigurationSuffix => (Release ? "-ret.out" : "-chk.out");
-        public string GCStress { get; set; }
-        public FileInfo[] MibcPath { get; set; }
+
         public string DotNetCli
         {
             get
index 404aa63..79865a1 100644 (file)
@@ -5,316 +5,395 @@ using System;
 using System.CommandLine;
 using System.CommandLine.Builder;
 using System.CommandLine.Invocation;
+using System.CommandLine.Parsing;
 using System.IO;
 
 namespace R2RTest
 {
-    internal static class CommandLineOptions
+    public class R2RTestRootCommand : RootCommand
     {
-        public static CommandLineBuilder Build()
+        void CreateCommand(string name, string description, Option[] options, Func<BuildOptions, int> action)
         {
-            var parser = new CommandLineBuilder()
-                .AddCommand(CompileFolder())
-                .AddCommand(CompileSubtree())
-                .AddCommand(CompileFramework())
-                .AddCommand(CompileNugetPackages())
-                .AddCommand(CompileSerp());
-
-            return parser;
-
-            Command CreateCommand(string name, string description, Option[] options, Func<BuildOptions, int> action)
-            {
-                Command command = new Command(name, description);
-                foreach (var option in GetCommonOptions())
-                    command.AddOption(option);
-                foreach (var option in options)
-                    command.AddOption(option);
-                command.Handler = CommandHandler.Create<BuildOptions>(action);
-                return command;
-            }
-
-            Option[] GetCommonOptions() => new[] { CoreRootDirectory(), DotNetCli() };
-
-            Command CompileFolder() =>
-                CreateCommand("compile-directory", "Compile all assemblies in directory",
-                    new Option[]
-                    {
-                        InputDirectory(),
-                        OutputDirectory(),
-                        Crossgen2Path(),
-                        TargetArch(),
-                        VerifyTypeAndFieldLayout(),
-                        NoJit(),
-                        NoCrossgen2(),
-                        Exe(),
-                        NoExe(),
-                        NoEtw(),
-                        NoCleanup(),
-                        Map(),
-                        Pdb(),
-                        Perfmap(),
-                        PerfmapFormatVersion(),
-                        DegreeOfParallelism(),
-                        Sequential(),
-                        Iterations(),
-                        Framework(),
-                        UseFramework(),
-                        Release(),
-                        LargeBubble(),
-                        Composite(),
-                        Crossgen2Parallelism(),
-                        Crossgen2JitPath(),
-                        ReferencePath(),
-                        IssuesPath(),
-                        CompilationTimeoutMinutes(),
-                        ExecutionTimeoutMinutes(),
-                        R2RDumpPath(),
-                        MeasurePerf(),
-                        InputFileSearchString(),
-                        MibcPath(),
-                    },
-                    CompileDirectoryCommand.CompileDirectory);
-
-            Command CompileSubtree() =>
-                CreateCommand("compile-subtree", "Build each directory in a given subtree containing any managed assemblies as a separate app",
-                    new Option[]
-                    {
-                        InputDirectory(),
-                        OutputDirectory(),
-                        Crossgen2Path(),
-                        TargetArch(),
-                        VerifyTypeAndFieldLayout(),
-                        NoJit(),
-                        NoCrossgen2(),
-                        Exe(),
-                        NoExe(),
-                        NoEtw(),
-                        NoCleanup(),
-                        Map(),
-                        Pdb(),
-                        Perfmap(),
-                        PerfmapFormatVersion(),
-                        DegreeOfParallelism(),
-                        Sequential(),
-                        Iterations(),
-                        Framework(),
-                        UseFramework(),
-                        Release(),
-                        LargeBubble(),
-                        Composite(),
-                        Crossgen2Parallelism(),
-                        Crossgen2JitPath(),
-                        ReferencePath(),
-                        IssuesPath(),
-                        CompilationTimeoutMinutes(),
-                        ExecutionTimeoutMinutes(),
-                        R2RDumpPath(),
-                        GCStress(),
-                        MibcPath(),
-                    },
-                    CompileSubtreeCommand.CompileSubtree);
-
-            Command CompileFramework() =>
-                CreateCommand("compile-framework", "Compile managed framework assemblies in Core_Root",
-                    new Option[]
-                    {
-                        Crossgen2Path(),
-                        TargetArch(),
-                        VerifyTypeAndFieldLayout(),
-                        NoCrossgen2(),
-                        NoCleanup(),
-                        Map(),
-                        Pdb(),
-                        Perfmap(),
-                        PerfmapFormatVersion(),
-                        Crossgen2Parallelism(),
-                        Crossgen2JitPath(),
-                        DegreeOfParallelism(),
-                        Sequential(),
-                        Iterations(),
-                        Release(),
-                        LargeBubble(),
-                        Composite(),
-                        ReferencePath(),
-                        IssuesPath(),
-                        CompilationTimeoutMinutes(),
-                        R2RDumpPath(),
-                        MeasurePerf(),
-                        InputFileSearchString(),
-                        OutputDirectory(),
-                        MibcPath(),
-                    },
-                    CompileFrameworkCommand.CompileFramework);
-
-            Command CompileNugetPackages() =>
-                CreateCommand("compile-nuget", "Restore a list of Nuget packages into an empty console app, publish, and optimize with Crossgen / CPAOT",
-                    new Option[]
-                    {
-                        R2RDumpPath(),
-                        InputDirectory(),
-                        OutputDirectory(),
-                        PackageList(),
-                        NoCleanup(),
-                        Map(),
-                        Pdb(),
-                        Perfmap(),
-                        PerfmapFormatVersion(),
-                        DegreeOfParallelism(),
-                        CompilationTimeoutMinutes(),
-                        ExecutionTimeoutMinutes(),
-                        MibcPath(),
-                    },
-                    CompileNugetCommand.CompileNuget);
-
-            Command CompileSerp() =>
-                CreateCommand("compile-serp", "Compile existing application",
-                    new Option[]
-                    {
-                        InputDirectory(),
-                        DegreeOfParallelism(),
-                        AspNetPath(),
-                        Composite(),
-                        Map(),
-                        Pdb(),
-                        Perfmap(),
-                        PerfmapFormatVersion(),
-                        CompilationTimeoutMinutes(),
-                        Crossgen2Path(),
-                        MibcPath(),
-                    },
-                    options =>
-                    {
-                        var compileSerp = new CompileSerpCommand(options);
-                        return compileSerp.CompileSerpAssemblies();
-                    });
-
-            // Todo: Input / Output directories should be required arguments to the command when they're made available to handlers
-            // https://github.com/dotnet/command-line-api/issues/297
-            Option InputDirectory() =>
-                new Option<DirectoryInfo>(new[] { "--input-directory", "-in" }, "Folder containing assemblies to optimize").ExistingOnly();
-
-            Option OutputDirectory() =>
-                new Option<DirectoryInfo>(new[] { "--output-directory", "-out" }, "Folder to emit compiled assemblies").LegalFilePathsOnly();
-
-            Option CoreRootDirectory() =>
-                new Option<DirectoryInfo>(new[] { "--core-root-directory", "-cr" }, "Location of the CoreCLR CORE_ROOT folder")
+            Command command = new Command(name, description);
+            foreach (var option in GetCommonOptions())
+                command.AddOption(option);
+            foreach (var option in options)
+                command.AddOption(option);
+            command.SetHandler<InvocationContext>((InvocationContext context) =>
+                context.ExitCode = action(new BuildOptions(this, context.ParseResult)));
+            AddCommand(command);
+        }
+
+        Option[] GetCommonOptions() => new Option[] { CoreRootDirectory, DotNetCli };
+
+        R2RTestRootCommand()
+        {
+            CreateCommand("compile-directory", "Compile all assemblies in directory",
+                new Option[]
+                {
+                    InputDirectory,
+                    OutputDirectory,
+                    Crossgen2Path,
+                    TargetArch,
+                    VerifyTypeAndFieldLayout,
+                    NoJit,
+                    NoCrossgen2,
+                    Exe,
+                    NoExe,
+                    NoEtw,
+                    NoCleanup,
+                    Map,
+                    Pdb,
+                    Perfmap,
+                    PerfmapFormatVersion,
+                    DegreeOfParallelism,
+                    Sequential,
+                    Iterations,
+                    Framework,
+                    UseFramework,
+                    Release,
+                    LargeBubble,
+                    Composite,
+                    Crossgen2Parallelism,
+                    Crossgen2JitPath,
+                    ReferencePath,
+                    IssuesPath,
+                    CompilationTimeoutMinutes,
+                    ExecutionTimeoutMinutes,
+                    R2RDumpPath,
+                    MeasurePerf,
+                    InputFileSearchString,
+                    MibcPath,
+                },
+                CompileDirectoryCommand.CompileDirectory);
+
+            CreateCommand("compile-subtree", "Build each directory in a given subtree containing any managed assemblies as a separate app",
+                new Option[]
+                {
+                    InputDirectory,
+                    OutputDirectory,
+                    Crossgen2Path,
+                    TargetArch,
+                    VerifyTypeAndFieldLayout,
+                    NoJit,
+                    NoCrossgen2,
+                    Exe,
+                    NoExe,
+                    NoEtw,
+                    NoCleanup,
+                    Map,
+                    Pdb,
+                    Perfmap,
+                    PerfmapFormatVersion,
+                    DegreeOfParallelism,
+                    Sequential,
+                    Iterations,
+                    Framework,
+                    UseFramework,
+                    Release,
+                    LargeBubble,
+                    Composite,
+                    Crossgen2Parallelism,
+                    Crossgen2JitPath,
+                    ReferencePath,
+                    IssuesPath,
+                    CompilationTimeoutMinutes,
+                    ExecutionTimeoutMinutes,
+                    R2RDumpPath,
+                    GCStress,
+                    MibcPath,
+                },
+                CompileSubtreeCommand.CompileSubtree);
+
+            CreateCommand("compile-framework", "Compile managed framework assemblies in Core_Root",
+                new Option[]
                 {
-                    Required = true
-                }.ExistingOnly();
+                    Crossgen2Path,
+                    TargetArch,
+                    VerifyTypeAndFieldLayout,
+                    NoCrossgen2,
+                    NoCleanup,
+                    Map,
+                    Pdb,
+                    Perfmap,
+                    PerfmapFormatVersion,
+                    Crossgen2Parallelism,
+                    Crossgen2JitPath,
+                    DegreeOfParallelism,
+                    Sequential,
+                    Iterations,
+                    Release,
+                    LargeBubble,
+                    Composite,
+                    ReferencePath,
+                    IssuesPath,
+                    CompilationTimeoutMinutes,
+                    R2RDumpPath,
+                    MeasurePerf,
+                    InputFileSearchString,
+                    OutputDirectory,
+                    MibcPath,
+                },
+                CompileFrameworkCommand.CompileFramework);
+
+            CreateCommand("compile-nuget", "Restore a list of Nuget packages into an empty console app, publish, and optimize with Crossgen / CPAOT",
+                new Option[]
+                {
+                    R2RDumpPath,
+                    InputDirectory,
+                    OutputDirectory,
+                    PackageList,
+                    NoCleanup,
+                    Map,
+                    Pdb,
+                    Perfmap,
+                    PerfmapFormatVersion,
+                    DegreeOfParallelism,
+                    CompilationTimeoutMinutes,
+                    ExecutionTimeoutMinutes,
+                    MibcPath,
+                },
+                CompileNugetCommand.CompileNuget);
+
+            CreateCommand("compile-serp", "Compile existing application",
+                new Option[]
+                {
+                    InputDirectory,
+                    DegreeOfParallelism,
+                    AspNetPath,
+                    Composite,
+                    Map,
+                    Pdb,
+                    Perfmap,
+                    PerfmapFormatVersion,
+                    CompilationTimeoutMinutes,
+                    Crossgen2Path,
+                    MibcPath,
+                },
+                options =>
+                {
+                    var compileSerp = new CompileSerpCommand(options);
+                    return compileSerp.CompileSerpAssemblies();
+                });
+        }
+
+        // Todo: Input / Output directories should be required arguments to the command when they're made available to handlers
+        // https://github.com/dotnet/command-line-api/issues/297
+        public Option<DirectoryInfo> InputDirectory { get; } =
+            new Option<DirectoryInfo>(new[] { "--input-directory", "-in" }, "Folder containing assemblies to optimize").ExistingOnly();
 
-            Option ReferencePath() =>
-                new Option<DirectoryInfo[]>(new[] { "--reference-path", "-r" }, "Folder containing assemblies to reference during compilation")
-                { Argument = new Argument<DirectoryInfo[]>() { Arity = ArgumentArity.ZeroOrMore }.ExistingOnly() };
+        public Option<DirectoryInfo> OutputDirectory { get; } =
+            new Option<DirectoryInfo>(new[] { "--output-directory", "-out" }, "Folder to emit compiled assemblies").LegalFilePathsOnly();
 
-            Option MibcPath() =>
-                new Option<FileInfo[]>(new[] { "--mibc-path", "-m" }, "Mibc files to use in compilation")
-                { Argument = new Argument<FileInfo[]>() { Arity = ArgumentArity.ZeroOrMore }.ExistingOnly() };
+        public Option<DirectoryInfo> CoreRootDirectory { get; } =
+            new Option<DirectoryInfo>(new[] { "--core-root-directory", "-cr" }, "Location of the CoreCLR CORE_ROOT folder")
+            { Arity = ArgumentArity.ExactlyOne }.ExistingOnly();
 
-            Option Crossgen2Path() =>
-                new Option<FileInfo>(new[] { "--crossgen2-path", "-c2p" }, "Explicit Crossgen2 path (useful for cross-targeting)").ExistingOnly();
+        public Option<DirectoryInfo[]> ReferencePath { get; } =
+            new Option<DirectoryInfo[]>(new[] { "--reference-path", "-r" }, "Folder containing assemblies to reference during compilation")
+            { Arity = ArgumentArity.ZeroOrMore }.ExistingOnly();
 
-            Option VerifyTypeAndFieldLayout() =>
-                new Option<bool>(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 Option<FileInfo[]> MibcPath { get; } =
+            new Option<FileInfo[]>(new[] { "--mibc-path", "-m" }, "Mibc files to use in compilation")
+            { Arity = ArgumentArity.ZeroOrMore }.ExistingOnly();
 
-            Option NoJit() =>
-                new Option<bool>(new[] { "--nojit" }, "Don't run tests in JITted mode");
+        public Option<FileInfo> Crossgen2Path { get; } =
+            new Option<FileInfo>(new[] { "--crossgen2-path", "-c2p" }, "Explicit Crossgen2 path (useful for cross-targeting)").ExistingOnly();
 
-            Option NoCrossgen2() =>
-                new Option<bool>(new[] { "--nocrossgen2" }, "Don't run tests in Crossgen2 mode");
+        public Option<bool> 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.");
 
-            Option Exe() =>
-                new Option<bool>(new[] { "--exe" }, "Don't compile tests, just execute them");
+        public Option<bool> NoJit { get; } =
+            new(new[] { "--nojit" }, "Don't run tests in JITted mode");
 
-            Option NoExe() =>
-                new Option<bool>(new[] { "--noexe" }, "Compilation-only mode (don't execute the built apps)");
+        public Option<bool> NoCrossgen2 { get; } =
+            new(new[] { "--nocrossgen2" }, "Don't run tests in Crossgen2 mode");
 
-            Option NoEtw() =>
-                new Option<bool>(new[] { "--noetw" }, "Don't capture jitted methods using ETW");
+        public Option<bool> Exe { get; } =
+            new(new[] { "--exe" }, "Don't compile tests, just execute them");
 
-            Option NoCleanup() =>
-                new Option<bool>(new[] { "--nocleanup" }, "Don't clean up compilation artifacts after test runs");
+        public Option<bool> NoExe { get; } =
+            new(new[] { "--noexe" }, "Compilation-only mode (don't execute the built apps)");
 
-            Option Map() =>
-                new Option<bool>(new[] { "--map" }, "Generate a map file (Crossgen2)");
+        public Option<bool> NoEtw { get; } =
+            new(new[] { "--noetw" }, "Don't capture jitted methods using ETW");
 
-            Option Pdb() =>
-                new Option<bool>(new[] { "--pdb" }, "Generate PDB symbol information (Crossgen2 / Windows only)");
+        public Option<bool> NoCleanup { get; } =
+            new(new[] { "--nocleanup" }, "Don't clean up compilation artifacts after test runs");
 
-            Option Perfmap() =>
-                new Option<bool>(new[] { "--perfmap" }, "Generate perfmap symbol information");
+        public Option<bool> Map { get; } =
+            new(new[] { "--map" }, "Generate a map file (Crossgen2)");
 
-            Option PerfmapFormatVersion() =>
-                new Option<int>(new[] { "--perfmap-format-version" }, "Perfmap format version to generate");
+        public Option<bool> Pdb { get; } =
+            new(new[] { "--pdb" }, "Generate PDB symbol information (Crossgen2 / Windows only)");
 
-            Option DegreeOfParallelism() =>
-                new Option<int>(new[] { "--degree-of-parallelism", "-dop" }, "Override default compilation / execution DOP (default = logical processor count)");
+        public Option<bool> Perfmap { get; } =
+            new(new[] { "--perfmap" }, "Generate perfmap symbol information");
 
-            Option Sequential() =>
-                new Option<bool>(new[] { "--sequential" }, "Run tests sequentially");
+        public Option<int> PerfmapFormatVersion { get; } =
+            new(new[] { "--perfmap-format-version" }, () => 1, "Perfmap format version to generate");
 
-            Option Iterations() =>
-                new Option<int>(new[] { "--iterations" }, "Number of iterations for each test execution");
+        public Option<int> DegreeOfParallelism { get; } =
+            new(new[] { "--degree-of-parallelism", "-dop" }, "Override default compilation / execution DOP (default = logical processor count)");
 
-            Option Framework() =>
-                new Option<bool>(new[] { "--framework" }, "Precompile and use native framework");
+        public Option<bool> Sequential { get; } =
+            new(new[] { "--sequential" }, "Run tests sequentially");
 
-            Option UseFramework() =>
-                new Option<bool>(new[] { "--use-framework" }, "Use native framework (don't precompile, assume previously compiled)");
+        public Option<int> Iterations { get; } =
+            new(new[] { "--iterations" }, () => 1, "Number of iterations for each test execution");
 
-            Option Release() =>
-                new Option<bool>(new[] { "--release" }, "Build the tests in release mode");
+        public Option<bool> Framework { get; } =
+            new(new[] { "--framework" }, "Precompile and use native framework");
 
-            Option LargeBubble() =>
-                new Option<bool>(new[] { "--large-bubble" }, "Assume all input files as part of one version bubble");
+        public Option<bool> UseFramework { get; } =
+            new(new[] { "--use-framework" }, "Use native framework (don't precompile, assume previously compiled)");
 
-            Option Composite() =>
-                new Option<bool>(new[] { "--composite" }, "Compile tests in composite R2R mode");
+        public Option<bool> Release { get; } =
+            new(new[] { "--release" }, "Build the tests in release mode");
 
-            Option Crossgen2Parallelism() =>
-                new Option<int>(new[] { "--crossgen2-parallelism" }, "Max number of threads to use in Crossgen2 (default = logical processor count)");
-            
-            Option Crossgen2JitPath() =>
-                new Option<FileInfo>(new[] { "--crossgen2-jitpath" }, "Jit path to use for crossgen2");
+        public Option<bool> LargeBubble { get; } =
+            new(new[] { "--large-bubble" }, "Assume all input files as part of one version bubble");
 
-            Option IssuesPath() =>
-                new Option<FileInfo[]>(new[] { "--issues-path", "-ip" }, "Path to issues.targets")
-                    { Argument = new Argument<FileInfo[]>() { Arity = ArgumentArity.ZeroOrMore } };
+        public Option<bool> Composite { get; } =
+            new(new[] { "--composite" }, "Compile tests in composite R2R mode");
 
-            Option CompilationTimeoutMinutes() =>
-                new Option<int>(new[] { "--compilation-timeout-minutes", "-ct" }, "Compilation timeout (minutes)");
+        public Option<int> Crossgen2Parallelism { get; } =
+            new(new[] { "--crossgen2-parallelism" }, "Max number of threads to use in Crossgen2 (default = logical processor count)");
 
-            Option ExecutionTimeoutMinutes() =>
-                new Option<int>(new[] { "--execution-timeout-minutes", "-et" }, "Execution timeout (minutes)");
+        public Option<FileInfo> Crossgen2JitPath { get; } =
+            new(new[] { "--crossgen2-jitpath" }, "Jit path to use for crossgen2");
 
-            Option R2RDumpPath() =>
-                new Option<FileInfo>(new[] { "--r2r-dump-path" }, "Path to R2RDump.exe/dll").ExistingOnly();
+        public Option<FileInfo[]> IssuesPath { get; } =
+            new Option<FileInfo[]>(new[] { "--issues-path", "-ip" }, "Path to issues.targets")
+                { Arity = ArgumentArity.ZeroOrMore };
 
-            Option MeasurePerf() =>
-                new Option<bool>(new[] { "--measure-perf" }, "Print out compilation time");
+        public Option<int> CompilationTimeoutMinutes { get; } =
+            new(new[] { "--compilation-timeout-minutes", "-ct" }, "Compilation timeout (minutes)");
 
-            Option InputFileSearchString() =>
-                new Option<string>(new[] { "--input-file-search-string", "-input-file" }, "Search string for input files in the input directory");
+        public Option<int> ExecutionTimeoutMinutes { get; } =
+            new(new[] { "--execution-timeout-minutes", "-et" }, "Execution timeout (minutes)");
 
-            Option GCStress() =>
-                new Option<string>(new[] { "--gcstress" }, "Run tests with the specified GC stress level enabled (the argument value is in hex)");
+        public Option<FileInfo> R2RDumpPath { get; } =
+            new Option<FileInfo>(new[] { "--r2r-dump-path" }, "Path to R2RDump.exe/dll").ExistingOnly();
 
-            Option DotNetCli() =>
-                new Option<string>(new [] { "--dotnet-cli", "-cli" }, "For dev box testing, point at .NET 5 dotnet.exe or <repo>/dotnet.cmd.");
+        public Option<bool> MeasurePerf { get; } =
+            new(new[] { "--measure-perf" }, "Print out compilation time");
 
-            Option TargetArch() =>
-                new Option<string>(new[] { "--target-arch" }, "Target architecture for crossgen2");
+        public Option<string> InputFileSearchString { get; } =
+            new(new[] { "--input-file-search-string", "-input-file" }, "Search string for input files in the input directory");
 
-            //
-            // compile-nuget specific options
-            //
-            Option PackageList() =>
-                new Option<FileInfo>(new[] { "--package-list", "-pl" }, "Text file containing a package name on each line").ExistingOnly();
+        public Option<string> GCStress { get; } =
+            new(new[] { "--gcstress" }, "Run tests with the specified GC stress level enabled (the argument value is in hex)");
 
-            //
-            // compile-serp specific options
-            //
-            Option AspNetPath() =>
-                new Option<DirectoryInfo>(new[] { "--asp-net-path", "-asp" }, "Path to SERP's ASP.NET Core folder").ExistingOnly();
+        public Option<string> DotNetCli { get; } =
+            new(new [] { "--dotnet-cli", "-cli" }, "For dev box testing, point at .NET 5 dotnet.exe or <repo>/dotnet.cmd.");
+
+        public Option<string> TargetArch { get; } =
+            new(new[] { "--target-arch" }, "Target architecture for crossgen2");
+
+        //
+        // compile-nuget specific options
+        //
+        public Option<FileInfo> PackageList { get; } =
+            new Option<FileInfo>(new[] { "--package-list", "-pl" }, "Text file containing a package name on each line").ExistingOnly();
+
+        //
+        // compile-serp specific options
+        //
+        public Option<DirectoryInfo> AspNetPath { get; } =
+            new Option<DirectoryInfo>(new[] { "--asp-net-path", "-asp" }, "Path to SERP's ASP.NET Core folder").ExistingOnly();
+
+        static int Main(string[] args)
+        {
+            return new CommandLineBuilder(new R2RTestRootCommand())
+                .UseHelp()
+                .UseParseErrorReporting()
+                .Build()
+                .Invoke(args);
         }
     }
+
+    public partial class BuildOptions
+    {
+        public BuildOptions(R2RTestRootCommand cmd, ParseResult res)
+        {
+            InputDirectory = res.GetValueForOption(cmd.InputDirectory);
+            OutputDirectory = res.GetValueForOption(cmd.OutputDirectory);
+            CoreRootDirectory = res.GetValueForOption(cmd.CoreRootDirectory);
+            Crossgen2Path = res.GetValueForOption(cmd.Crossgen2Path);
+            VerifyTypeAndFieldLayout = res.GetValueForOption(cmd.VerifyTypeAndFieldLayout);
+            TargetArch = res.GetValueForOption(cmd.TargetArch);
+            Exe = res.GetValueForOption(cmd.Exe);
+            NoJit = res.GetValueForOption(cmd.NoJit);
+            NoCrossgen2 = res.GetValueForOption(cmd.NoCrossgen2);
+            NoExe = res.GetValueForOption(cmd.NoExe);
+            NoEtw = res.GetValueForOption(cmd.NoEtw);
+            NoCleanup = res.GetValueForOption(cmd.NoCleanup);
+            Map = res.GetValueForOption(cmd.Map);
+            Pdb = res.GetValueForOption(cmd.Pdb);
+
+            Perfmap = res.GetValueForOption(cmd.Perfmap);
+            PerfmapFormatVersion = res.GetValueForOption(cmd.PerfmapFormatVersion);
+            PackageList = res.GetValueForOption(cmd.PackageList);
+            DegreeOfParallelism = res.GetValueForOption(cmd.DegreeOfParallelism);
+            Sequential = res.GetValueForOption(cmd.Sequential);
+            Iterations = res.GetValueForOption(cmd.Iterations);
+            Framework = res.GetValueForOption(cmd.Framework);
+            UseFramework = res.GetValueForOption(cmd.UseFramework);
+            Release = res.GetValueForOption(cmd.Release);
+            LargeBubble = res.GetValueForOption(cmd.LargeBubble);
+            Composite = res.GetValueForOption(cmd.Composite);
+            Crossgen2Parallelism = res.GetValueForOption(cmd.Crossgen2Parallelism);
+            Crossgen2JitPath = res.GetValueForOption(cmd.Crossgen2JitPath);
+            CompilationTimeoutMinutes = res.GetValueForOption(cmd.CompilationTimeoutMinutes);
+            ExecutionTimeoutMinutes = res.GetValueForOption(cmd.ExecutionTimeoutMinutes);
+            ReferencePath = res.GetValueForOption(cmd.ReferencePath);
+            IssuesPath = res.GetValueForOption(cmd.IssuesPath);
+            R2RDumpPath = res.GetValueForOption(cmd.R2RDumpPath);
+            AspNetPath = res.GetValueForOption(cmd.AspNetPath);
+            MeasurePerf = res.GetValueForOption(cmd.MeasurePerf);
+            InputFileSearchString = res.GetValueForOption(cmd.InputFileSearchString);
+            GCStress = res.GetValueForOption(cmd.GCStress);
+            MibcPath = res.GetValueForOption(cmd.MibcPath);
+        }
+
+        public DirectoryInfo InputDirectory { get; set; }
+        public DirectoryInfo OutputDirectory { get; set; }
+        public DirectoryInfo CoreRootDirectory { get; }
+        public FileInfo Crossgen2Path { get; }
+        public bool VerifyTypeAndFieldLayout { get; }
+        public string TargetArch { get; }
+        public bool Exe { get; }
+        public bool NoJit { get; set; }
+        public bool NoCrossgen2 { get; }
+        public bool NoExe { get; set; }
+        public bool NoEtw { get; set; }
+        public bool NoCleanup { get; }
+        public bool Map { get; }
+        public bool Pdb { get; }
+
+        public bool Perfmap { get; }
+        public int PerfmapFormatVersion { get; }
+        public FileInfo PackageList { get; }
+        public int DegreeOfParallelism { get; set; }
+        public bool Sequential { get; }
+        public int Iterations { get; }
+        public bool Framework { get; set; }
+        public bool UseFramework { get; }
+        public bool Release { get; set; }
+        public bool LargeBubble { get; }
+        public bool Composite { get; }
+        public int Crossgen2Parallelism { get; }
+        public FileInfo Crossgen2JitPath { get; }
+        public int CompilationTimeoutMinutes { get; }
+        public int ExecutionTimeoutMinutes { get; }
+        public DirectoryInfo[] ReferencePath { get; }
+        public FileInfo[] IssuesPath { get; }
+        public FileInfo R2RDumpPath { get; }
+        public DirectoryInfo AspNetPath { get; }
+        public bool MeasurePerf { get; }
+        public string InputFileSearchString { get; }
+        public string GCStress { get; }
+        public FileInfo[] MibcPath { get; }
+    }
 }
diff --git a/src/coreclr/tools/r2rtest/Program.cs b/src/coreclr/tools/r2rtest/Program.cs
deleted file mode 100644 (file)
index 86cb14e..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System;
-using System.CommandLine;
-using System.CommandLine.Builder;
-using System.CommandLine.Parsing;
-using System.Threading.Tasks;
-
-namespace R2RTest
-{
-    class Program
-    {
-        static async Task<int> Main(string[] args)
-        {
-            var parser = CommandLineOptions.Build().UseDefaults().Build();
-
-            return await parser.InvokeAsync(args);
-        }
-    }
-}