Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET 6 TrimMode CopyUsed isn't working like .NET 5? #63290

Closed
natan-abolafya opened this issue Jan 3, 2022 · 8 comments
Closed

.NET 6 TrimMode CopyUsed isn't working like .NET 5? #63290

natan-abolafya opened this issue Jan 3, 2022 · 8 comments
Labels
linkable-framework Issues associated with delivering a linker friendly framework untriaged New issue has not been triaged by the area owner

Comments

@natan-abolafya
Copy link

natan-abolafya commented Jan 3, 2022

Description

We have a solution with 3 executable projects. They have various libraries in common, including another library project in this solution. Self-contained, not single file. They are also neither WPF nor WinForms projects. All three have these in csproj:

    <OutputType>Exe</OutputType>    
    <PublishTrimmed>true</PublishTrimmed>    
    <PlatformTarget>x64</PlatformTarget>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>

Currently, with .NET 5, we trim these 3 executables and combine them together to save space. And it works well.

dotnet publish .\Windows.sln --configuration Release /p:platform="Any CPU" -o .\build-artifacts\

Now we're trying to use .NET 6 but the application crashes on startup with some sort of "Cannot find method".
This does not happen if we don't combine the executables (and their dependencies).

After some research, I figured this is normal with TrimMode: link which is the default. But changing it to copyused (or CopyUsed) did not help. Tried both changing the csproj file and adding -p:TrimMode=copyused to the publish command. Same. The size of the output folder is however slightly bigger, so there is some effect. Here is the failure reason now:

Fatal : 'App': Unhandled exception 'System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize
 ---> System.Configuration.ConfigurationErrorsException: An error occurred loading a configuration file: Could not load file or assembly 'System.ComponentModel.EventBasedAsync, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified. (C:\Users\natan.abolafya\Code\stratus-client-shared\src\client\build-artifacts\Config\machine.config)
 ---> System.IO.FileNotFoundException: Could not load file or assembly 'System.ComponentModel.EventBasedAsync, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
File name: 'System.ComponentModel.EventBasedAsync, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
   at System.Configuration.ClientConfigurationHost.OpenStreamForRead(String streamName)

I double checked that all csproj have the same dependency versions. So one is not overriding the other.

I can probably get into TrimmerRootAssembly rabbit hole here but not sure if that's a good way to go.
My question is, is this how it's supposed to behave? What should be the strategy to combine 3 executables with many similar dependencies and have some sort of trimming?

Reproduction Steps

Maybe add a bunch of <TrimmerRootAssembly Include entries to .csproj?

Expected behavior

I expected CopyUsed to leave the dependency dlls as is.

Actual behavior

But I can see that each project gets different sized dlls. I.e. the dlls are getting trimmed?

Regression?

Yes

Known Workarounds

No response

Configuration

dotnet 6.0.101
Windows 10 21H2 x64

Other information

No response

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Jan 3, 2022
@danmoseley
Copy link
Member

perhaps @eerhardt has an idea where this should route.

@marek-safar marek-safar added the linkable-framework Issues associated with delivering a linker friendly framework label Jan 4, 2022
@ghost
Copy link

ghost commented Jan 4, 2022

Tagging subscribers to 'linkable-framework': @eerhardt, @vitek-karas, @LakshanF, @sbomer, @joperezr
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

We have a solution with 3 executable projects. They have various libraries in common, including another library project in this solution. Self-contained, not single file. They are also neither WPF nor WinForms projects. All three have these in csproj:

    <OutputType>Exe</OutputType>    
    <PublishTrimmed>true</PublishTrimmed>    
    <PlatformTarget>x64</PlatformTarget>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>

Currently, with .NET 5, we trim these 3 executables and combine them together to save space. And it works well.

dotnet publish .\Windows.sln --configuration Release /p:platform="Any CPU" -o .\build-artifacts\

Now we're trying to use .NET 6 but the application crashes on startup with some sort of "Cannot find method".
This does not happen if we don't combine the executables (and their dependencies).

