diff --git a/src/FSharp.Build/FSharp.Build.fsproj b/src/FSharp.Build/FSharp.Build.fsproj
index 8f2fd4f5951..080c53399b1 100644
--- a/src/FSharp.Build/FSharp.Build.fsproj
+++ b/src/FSharp.Build/FSharp.Build.fsproj
@@ -46,6 +46,7 @@
+
diff --git a/src/FSharp.Build/GenerateILLinkSubstitutions.fs b/src/FSharp.Build/GenerateILLinkSubstitutions.fs
new file mode 100644
index 00000000000..3bf461ce928
--- /dev/null
+++ b/src/FSharp.Build/GenerateILLinkSubstitutions.fs
@@ -0,0 +1,84 @@
+// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
+
+namespace FSharp.Build
+
+open System
+open System.IO
+open System.Text
+open Microsoft.Build.Framework
+open Microsoft.Build.Utilities
+
+///
+/// MSBuild task that generates ILLink.Substitutions.xml file to remove F# metadata resources during IL linking.
+///
+type GenerateILLinkSubstitutions() =
+ inherit Task()
+
+ ///
+ /// Assembly name to use when generating resource names to be removed.
+ ///
+ []
+ member val AssemblyName = "" with get, set
+
+ ///
+ /// Intermediate output path for storing the generated file.
+ ///
+ []
+ member val IntermediateOutputPath = "" with get, set
+
+ ///
+ /// Generated embedded resource items.
+ ///
+ []
+ member val GeneratedItems = [| |] : ITaskItem[] with get, set
+
+ override this.Execute() =
+ try
+ // Define the resource prefixes that need to be removed
+ let resourcePrefixes =
+ [|
+ // Signature variants
+ yield! [| for dataType in [| "Data"; "DataB" |] do
+ for compression in [| ""; "Compressed" |] do
+ yield $"FSharpSignature{compression}{dataType}" |]
+
+ // Optimization variants
+ yield! [| for dataType in [| "Data"; "DataB" |] do
+ for compression in [| ""; "Compressed" |] do
+ yield $"FSharpOptimization{compression}{dataType}" |]
+
+ // Info variants
+ yield "FSharpOptimizationInfo"
+ yield "FSharpSignatureInfo"
+ |]
+
+ // Generate the XML content
+ let sb = StringBuilder(4096) // pre-allocate capacity
+ sb.AppendLine("") |> ignore
+ sb.AppendLine("") |> ignore
+ sb.AppendLine($" ") |> ignore
+
+ // Add each resource entry with proper closing tag on the same line
+ for prefix in resourcePrefixes do
+ sb.AppendLine($" ") |> ignore
+
+ // Close assembly and linker tags
+ sb.AppendLine(" ") |> ignore
+ sb.AppendLine("") |> ignore
+
+ let xmlContent = sb.ToString()
+
+ // Create a file in the intermediate output path
+ let outputFileName = Path.Combine(this.IntermediateOutputPath, "ILLink.Substitutions.xml")
+ Directory.CreateDirectory(this.IntermediateOutputPath) |> ignore
+ File.WriteAllText(outputFileName, xmlContent)
+
+ // Create a TaskItem for the generated file
+ let item = TaskItem(outputFileName) :> ITaskItem
+ item.SetMetadata("LogicalName", "ILLink.Substitutions.xml")
+
+ this.GeneratedItems <- [| item |]
+ true
+ with ex ->
+ this.Log.LogErrorFromException(ex, true)
+ false
\ No newline at end of file
diff --git a/src/FSharp.Build/Microsoft.FSharp.NetSdk.targets b/src/FSharp.Build/Microsoft.FSharp.NetSdk.targets
index 2b121a778b2..6e3dbe14f2c 100644
--- a/src/FSharp.Build/Microsoft.FSharp.NetSdk.targets
+++ b/src/FSharp.Build/Microsoft.FSharp.NetSdk.targets
@@ -154,6 +154,18 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and
true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/AheadOfTime/Trimming/check.ps1 b/tests/AheadOfTime/Trimming/check.ps1
index 50a8a9e6422..4c3016c9e70 100644
--- a/tests/AheadOfTime/Trimming/check.ps1
+++ b/tests/AheadOfTime/Trimming/check.ps1
@@ -42,8 +42,11 @@ function CheckTrim($root, $tfm, $outputfile, $expected_len) {
# NOTE: Trimming now errors out on desktop TFMs, as shown below:
# error NETSDK1124: Trimming assemblies requires .NET Core 3.0 or higher.
-# Check net7.0 trimmed assemblies
+# Check net9.0 trimmed assemblies
CheckTrim -root "SelfContained_Trimming_Test" -tfm "net9.0" -outputfile "FSharp.Core.dll" -expected_len 300032
-# Check net8.0 trimmed assemblies
+# Check net9.0 trimmed assemblies with static linked FSharpCore
CheckTrim -root "StaticLinkedFSharpCore_Trimming_Test" -tfm "net9.0" -outputfile "StaticLinkedFSharpCore_Trimming_Test.dll" -expected_len 9150976
+
+# Check net9.0 trimmed assemblies with F# metadata resources removed
+CheckTrim -root "FSharpMetadataResource_Trimming_Test" -tfm "net9.0" -outputfile "FSharpMetadataResource_Trimming_Test.dll" -expected_len -1