Skip to content

Auto-generate IlLink.Substitutions.xml to Remove F# Metadata Resources #18591

@T-Gro

Description

@T-Gro

Background

F# assemblies contain embedded resources for signature and optimization data that can significantly increase assembly size. These resource names are templates that get combined with the actual assembly name. To reduce the size of trimmed F# assemblies, we need to automatically remove these metadata resources during IL linking:
(the asterisk is not there, this is where the project name goes)

The full set of F#-compiler-generated prefixes currently is:
FSharpSignatureData.*
FSharpSignatureDataB.*
FSharpSignatureCompressedData.*
FSharpSignatureCompressedDataB.*

FSharpOptimizationData.*
FSharpOptimizationDataB.*
FSharpOptimizationCompressedData.*
FSharpOptimizationCompressedDataB.*

FSharpOptimizationInfo.*
FSharpSignatureInfo.*

Files to Modify

1. Create new MSBuild task: src/FSharp.Build/GenerateILLinkSubstitutions.fs

Create a new task that follows the pattern of existing embedded resource generators. Study how it creates TaskItem objects for embedded resources.

The task should:

  • Inherit from Microsoft.Build.Utilities.Task
  • Take the assembly name as input
  • Generate XML content with actual resource names (not patterns with asterisks)
  • Create the content in memory and add it as an EmbeddedResource item

Example of what the generated XML should contain (using actual assembly name):

<linker>
  <assembly fullname="MyProject">
    <resource name="FSharpOptimizationData.MyProject" action="remove" />
    <resource name="FSharpSignatureData.MyProject" action="remove" />
    <resource name="FSharpOptimizationCompressedData.MyProject" action="remove" />
    <resource name="FSharpSignatureCompressedData.MyProject" action="remove" />
    <resource name="FSharpOptimizationDataB.MyProject" action="remove" />
    <resource name="FSharpSignatureDataB.MyProject" action="remove" />
    <resource name="FSharpOptimizationCompressedDataB.MyProject" action="remove" />
    <resource name="FSharpSignatureCompressedDataB.MyProject" action="remove" />
    <resource name="FSharpOptimizationInfo.MyProject" action="remove" />
    <resource name="FSharpSignatureInfo.MyProject" action="remove" />
  </assembly>
</linker>

2. Update src/FSharp.Build/FSharp.Build.fsproj

Add the new task file to the compilation list: 3

<Compile Include="GenerateILLinkSubstitutions.fs" />

3. Add target to src/FSharp.Build/Microsoft.FSharp.NetSdk.targets

Create a new target that runs during every compilation. Looking at the existing props file structure, add:

<UsingTask TaskName="GenerateILLinkSubstitutions" AssemblyFile="$(FSharpBuildTasksAssembly)" />

<Target Name="GenerateFSharpILLinkSubstitutions" BeforeTargets="CoreCompile">
  <GenerateILLinkSubstitutions 
    AssemblyName="$(AssemblyName)"
    IntermediateOutputPath="$(IntermediateOutputPath)" />
</Target>

Implementation Steps

  1. Study the existing embedded resource pattern: Look at how it generates embedded resources programmatically.

  2. Understand resource name construction: The patterns are templates. You need to append the actual assembly name to create the full resource names (e.g., "FSharpOptimizationData." + assemblyName).

  3. Create the MSBuild task:

    • Follow the structure of existing tasks in src/FSharp.Build/
    • Generate XML content in memory
    • Create TaskItem with the XML content as an embedded resource
    • Set appropriate metadata like LogicalName="ILLink.Substitutions.xml"
    • Use action="remove" for all resource entries
  4. Wire into the build system:

    • Add the target to run during normal compilation
    • Use BeforeTargets="CoreCompile" to ensure it runs at the right time
    • Pass the assembly name from MSBuild properties
  5. ** Create tests for the behavior.

Look at the folder https://github.com/dotnet/fsharp/tree/main/tests/AheadOfTime/Trimming to see existing trimming tests.
You will need to adjust the test setup to make it use freshly built FSharp.Build.dll as well as the adjusted props and targets.

Notes

This approach generates the substitution file as an embedded resource during compilation, ensuring F# metadata resources are removed during IL linking to reduce the size of trimmed F# assemblies.

Metadata

Metadata

Assignees

Projects

Status

New

Relationships

None yet

Development

No branches or pull requests

Issue actions