After some research, I figured this is normal with TrimMode: link which is the default. But changing it to copyused (or CopyUsed did not help. Tried both changing the csproj file and adding -p:TrimMode=copyused to the publish command. Same. The size of the output folder is however slightly bigger, so there is some effect. Here is the failure reason now:

Fatal : 'App': Unhandled exception 'System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize
 ---> System.Configuration.ConfigurationErrorsException: An error occurred loading a configuration file: Could not load file or assembly 'System.ComponentModel.EventBasedAsync, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified. (C:\Users\natan.abolafya\Code\stratus-client-shared\src\client\build-artifacts\Config\machine.config)
 ---> System.IO.FileNotFoundException: Could not load file or assembly 'System.ComponentModel.EventBasedAsync, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
File name: 'System.ComponentModel.EventBasedAsync, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
   at System.Configuration.ClientConfigurationHost.OpenStreamForRead(String streamName)

I double checked that all csproj have the same dependency versions. So one is not overriding the other.

I can probably get into TrimmerRootAssembly rabbit hole here but not sure if that's a good way to go.
My question is, is this how it's supposed to behave? What should be the strategy to combine 3 executables with many similar dependencies and have some sort of trimming?

Reproduction Steps

Maybe add a bunch of <TrimmerRootAssembly Include entries to .csproj?

Expected behavior

I expected CopyUsed to leave the dependency dlls as is.

Actual behavior

But I can see that each project gets different sized dlls. I.e. the dlls are getting trimmed?

Regression?

Yes

Known Workarounds

No response

Configuration

dotnet 6.0.101
Windows 10 21H2 x64

Other information

No response

Author: natan-abolafya
Assignees: -
Labels:

untriaged, linkable-framework

Milestone: -

@vitek-karas
Copy link
Member

Could you please explain what does it mean to "combine" the output of the three projects?

Just to set the expectations: In .NET 5 trimming was in preview and in .NET 6 there are lot of changes to how it works, it's expected that things will work differently. Also the only really supported trimming scenario is if there are no warnings produced by the trimming (and they're not blindly suppressed obviously).

We did try to keep copyused working more or less similar to how it did in .NET 5, but there were simply too many internal changes and it's definitely possible things are behaving differently. Without more details, it's really hard to tell what's going on.

To explain the differences in size: "copyused" doesn't mean that it will leave the assemblies as is. That's what "copy" does. "copyused" will still for example rewrite referenced to forwarded types to point to the implementation assembly. And it will try to remove type forwarders which are not used (after the rewrite). I would expect to see differences in assemblies with type forwarders in them. For example System.Configuration.dll is a "pure facade", meaning the assembly only contains type forwarders, it could be fully removed by even copyused trimming.

We've been discussing what it would mean to support trimming multiple applications together, but currently there's nothing which works. Trimming each app in isolation and "combining" is bound to run into issues.

@natan-abolafya
Copy link
Author

We've been discussing what it would mean to support trimming multiple applications together,

I think this is what I meant. It's a solution with 3 self-contained executables (based on 3 .csproj projects) that we want to ship in the same folder. Since every self-contained executable comes with all the .NET dlls, it becomes rather big. So we want to have 3 executables with the same .NET (+ some more same NuGet libraries). Combining means collecting dotnet publish artifacts (each in their own bin folder) into one folder, overwriting the same .NET dlls. I also tried dotnet publish xxx.sln --output-directory ./artifacts to see if .NET can take care of the conflicts, but the result was the same.

Also the only really supported trimming scenario is if there are no warnings produced by the trimming (and they're not blindly suppressed obviously).

Yes, there were warnings that I have ignored. However, I haven't digged into them (yet) since individual published executables do work as long as they are not combined.

Now I understand that trimming isn't very ideal for this scenario. I suppose my expectations were different since .NET 5 was fine with this. But it would be nice to have a strategy for this use case.

To explain the differences in size: "copyused" doesn't mean that it will leave the assemblies as is. That's what "copy" does.

Is "copy" an option? It's not documented, or?
Well, I ended up trying it and it created something bigger than CopyUsed and smaller than no-trimming; but still the same issue.

@vitek-karas
Copy link
Member

I'm well aware of the scenario, but we don't have anything specific available for it at the moment.

What is more likely to happen is this: #53834
No trimming (for now), but it would make it easier to ship multiple apps with "private" runtime. This is already possible today, but it requires to set environment variable (DOTNET_ROOT) which may not always be possible.

@natan-abolafya
Copy link
Author

natan-abolafya commented Jan 4, 2022

Ah, I see. I will follow that issue, thanks. Our way of combining seems to work without trimming on .NET 6 by the way.

I take it it was kind of a luck that our approach was working with .NET 5 then. If that's the expectation, then we can close this issue.
Thanks.

@vitek-karas
Copy link
Member

I take it it was kind of a luck that our approach was working with .NET 5 then. If that's the expectation, then we can close this issue.

Yes - the proper way to do this would be to run the trimming once on the combined output - which is technically possible, but the SKD doesn't have a support for this (and there might be bugs in the trimming tool since nobody tests this).

@ghost ghost locked as resolved and limited conversation to collaborators Feb 3, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
linkable-framework Issues associated with delivering a linker friendly framework untriaged New issue has not been triaged by the area owner
Projects
None yet
Development

No branches or pull requests

4 participants