--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.CommandLine;
+using System.IO;
+
+namespace ILCompiler
+{
+ public class CommandLineOptions
+ {
+ public FileInfo[] InputFilePaths { get; set; }
+ public string[] Reference { get; set; }
+ public FileInfo OutputFilePath { get; set; }
+ public bool Optimize { get; set; }
+ public bool OptimizeSpace { get; set; }
+ public bool OptimizeTime { get; set; }
+ public bool InputBubble { get; set; }
+ public bool CompileBubbleGenerics { get; set; }
+ public bool Verbose { get; set; }
+
+ public FileInfo DgmlLogFileName { get; set; }
+ public bool GenerateFullDgmlLog { get; set; }
+
+ public string TargetArch { get; set; }
+ public string TargetOS { get; set; }
+ public string JitPath { get; set; }
+ public string SystemModule { get; set; }
+ public bool WaitForDebugger { get; set; }
+ public bool Tuning { get; set; }
+ public bool Partial { get; set; }
+ public bool Resilient { get; set; }
+
+ public string SingleMethodTypeName { get; set; }
+ public string SingleMethodName { get; set; }
+ public string[] SingleMethodGenericArgs { get; set; }
+
+ public string[] CodegenOptions { get; set; }
+
+ public static Command RootCommand()
+ {
+ // For some reason, arity caps at 255 by default
+ ArgumentArity arbitraryArity = new ArgumentArity(0, 100000);
+
+ return new Command("Crossgen2Compilation")
+ {
+ new Argument<FileInfo[]>()
+ {
+ Name = "input-file-paths",
+ Description = "Input file(s) to compile",
+ Arity = arbitraryArity,
+ },
+ new Option(new[] { "--reference", "-r" }, "Reference file(s) for compilation")
+ {
+ Argument = new Argument<string[]>()
+ {
+ Arity = arbitraryArity
+ }
+ },
+ new Option(new[] { "--outputfilepath", "--out", "-o" }, "Output file path")
+ {
+ Argument = new Argument<FileInfo>()
+ },
+ new Option(new[] { "--optimize", "-O" }, "Enable optimizations")
+ {
+ Argument = new Argument<bool>()
+ },
+ new Option(new[] { "--optimize-space", "--Os" }, "Enable optimizations, favor code space")
+ {
+ Argument = new Argument<bool>()
+ },
+ new Option(new[] { "--optimize-time", "--Ot" }, "Enable optimizations, favor code speed"),
+ new Option(new[] { "--inputbubble" }, "True when the entire input forms a version bubble (default = per-assembly bubble)"),
+ new Option(new[] { "--tuning" }, "Generate IBC tuning image")
+ {
+ Argument = new Argument<bool>()
+ },
+ new Option(new[] { "--partial" }, "Generate partial image driven by profile")
+ {
+ Argument = new Argument<bool>()
+ },
+ new Option(new[] { "--compilebubblegenerics" }, "Compile instantiations from reference modules used in the current module")
+ {
+ Argument = new Argument<bool>()
+ },
+ new Option(new[] { "--dgml-log-file-name", "--dmgllog" }, "Save result of dependency analysis as DGML")
+ {
+ Argument = new Argument<FileInfo>()
+ },
+ new Option(new[] { "--generate-full-dmgl-log", "--fulllog" }, "Save detailed log of dependency analysis")
+ {
+ Argument = new Argument<bool>()
+ },
+ new Option(new[] { "--verbose" }, "Enable verbose logging")
+ {
+ Argument = new Argument<bool>()
+ },
+ new Option(new[] { "--systemmodule" }, "System module name (default: System.Private.CoreLib)")
+ {
+ Argument = new Argument<string>()
+ },
+ new Option(new[] { "--waitfordebugger" }, "Pause to give opportunity to attach debugger")
+ {
+ Argument = new Argument<bool>()
+ },
+ new Option(new[] { "--codegen-options", "--codegenopt" }, "Define a codegen option")
+ {
+ Argument = new Argument<string[]>()
+ {
+ Arity = arbitraryArity
+ }
+ },
+ new Option(new[] { "--resilient" }, "Disable behavior where unexpected compilation failures cause overall compilation failure")
+ {
+ Argument = new Argument<bool>()
+ },
+ new Option(new[] { "--targetarch" }, "Target architecture for cross compilation")
+ {
+ Argument = new Argument<string>()
+ },
+ new Option(new[] { "--targetos" }, "Target OS for cross compilation")
+ {
+ Argument = new Argument<string>()
+ },
+ new Option(new[] { "--jitpath" }, "Path to JIT compiler library")
+ {
+ Argument = new Argument<string>()
+ },
+ new Option(new[] { "--singlemethodtypename" }, "Single method compilation: name of the owning type")
+ {
+ Argument = new Argument<string>()
+ },
+ new Option(new[] { "--singlemethodname" }, "Single method compilation: generic arguments to the method")
+ {
+ Argument = new Argument<string>()
+ },
+ new Option(new[] { "--singlemethodgenericarg" }, "Single method compilation: generic arguments to the method")
+ {
+ // We don't need to override arity here as 255 is the maximum number of generic arguments
+ Argument = new Argument<string[]>()
+ },
+ };
+ }
+ }
+}
using System;
using System.Collections.Generic;
-using System.Reflection;
using System.CommandLine;
+using System.CommandLine.Invocation;
+using System.Reflection;
using System.Runtime.InteropServices;
+using System.Threading.Tasks;
using Internal.IL;
using Internal.TypeSystem;
using Internal.TypeSystem.Ecma;
using Internal.CommandLine;
+using System.Linq;
+using System.IO;
namespace ILCompiler
{
{
private const string DefaultSystemModule = "System.Private.CoreLib";
+ private CommandLineOptions _commandLineOptions;
+ public TargetOS _targetOS;
+ public TargetArchitecture _targetArchitecture;
+ public OptimizationMode _optimizationMode;
private Dictionary<string, string> _inputFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
private Dictionary<string, string> _referenceFilePaths = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- private string _outputFilePath;
- private bool _isInputVersionBubble;
- private bool _includeGenericsFromVersionBubble;
- private bool _isVerbose;
-
- private string _dgmlLogFileName;
- private bool _generateFullDgmlLog;
-
- private TargetArchitecture _targetArchitecture;
- private string _targetArchitectureStr;
- private TargetOS _targetOS;
- private string _targetOSStr;
- private string _jitPath;
- private OptimizationMode _optimizationMode;
- private string _systemModuleName = DefaultSystemModule;
- private bool _tuning;
- private bool _partial;
- private bool _resilient;
-
- private string _singleMethodTypeName;
- private string _singleMethodName;
- private IReadOnlyList<string> _singleMethodGenericArgs;
-
- private IReadOnlyList<string> _codegenOptions = Array.Empty<string>();
-
- private bool _help;
-
- private Program()
+ private Program(CommandLineOptions commandLineOptions)
{
+ _commandLineOptions = commandLineOptions;
}
private void Help(string helpText)
_targetArchitecture = TargetArchitecture.X64;
}
- private ArgumentSyntax ParseCommandLine(string[] args)
+ private void ProcessCommandLine()
{
- IReadOnlyList<string> inputFiles = Array.Empty<string>();
- IReadOnlyList<string> referenceFiles = Array.Empty<string>();
-
- bool optimize = false;
- bool optimizeSpace = false;
- bool optimizeTime = false;
-
- bool waitForDebugger = false;
AssemblyName name = typeof(Program).GetTypeInfo().Assembly.GetName();
- ArgumentSyntax argSyntax = ArgumentSyntax.Parse(args, syntax =>
- {
- syntax.ApplicationName = name.Name.ToString();
-
- // HandleHelp writes to error, fails fast with crash dialog and lacks custom formatting.
- syntax.HandleHelp = false;
- syntax.HandleErrors = true;
-
- syntax.DefineOption("h|help", ref _help, "Help message for ILC");
- syntax.DefineOptionList("r|reference", ref referenceFiles, "Reference file(s) for compilation");
- syntax.DefineOption("o|out", ref _outputFilePath, "Output file path");
- syntax.DefineOption("O", ref optimize, "Enable optimizations");
- syntax.DefineOption("Os", ref optimizeSpace, "Enable optimizations, favor code space");
- syntax.DefineOption("Ot", ref optimizeTime, "Enable optimizations, favor code speed");
- syntax.DefineOption("inputbubble", ref _isInputVersionBubble, "True when the entire input forms a version bubble (default = per-assembly bubble)");
- syntax.DefineOption("tuning", ref _tuning, "Generate IBC tuning image");
- syntax.DefineOption("partial", ref _partial, "Generate partial image driven by profile");
- syntax.DefineOption("compilebubblegenerics", ref _includeGenericsFromVersionBubble, "Compile instantiations from reference modules used in the current module");
- syntax.DefineOption("dgmllog", ref _dgmlLogFileName, "Save result of dependency analysis as DGML");
- syntax.DefineOption("fulllog", ref _generateFullDgmlLog, "Save detailed log of dependency analysis");
- syntax.DefineOption("verbose", ref _isVerbose, "Enable verbose logging");
- syntax.DefineOption("systemmodule", ref _systemModuleName, "System module name (default: System.Private.CoreLib)");
- syntax.DefineOption("waitfordebugger", ref waitForDebugger, "Pause to give opportunity to attach debugger");
- syntax.DefineOptionList("codegenopt", ref _codegenOptions, "Define a codegen option");
- syntax.DefineOption("resilient", ref _resilient, "Disable behavior where unexpected compilation failures cause overall compilation failure");
-
- syntax.DefineOption("targetarch", ref _targetArchitectureStr, "Target architecture for cross compilation");
- syntax.DefineOption("targetos", ref _targetOSStr, "Target OS for cross compilation");
- syntax.DefineOption("jitpath", ref _jitPath, "Path to JIT compiler library");
-
- syntax.DefineOption("singlemethodtypename", ref _singleMethodTypeName, "Single method compilation: name of the owning type");
- syntax.DefineOption("singlemethodname", ref _singleMethodName, "Single method compilation: name of the method");
- syntax.DefineOptionList("singlemethodgenericarg", ref _singleMethodGenericArgs, "Single method compilation: generic arguments to the method");
-
- syntax.DefineParameterList("in", ref inputFiles, "Input file(s) to compile");
- });
- if (waitForDebugger)
+
+ if (_commandLineOptions.WaitForDebugger)
{
Console.WriteLine("Waiting for debugger to attach. Press ENTER to continue");
Console.ReadLine();
}
- if (_includeGenericsFromVersionBubble)
+ if (_commandLineOptions.CompileBubbleGenerics)
{
- if (!_isInputVersionBubble)
+ if (!_commandLineOptions.InputBubble)
{
Console.WriteLine("Warning: ignoring --compilebubblegenerics because --inputbubble was not specified");
- _includeGenericsFromVersionBubble = false;
+ _commandLineOptions.CompileBubbleGenerics = false;
}
}
_optimizationMode = OptimizationMode.None;
- if (optimizeSpace)
+ if (_commandLineOptions.OptimizeSpace)
{
- if (optimizeTime)
+ if (_commandLineOptions.OptimizeTime)
Console.WriteLine("Warning: overriding -Ot with -Os");
_optimizationMode = OptimizationMode.PreferSize;
}
- else if (optimizeTime)
+ else if (_commandLineOptions.OptimizeTime)
_optimizationMode = OptimizationMode.PreferSpeed;
- else if (optimize)
+ else if (_commandLineOptions.Optimize)
_optimizationMode = OptimizationMode.Blended;
- foreach (var input in inputFiles)
- Helpers.AppendExpandedPaths(_inputFilePaths, input, true);
+ foreach (var input in _commandLineOptions.InputFilePaths ?? Enumerable.Empty<FileInfo>())
+ Helpers.AppendExpandedPaths(_inputFilePaths, input.FullName, true);
- foreach (var reference in referenceFiles)
+ foreach (var reference in _commandLineOptions.Reference ?? Enumerable.Empty<string>())
Helpers.AppendExpandedPaths(_referenceFilePaths, reference, false);
-
- return argSyntax;
}
- private int Run(string[] args)
+ private int Run()
{
InitializeDefaultOptions();
- ArgumentSyntax syntax = ParseCommandLine(args);
- if (_help)
- {
- Help(syntax.GetHelpText());
- return 1;
- }
+ ProcessCommandLine();
- if (_outputFilePath == null)
+ if (_commandLineOptions.OutputFilePath == null)
throw new CommandLineException("Output filename must be specified (/out <file>)");
//
// Set target Architecture and OS
//
- if (_targetArchitectureStr != null)
+ if (_commandLineOptions.TargetArch != null)
{
- if (_targetArchitectureStr.Equals("x86", StringComparison.OrdinalIgnoreCase))
+ if (_commandLineOptions.TargetArch.Equals("x86", StringComparison.OrdinalIgnoreCase))
_targetArchitecture = TargetArchitecture.X86;
- else if (_targetArchitectureStr.Equals("x64", StringComparison.OrdinalIgnoreCase))
+ else if (_commandLineOptions.TargetArch.Equals("x64", StringComparison.OrdinalIgnoreCase))
_targetArchitecture = TargetArchitecture.X64;
- else if (_targetArchitectureStr.Equals("arm", StringComparison.OrdinalIgnoreCase))
+ else if (_commandLineOptions.TargetArch.Equals("arm", StringComparison.OrdinalIgnoreCase))
_targetArchitecture = TargetArchitecture.ARM;
- else if (_targetArchitectureStr.Equals("armel", StringComparison.OrdinalIgnoreCase))
+ else if (_commandLineOptions.TargetArch.Equals("armel", StringComparison.OrdinalIgnoreCase))
_targetArchitecture = TargetArchitecture.ARM;
- else if (_targetArchitectureStr.Equals("arm64", StringComparison.OrdinalIgnoreCase))
+ else if (_commandLineOptions.TargetArch.Equals("arm64", StringComparison.OrdinalIgnoreCase))
_targetArchitecture = TargetArchitecture.ARM64;
else
throw new CommandLineException("Target architecture is not supported");
}
- if (_targetOSStr != null)
+ if (_commandLineOptions.TargetOS != null)
{
- if (_targetOSStr.Equals("windows", StringComparison.OrdinalIgnoreCase))
+ if (_commandLineOptions.TargetOS.Equals("windows", StringComparison.OrdinalIgnoreCase))
_targetOS = TargetOS.Windows;
- else if (_targetOSStr.Equals("linux", StringComparison.OrdinalIgnoreCase))
+ else if (_commandLineOptions.TargetOS.Equals("linux", StringComparison.OrdinalIgnoreCase))
_targetOS = TargetOS.Linux;
- else if (_targetOSStr.Equals("osx", StringComparison.OrdinalIgnoreCase))
+ else if (_commandLineOptions.TargetOS.Equals("osx", StringComparison.OrdinalIgnoreCase))
_targetOS = TargetOS.OSX;
else
throw new CommandLineException("Target OS is not supported");
typeSystemContext.InputFilePaths = inputFilePaths;
typeSystemContext.ReferenceFilePaths = _referenceFilePaths;
- typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(_systemModuleName));
+ string systemModuleName = _commandLineOptions.SystemModule ?? DefaultSystemModule;
+ typeSystemContext.SetSystemModule(typeSystemContext.GetModuleForSimpleName(systemModuleName));
if (typeSystemContext.InputFilePaths.Count == 0)
throw new CommandLineException("No input files specified");
// Single method mode?
MethodDesc singleMethod = CheckAndParseSingleMethodModeArguments(typeSystemContext);
- var logger = new Logger(Console.Out, _isVerbose);
+ var logger = new Logger(Console.Out, _commandLineOptions.Verbose);
List<ModuleDesc> referenceableModules = new List<ModuleDesc>();
foreach (var inputFile in inputFilePaths)
compilationRoots.Add(new ReadyToRunRootProvider(module, profileDataManager));
inputModules.Add(module);
- if (!_isInputVersionBubble)
+ if (!_commandLineOptions.InputBubble)
{
break;
}
List<ModuleDesc> versionBubbleModules = new List<ModuleDesc>();
- if (_isInputVersionBubble)
+ if (_commandLineOptions.InputBubble)
{
// In large version bubble mode add reference paths to the compilation group
foreach (string referenceFile in _referenceFilePaths.Values)
}
compilationGroup = new ReadyToRunSingleAssemblyCompilationModuleGroup(
- typeSystemContext, inputModules, versionBubbleModules, _includeGenericsFromVersionBubble,
- _partial ? profileDataManager : null);
+ typeSystemContext, inputModules, versionBubbleModules, _commandLineOptions.CompileBubbleGenerics,
+ _commandLineOptions.Partial ? profileDataManager : null);
}
//
break;
}
CompilationBuilder builder = new ReadyToRunCodegenCompilationBuilder(typeSystemContext, compilationGroup, inputFilePath,
- ibcTuning: _tuning,
- resilient: _resilient);
+ ibcTuning: _commandLineOptions.Tuning,
+ resilient: _commandLineOptions.Resilient);
string compilationUnitPrefix = "";
builder.UseCompilationUnitPrefix(compilationUnitPrefix);
ILProvider ilProvider = new ReadyToRunILProvider();
- DependencyTrackingLevel trackingLevel = _dgmlLogFileName == null ?
- DependencyTrackingLevel.None : (_generateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First);
+ DependencyTrackingLevel trackingLevel = _commandLineOptions.DgmlLogFileName == null ?
+ DependencyTrackingLevel.None : (_commandLineOptions.GenerateFullDgmlLog ? DependencyTrackingLevel.All : DependencyTrackingLevel.First);
builder
.UseILProvider(ilProvider)
- .UseJitPath(_jitPath)
- .UseBackendOptions(_codegenOptions)
+ .UseJitPath(_commandLineOptions.JitPath)
+ .UseBackendOptions(_commandLineOptions.CodegenOptions)
.UseLogger(logger)
.UseDependencyTracking(trackingLevel)
.UseCompilationRoots(compilationRoots)
compilation = builder.ToCompilation();
}
- compilation.Compile(_outputFilePath);
+ compilation.Compile(_commandLineOptions.OutputFilePath.FullName);
- if (_dgmlLogFileName != null)
- compilation.WriteDependencyLog(_dgmlLogFileName);
+ if (_commandLineOptions.DgmlLogFileName != null)
+ compilation.WriteDependencyLog(_commandLineOptions.DgmlLogFileName.FullName);
}
return 0;
private MethodDesc CheckAndParseSingleMethodModeArguments(CompilerTypeSystemContext context)
{
- if (_singleMethodName == null && _singleMethodTypeName == null && _singleMethodGenericArgs == null)
+ if (_commandLineOptions.SingleMethodName == null && _commandLineOptions.SingleMethodTypeName == null && _commandLineOptions.SingleMethodGenericArgs == null)
return null;
- if (_singleMethodName == null || _singleMethodTypeName == null)
+ if (_commandLineOptions.SingleMethodName == null || _commandLineOptions.SingleMethodTypeName == null)
throw new CommandLineException("Both method name and type name are required parameters for single method mode");
- TypeDesc owningType = FindType(context, _singleMethodTypeName);
+ TypeDesc owningType = FindType(context, _commandLineOptions.SingleMethodTypeName);
// TODO: allow specifying signature to distinguish overloads
- MethodDesc method = owningType.GetMethod(_singleMethodName, null);
+ MethodDesc method = owningType.GetMethod(_commandLineOptions.SingleMethodName, null);
if (method == null)
- throw new CommandLineException($"Method '{_singleMethodName}' not found in '{_singleMethodTypeName}'");
+ throw new CommandLineException($"Method '{_commandLineOptions.SingleMethodName}' not found in '{_commandLineOptions.SingleMethodTypeName}'");
- if (method.HasInstantiation != (_singleMethodGenericArgs != null) ||
- (method.HasInstantiation && (method.Instantiation.Length != _singleMethodGenericArgs.Count)))
+ if (method.HasInstantiation != (_commandLineOptions.SingleMethodGenericArgs != null) ||
+ (method.HasInstantiation && (method.Instantiation.Length != _commandLineOptions.SingleMethodGenericArgs.Length)))
{
throw new CommandLineException(
- $"Expected {method.Instantiation.Length} generic arguments for method '{_singleMethodName}' on type '{_singleMethodTypeName}'");
+ $"Expected {method.Instantiation.Length} generic arguments for method '{_commandLineOptions.SingleMethodName}' on type '{_commandLineOptions.SingleMethodTypeName}'");
}
if (method.HasInstantiation)
{
List<TypeDesc> genericArguments = new List<TypeDesc>();
- foreach (var argString in _singleMethodGenericArgs)
+ foreach (var argString in _commandLineOptions.SingleMethodGenericArgs)
genericArguments.Add(FindType(context, argString));
method = method.MakeInstantiatedMethod(genericArguments.ToArray());
}
return false;
}
- private static int Main(string[] args)
+ public static async Task<int> Main(string[] args)
+ {
+ var command = CommandLineOptions.RootCommand();
+ command.Handler = CommandHandler.Create<CommandLineOptions>((CommandLineOptions options) => InnerMain(options));
+ return await command.InvokeAsync(args);
+ }
+
+ private static int InnerMain(CommandLineOptions buildOptions)
{
#if DEBUG
try
{
- return new Program().Run(args);
+ return new Program(buildOptions).Run();
}
catch (CodeGenerationFailedException ex) when (DumpReproArguments(ex))
{
#else
try
{
- return new Program().Run(args);
+ return new Program(buildOptions).Run();
}
catch (Exception e)
{