.NET Core SDK (reflecting any global.json):
Version: 3.1.401
Commit: 39d17847db
Runtime Environment:
OS Name: Windows
OS Version: 10.0.19041
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\3.1.401\
Host (useful for support):
Version: 3.1.7
Commit: fcfdef8d6b
Problem description:
A blank 3.1 WinForms app, published as self-contained and trimmed, still bundles all WPF assemblies, which adds at least extra 12MB.
Expected behavior:
WPF assemblies (Presentation*.dll etc) should not be included if the are not used.
Minimal repro:
dotnet new winforms
dotnet publish -r win-x64 -c Release --self-contained true /p:PublishTrimmed=true
cd .\MyFormsAppCore\bin\Release\netcoreapp3.1\win-x64\publish
dir Presentation*.dll
Note all Presentation*.dll
@danmosemsft can you help route this one?
@joperezr thoughts about where this should go?
A couple of thoughts here:
System.*.dlland Microsoft.*.dll so all other assemblies will by default be copied as-is. A TL;DR explanation of why, is that the linker is very smart but there are still ways of writing code where the linker won't be able to detect that some code needs to be kept, so if not annotated correctly it could by mistake remove that and cause an exception when the code tries to load at runtime. In System.*.dll and Microsoft.*.dll we are able to do this because we have been annotating appropriately all of these places in order to make them linker friendly, so that is why the linker will trim all of those but not others (including Presentation*.dll). @eerhardt can share more details and his opinion here, but I would probably move this issue over to the SDK to track the fact of also trimming Presentation*.dlland also log a bug into this repo in order to make the dlls linker friendly.@joperezr, thanks for your thoughts. I've just tried it with the .NET SDK 5.0.100-preview.7 and it still bundles the WPF assemblies. That was very easy to do under Windows Sandbox, using the same two commands from the "Minimal repro" section in my report.
Also, for the sake of trying, I deleted Presentation*.dll and edited them out of the generated MyFormsAppCore.deps.json. The app wouldn't start then. I can speculate that WinForms and WPF assemblies may have some mutual dependencies. This can also be easily repro'ed with the placeholder blank app as above.
I suppose I'll have to move on and package it as is for my otherwise lightweight WinForms app. One other option I considered was to back-port it to NET 4.8 to keep the download size to a minimum, but I really like using the modern .NET and C# 8+.
To follow up on what @joperezr said above -
The reason all the Presentation*.dll files still exist is because the SDK is explicitly marking them as copy, which means they won't be trimmed. The reason the SDK is marking them as copy is because by default the only assemblies that can be trimmed come from the Microsoft.NETCore.App runtimepack.
You can see that by the IsTrimmable="true" property here:
<KnownFrameworkReference Include="Microsoft.NETCore.App"
TargetFramework="netcoreapp5.0"
RuntimeFrameworkName="Microsoft.NETCore.App"
DefaultRuntimeFrameworkVersion="$(MicrosoftNETCoreAppDefaultRuntimeFrameworkVersion)"
LatestRuntimeFrameworkVersion="$(MicrosoftNETCoreAppRuntimePackageVersion)"
TargetingPackName="Microsoft.NETCore.App.Ref"
TargetingPackVersion="$(MicrosoftNETCoreAppRefPackageVersion)"
RuntimePackNamePatterns="Microsoft.NETCore.App.Runtime.**RID**"
RuntimePackRuntimeIdentifiers="@(NetCoreRuntimePackRids, '%3B')"
IsTrimmable="true"
/>
The Microsoft.WindowsDesktop.App entry doesn't have IsTrimmable="true", so the SDK/linker won't trim any assembly coming from that package. The same is true for the Microsoft.AspNetCore.App.
<KnownFrameworkReference Include="Microsoft.WindowsDesktop.App"
TargetFramework="netcoreapp5.0"
RuntimeFrameworkName="Microsoft.WindowsDesktop.App"
DefaultRuntimeFrameworkVersion="$(MicrosoftWindowsDesktopAppDefaultRuntimeFrameworkVersion)"
LatestRuntimeFrameworkVersion="$(MicrosoftWindowsDesktopAppRuntimePackageVersion)"
TargetingPackName="Microsoft.WindowsDesktop.App.Ref"
TargetingPackVersion="$(MicrosoftWindowsDesktopAppRefPackageVersion)"
RuntimePackNamePatterns="Microsoft.WindowsDesktop.App.Runtime.**RID**"
RuntimePackRuntimeIdentifiers="@(WindowsDesktopRuntimePackRids, '%3B')"
IsWindowsOnly="true"
/>
So if we wanted to allow WPF apps to trim WinForms assemblies, and vice-versa, we would need to set IsTrimmable for the <KnownFrameworkReference Include="Microsoft.WindowsDesktop.App". And then of course test it and make sure it works correctly.
cc @sbomer
Most helpful comment
A couple of thoughts here:
System.*.dllandMicrosoft.*.dllso all other assemblies will by default be copied as-is. A TL;DR explanation of why, is that the linker is very smart but there are still ways of writing code where the linker won't be able to detect that some code needs to be kept, so if not annotated correctly it could by mistake remove that and cause an exception when the code tries to load at runtime. InSystem.*.dllandMicrosoft.*.dllwe are able to do this because we have been annotating appropriately all of these places in order to make them linker friendly, so that is why the linker will trim all of those but not others (includingPresentation*.dll). @eerhardt can share more details and his opinion here, but I would probably move this issue over to the SDK to track the fact of also trimmingPresentation*.dlland also log a bug into this repo in order to make the dlls linker friendly.