CommandLineUtils icon indicating copy to clipboard operation
CommandLineUtils copied to clipboard

Trimmed compilation fails with annotations

Open ProphetLamb opened this issue 2 years ago • 3 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

ProphetLamb avatar Aug 09 '23 13:08 ProphetLamb

Warnings for reflection can be enabled using the following switch

<IsTrimmable>true</IsTrimmable>

ProphetLamb avatar Aug 09 '23 13:08 ProphetLamb

This issue has been automatically marked as stale because it has no recent activity. It will be closed if no further activity occurs. Please comment if you believe this should remain open, otherwise it will be closed in 14 days. Thank you for your contributions to this project.

github-actions[bot] avatar Aug 09 '24 01:08 github-actions[bot]