natemcmaster / CommandLineUtils

Command line parsing and utilities for .NET

Home Page:https://natemcmaster.github.io/CommandLineUtils/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Trimmed compilation fails with annotations

ProphetLamb opened this issue · comments

Compilation with trimming fails due to reflection. Trimming is often used with single file executables:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>

    <PublishSingleFile>true</PublishSingleFile>
    <SelfContained>true</SelfContained>
    <PublishTrimmed>true</PublishTrimmed>
    <PublishReadyToRun>true</PublishReadyToRun>
    <EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.0.2" />
  </ItemGroup>
</Project>

Compilation yields the following warning:

...\mcmaster.extensions.commandlineutils\4.0.2\lib\netstandard2.1\McMaster.E
xtensions.CommandLineUtils.dll : warning IL2104: Assembly 'McMaster.Extensions.CommandLineUtils' prod 
uced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries [...]

Execution fails with the following exception:

Unhandled exception. System.InvalidOperationException: No method named 'OnExecute' or 'OnExecuteAsync' could be found.
   at McMaster.Extensions.CommandLineUtils.Conventions.ExecuteMethodConvention.OnExecute(ConventionContext, CancellationToken)
   at McMaster.Extensions.CommandLineUtils.Conventions.ExecuteMethodConvention.<>c__DisplayClass0_0.<<Apply>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.ExecuteAsync(String[], CancellationToken )
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.ExecuteAsync[TApp](CommandLineContext, CancellationToken )
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](CommandLineContext)   
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](IConsole, String[] )  
   at Program.<Main>$(String[])

Enabling reflection for public methods yield the following exception:

Unhandled exception. System.InvalidOperationException: Could not find any public constructors of type 
'MyCommand'.
   at McMaster.Extensions.CommandLineUtils.Conventions.ConstructorInjectionConvention.<>c__6`1.<FindMatchedConstructor>b__6_0()
   at System.Lazy`1.ViaFactory(LazyThreadSafetyMode)
   at System.Lazy`1.ExecutionAndPublication(LazyHelper, Boolean)
   at System.Lazy`1.CreateValue()
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication`1.McMaster.Extensions.CommandLineUtils.Abstractions.IModelAccessor.GetModel()
   at McMaster.Extensions.CommandLineUtils.Conventions.ArgumentAttributeConvention.<>c__DisplayClass1_0.<AddArgument>b__1(ParseResult)
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.HandleParseResult(ParseResult)      
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Parse(String[] )
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.ExecuteAsync(String[], CancellationToken )
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.ExecuteAsync[TApp](CommandLineContext, CancellationToken )
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](CommandLineContext)   
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](IConsole, String[] )  
   at Program.<Main>$(String[])

Enabling reflection for both, public methods and constructors resolves the issue.

This requires the following annotation:

using System.Diagnostics.CodeAnalysis;
using McMaster.Extensions.CommandLineUtils;

Execute<MyCommand>();

void Execute<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicConstructors)] T>()
	where T : class
{
	CommandLineApplication.Execute<T>(args);
}

Please add these annotations to all reflection methods consuming Types and generic type arguments.

I am glad to provide more information on the subject, please also refer to https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/prepare-libraries-for-trimming

Warnings for reflection can be enabled using the following switch

<IsTrimmable>true</IsTrimmable>