Open JoshEbersol opened 6 years ago
McMaster.Extensions.CommandLineUtils has its own dependency injection framework. The dependency injection frameworks do not work great with CoreRT AOT compilation currently. They require rd.xml file to describe types that will be needed by the dependency injection framework at runtime. https://github.com/dotnet/corert/tree/master/samples/WebApi has an example how to add rd.xml file.
In this case, you can start by adding <Assembly Name="McMaster.Extensions.CommandLineUtils" Dynamic="Required All" />
to rd.xml. It will make everything in McMaster.Extensions.CommandLineUtils available to reflection. It is probably not necessary to add everything (doing so bloats the native binary), but it has may make things just work.
Thanks, @jkotas - that gets me a little bit further. I now fail like this:
MyProgram.exe --help
Unhandled Exception: System.TypeInitializationException: A type initializer threw an exception. To determine which type, inspect the InnerException's StackTrace property. ---> System.InvalidOperationException: Sequence contains no matching element
at System.Linq.Enumerable.Single[TSource](IEnumerable`1, Func`2) + 0x1f9
at MyProgram!<BaseAddress>+0x2c0db0
at MyProgram!<BaseAddress>+0x8d48c6
at MyProgram!<BaseAddress>+0x8d4331
--- End of inner exception stack trace ---
at MyProgram!<BaseAddress>+0x8d441d
at MyProgram!<BaseAddress>+0x8d40b6
at McMaster.Extensions.CommandLineUtils.Abstractions.ValueParserProvider.GetParser(Type) + 0x2b
at McMaster.Extensions.CommandLineUtils.Conventions.OptionAttributeConventionBase`1.AddOption(ConventionContext, CommandOption, PropertyInfo) + 0x7a2
at McMaster.Extensions.CommandLineUtils.Conventions.OptionAttributeConvention.Apply(ConventionContext) + 0x138
at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Builder.McMaster.Extensions.CommandLineUtils.Conventions.IConventionBuilder.AddConvention(IConvention) + 0x4b
at McMaster.Extensions.CommandLineUtils.ConventionBuilderExtensions.UseOptionAttributes(IConventionBuilder) + 0x40
at McMaster.Extensions.CommandLineUtils.ConventionBuilderExtensions.UseAttributes(IConventionBuilder) + 0xb8
at McMaster.Extensions.CommandLineUtils.ConventionBuilderExtensions.UseDefaultConventions(IConventionBuilder) + 0x5a
at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](CommandLineContext) + 0x177
at McMaster.Extensions.CommandLineUtils.CommandLineApplication.ExecuteAsync[TApp](CommandLineContext) + 0x31
at McMaster.Extensions.CommandLineUtils.CommandLineApplication.ExecuteAsync[TApp](IConsole, String[]) + 0x9d
at McMaster.Extensions.CommandLineUtils.CommandLineApplication.ExecuteAsync[TApp](String[]) + 0x42
at MyProgram.MyProgram.Main(String[]) + 0x23
at MyProgram.MyProgram.<Main>(String[]) + 0x2b
at MyProgram!<BaseAddress>+0xbaec0f
at MyProgram!<BaseAddress>+0xbaec9f
Looking at the code that fails, there's a bunch of reflection going on in there - I've tried adding quite a few additional assemblies to rd.xml (System.ComponentModel, System.Collections, System.Globalization, System.Reflection, etc.), but I haven't managed to get past this one.
Hi,
I'm experiencing a similar issue. I'm using MacMaster.Extensions.CommandLineUtils v2.3.1 and Microsoft.DotNet.ILCompiler v1.0.0-alpha-27418-01.
My Program class looks like this:
static int Main(string[] args)
{
return CommandLineApplication.Execute<Program>(args);
}
int OnExecute(CommandLineApplication app)
{
// ...
}
and I'm using this rd.xml file:
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<Assembly Name="McMaster.Extensions.CommandLineUtils" Dynamic="Required All" />
</Application>
</Directives>
I'm not getting the TypeInitializationException, probably because I'm not using an async Main(). However, I'd say that our root reason is the same because this is the exception I get:
Unhandled Exception: EETypeRva:0x00D22560(System.Reflection.MissingRuntimeArtifactException): MakeGenericMethod() cannot create this generic method instantiation because the instantiation was not metadata-enabled: 'McMaster.Extensions.CommandLineUtils.Abstractions.ValueParserProvider.GetParser<System.String>()' For more information, please visit http://go.microsoft.com/fwlink/?LinkID=616868
at Internal.Reflection.Core.Execution.ExecutionEnvironment.GetMethodInvoker(RuntimeTypeInfo, QMethodDefinition, RuntimeTypeInfo[], MemberInfo) + 0x148
at System.Reflection.Runtime.MethodInfos.NativeFormat.NativeFormatMethodCommon.GetUncachedMethodInvoker(RuntimeTypeInfo[], MemberInfo) + 0x50
at System.Reflection.Runtime.MethodInfos.RuntimeMethodInfo.get_MethodInvoker() + 0xa8
at System.Reflection.Runtime.MethodInfos.RuntimeNamedMethodInfo`1.MakeGenericMethod(Type[]) + 0x104
at McMaster.Extensions.CommandLineUtils.Abstractions.ValueParserProvider.GetParser(Type) + 0x47
at McMaster.Extensions.CommandLineUtils.Conventions.OptionAttributeConventionBase`1.AddOption(ConventionContext, CommandOption, PropertyInfo) + 0x37d
at McMaster.Extensions.CommandLineUtils.Conventions.OptionAttributeConvention.Apply(ConventionContext) + 0xb0
at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Builder.McMaster.Extensions.CommandLineUtils.Conventions.IConventionBuilder.AddConvention(IConvention) + 0x3c
at McMaster.Extensions.CommandLineUtils.ConventionBuilderExtensions.UseAttributes(IConventionBuilder) + 0xe6
at McMaster.Extensions.CommandLineUtils.ConventionBuilderExtensions.UseDefaultConventions(IConventionBuilder) + 0x13
at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](CommandLineContext) + 0xc2
at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](IConsole, String[]) + 0xb8
at project!<BaseAddress>+0x6741af
You see, the method McMaster.Extensions.CommandLineUtils.Abstractions.ValueParserProvider.GetParser(Type)
is present in both our stack traces.
Looking at the exception message, I tried to explicitly include McMaster.Extensions.CommandLineUtils.Abstractions.ValueParserProvider.GetParser<System.String>()
in my rd.xml
. However, I have no idea how to achieve that. I tried this:
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<Assembly Name="McMaster.Extensions.CommandLineUtils" Dynamic="Required All">
<Type Name="McMaster.Extensions.CommandLineUtils.Abstractions.ValueParserProvider" Dynamic="Required All">
</Type>
</Assembly>
<Assembly Name="mscorlib" />
</Application>
</Directives>
and this:
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<Assembly Name="McMaster.Extensions.CommandLineUtils" Dynamic="Required All">
<Type Name="McMaster.Extensions.CommandLineUtils.Abstractions.ValueParserProvider" Dynamic="Required All">
<Method Name="GetParser">
</Method>
</Type>
</Assembly>
<Assembly Name="mscorlib" />
</Application>
</Directives>
But none of them made any difference. Then, I tried this:
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<Assembly Name="McMaster.Extensions.CommandLineUtils" Dynamic="Required All">
<Type Name="McMaster.Extensions.CommandLineUtils.Abstractions.ValueParserProvider" Dynamic="Required All">
<Method Name="GetParser">
<GenericArgument Name="System.String,mscorlib" />
</Method>
</Type>
</Assembly>
<Assembly Name="mscorlib" />
</Application>
</Directives>
I had to look at https://github.com/dotnet/corert/blob/master/src/ILCompiler/src/RdXmlRootProvider.cs to avoid the System.NotSupportedExceptions I got. It seems that the troubleshooters such as https://dotnet.github.io/native/troubleshooter/method.html aren't useful, and I had to use GenericArgument
inside Method
despite not being listed as available child tag in the schema.
The rd.xml above produced this on compile time:
EXEC : error : Exception of type 'System.Exception' was thrown. [C:\path\to\my\project.csproj]
System.Exception: Exception of type 'System.Exception' was thrown.
at ILCompiler.RdXmlRootProvider.ProcessMethodDirective(IRootingServiceProvider rootProvider, ModuleDesc containingModule, TypeDesc containingType, XElement methodElement)
at ILCompiler.RdXmlRootProvider.ProcessTypeDirective(IRootingServiceProvider rootProvider, ModuleDesc containingModule, XElement typeElement)
at ILCompiler.RdXmlRootProvider.ProcessAssemblyDirective(IRootingServiceProvider rootProvider, XElement assemblyElement)
at ILCompiler.RdXmlRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider)
at ILCompiler.Compilation..ctor(DependencyAnalyzerBase`1 dependencyGraph, NodeFactory nodeFactory, IEnumerable`1 compilationRoots, ILProvider ilProvider, DebugInformationProvider debugInformationProvider, DevirtualizationManager devirtualizationManager, Logger logger)
at ILCompiler.ILScannerBuilder.ToILScanner()
at ILCompiler.Program.Run(String[] args)
at ILCompiler.Program.Main(String[] args)
I thought that maybe I forgot to specify the generic type parameter in the method signature, so I tried this instead:
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<Assembly Name="McMaster.Extensions.CommandLineUtils" Dynamic="Required All">
<Type Name="McMaster.Extensions.CommandLineUtils.Abstractions.ValueParserProvider" Dynamic="Required All">
<Method Name="GetParser`1">
<GenericArgument Name="System.String,mscorlib" />
</Method>
</Type>
</Assembly>
<Assembly Name="mscorlib" />
</Application>
</Directives>
And then I got a NullReferenceException:
EXEC : error : Object reference not set to an instance of an object. [C:\path\to\my\project.csproj]
System.NullReferenceException: Object reference not set to an instance of an object.
at ILCompiler.RdXmlRootProvider.ProcessMethodDirective(IRootingServiceProvider rootProvider, ModuleDesc containingModule, TypeDesc containingType, XElement methodElement)
at ILCompiler.RdXmlRootProvider.ProcessTypeDirective(IRootingServiceProvider rootProvider, ModuleDesc containingModule, XElement typeElement)
at ILCompiler.RdXmlRootProvider.ProcessAssemblyDirective(IRootingServiceProvider rootProvider, XElement assemblyElement)
at ILCompiler.RdXmlRootProvider.AddCompilationRoots(IRootingServiceProvider rootProvider)
at ILCompiler.Compilation..ctor(DependencyAnalyzerBase`1 dependencyGraph, NodeFactory nodeFactory, IEnumerable`1 compilationRoots, ILProvider ilProvider, DebugInformationProvider debugInformationProvider, DevirtualizationManager devirtualizationManager, Logger logger)
at ILCompiler.ILScannerBuilder.ToILScanner()
at ILCompiler.Program.Run(String[] args)
at ILCompiler.Program.Main(String[] args)
I got the same if I used GetParser`1[[System.Object,mscorlib]] as the method name.
Is there anything else that I could try out? What is the proper way to enable metadata for that generic method?
Is there anything else that I could try out?
Try using GetParser
as the method name. The generic arity suffix is used for types only.
Alternatively, you can consider using command line parser that is more AOT friendly, e.g. https://github.com/dotnet/command-line-api.
Unfortunately I already tried that... I got a System.Exception() with no message, you can see the trace in my previous comment. Or did I miss something in that one?
I'll look into the link you proposed, thanks!
I'm pretty sure it's matching the non-generic version of the GetParser
method. Not being able to specify overloads is a known limitation.
No RD.XML should be needed once we merge #6987. It should work if you build the repo yourself with that change. We can't merge it yet because it causes CI to fail (it makes us discover generic CoreFX tests we weren't running before). With any luck, I'll get to it this weekend.
Thanks for the answer! I'll stay tuned to the new releases then, I appreciate your feedback 😃
I'm able to compile a program that uses McMaster.Extensions.CommandLineUtils (version 2.2.5) like so:
...but when I run the program, I crash like this:
Same code works fine if not natively compiled.