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

Detect correctly multitargeted apps for trim/AOT/single-file warnings #35767

Merged
merged 5 commits into from
Oct 3, 2023

Conversation

sbomer
Copy link
Member

@sbomer sbomer commented Sep 27, 2023

The warnings introduced by #34077 and described in https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/8.0/trimming-unsupported-targetframework are unnecessary noise for projects that multitarget to include a TargetFramework that is compatible with trimming/AOT/single-file, and is a low enough version to ensure that it will be picked over any other TFM's assets when the library is consumed in a trimmed/AOT'd/single-file app.

This fixes the issue by detecting correctly multi-targeted libraries and suppressing the warnings in these cases.

Note that prior to the .NET 8 SDK, netstandard libraries would still get the IsTrimmable attribute embedded in the assembly when setting IsTrimmable, and could use trim analysis despite incomplete warnings due to unannotated ref assemblies. With the .NET 8 SDK this is no longer the case. Even for correctly multi-targeted libraries, the netstandard output assembly will no longer contain the IsTrimmable attribute. We debated whether it was worth warning in this case, but decided that the negative impact of the warning on library developers following the "golden path" was too large to justify keeping this as a warning.

Fixes #35528

@sbomer sbomer requested review from AntonLapounov and a team as code owners September 27, 2023 23:56
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-ILLink untriaged Request triage from a team member labels Sep 27, 2023
@dsplaisted
Copy link
Member

Unfortunately, we want to avoid parsing other TargetFrameworks values for multi-targeted projects. The reason is we want to support TargetFramework aliases (see also here), and this kind of check would not correctly handle the aliases.

You could disable the warning when the project is multi-targeted at all without trying to parse the other TargetFramework values. I think we've done something similar in other situations.

@sbomer
Copy link
Member Author

sbomer commented Sep 28, 2023

Interesting, thanks for the pointers. Still trying to understand this - doesn't the pattern of using IsTargetFrameworkCompatible with $(TargetFramework) already have the problem, since it (I assume) wouldn't handle TargetFramework values that are just aliases?

@vitek-karas
Copy link
Member

Personally I'd be fine if this didn't work with the aliases. This is not a correctness feature, it's a convenience feature. So if it works in majority of scenarios, it's good enough. If there's a typical "golden path" way which works, I think that would be good enough for this.

@dsplaisted
Copy link
Member

Interesting, thanks for the pointers. Still trying to understand this - doesn't the pattern of using IsTargetFrameworkCompatible with $(TargetFramework) already have the problem, since it (I assume) wouldn't handle TargetFramework values that are just aliases?

Yes, we shouldn't be using IsTargetFrameworkCompatible in generic code. We should be using the TargetFrameworkIdentifier and TargetFrameworkVersion properties instead, for example: '$(TargetFrameworkIdentifier)' == '.NETCoreApp' and $([MSBuild]::VersionGreaterThanOrEquals($(TargetFrameworkVersion), 5.0)). IsTargetFrameworkCompatible is OK to use in project files where you're in control of the list of TargetFrameworks and you know you're not using aliases.

Personally I'd be fine if this didn't work with the aliases. This is not a correctness feature, it's a convenience feature. So if it works in majority of scenarios, it's good enough. If there's a typical "golden path" way which works, I think that would be good enough for this.

In projects where it doesn't work due to aliasing, wouldn't you always get the warnings?

@sbomer
Copy link
Member Author

sbomer commented Sep 28, 2023

Yes, we shouldn't be using IsTargetFrameworkCompatible in generic code.

Makes sense, I understand the reasoning. We'll need to decide somehow whether this use is justified given that it won't work for aliases.

In projects where it doesn't work due to aliasing, wouldn't you always get the warnings?

I believe this change doesn't make the behavior any worse for projects with aliases. For unsupported TFMs, you'd get the normal warning (the new logic won't detect aliases and so won't silence the warnings). If the TFMs are are all supported (as detected through TargetFrameworkIdentifier/TargetFrameworkVersion, there will be no warnings to silence in the first place.

I'll try to confirm with some new testcases.

@dsplaisted
Copy link
Member

I believe this change doesn't make the behavior any worse for projects with aliases. For unsupported TFMs, you'd get the normal warning (the new logic won't detect aliases and so won't silence the warnings). If the TFMs are are all supported (as detected through TargetFrameworkIdentifier/TargetFrameworkVersion, there will be no warnings to silence in the first place.

OK, that makes sense.

Copy link
Member

@vitek-karas vitek-karas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

@sbomer
Copy link
Member Author

sbomer commented Oct 2, 2023

@dsplaisted I think this is ready to merge if you are on board with the approach. PTAL.

Copy link
Member

@dsplaisted dsplaisted left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with this in general, but see my comment about the complexity of understanding it and consider whether it's worth simplifying.

Comment on lines +52 to +57
Correctly multitargeted projects include a TargetFramework that is compatible with the requested
functionality (trimming/AOT/single-file), and doesn't leave a "gap" where the incompatible TargetFramework's
assembly could be consumed from an app that uses the functionality. In other words, correctly multitargeted
projects should include at least one TFM that:
- supports the given functionality, and
- is no larger than the minimum non-EOL TargetFramework that supports this functionality.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I understand this now but it took me forever to wrap my head around. Is this complexity going to surface to developers? Is it worth doing so when it may be confusing, or would it be better to just ignore the "gap" and suppress the warning if any of the target frameworks supports the functionality?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I share your concern about the complexity - it would be super confusing to a user who is trying to figure out under which circumstances exactly the warning is produced. The intention is that users following our guidance won't need to worry about this and the warning should just go away.

Our team discussed at length whether this was worth doing before landing on this solution. The complexity solves a correctness problem that we wanted to avoid (the correctness problem: allowing an inadvertently non-"trimmable" netstandard asset to be consumed in a trimmed app that uses a supported TFM).

It's still possible to get into that situation in an app that explicitly references the netstandard assembly, or that targets an EOL TFM (causing it to consume the netstandard asset from a library that multitargets netstandard2.0;net6.0 for example).

@sbomer sbomer merged commit cc67f45 into dotnet:main Oct 3, 2023
sbomer added a commit to sbomer/sdk that referenced this pull request Oct 4, 2023
…dotnet#35767)

The warnings introduced by
 dotnet#34077 and described in
 https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/8.0/trimming-unsupported-targetframework
 are unnecessary noise for projects that multitarget to include a
 TargetFramework that is compatible with
 trimming/AOT/single-file, and is a low enough version to ensure
 that it will be picked over any other TFM's assets when the
 library is consumed in a trimmed/AOT'd/single-file app.

This fixes the issue by detecting correctly multi-targeted
libraries and suppressing the warnings in these cases.

Note that prior to the .NET 8 SDK, netstandard libraries would
still get the `IsTrimmable` attribute embedded in the assembly
when setting `IsTrimmable`, and could use trim analysis despite
incomplete warnings due to unannotated ref assemblies. With the
.NET 8 SDK this is no longer the case. Even for correctly
multi-targeted libraries, the netstandard output assembly will no
longer contain the `IsTrimmable` attribute. We debated whether it
was worth warning in this case, but decided that the negative
impact of the warning on library developers following the "golden
path" was too large to justify keeping this as a warning.

This solution addresses a correctness problem that we wanted to
avoid (the correctness problem: allowing an inadvertently
non-"trimmable" netstandard asset to be consumed in a trimmed app
that uses a supported TFM).

It's still possible to get into that situation in an app that
explicitly references the netstandard assembly, or that targets
an EOL TFM (causing it to consume the netstandard asset from a
library that multitargets `netstandard2.0;net6.0` for example).

Fixes dotnet#35528
sbomer added a commit that referenced this pull request Oct 6, 2023
…ngle-file warnings (#35851)

Backport of #35767.

## Customer Impact
In .NET 8 we made a breaking change that introduces a warning when setting `IsTrimmable` or related properties in a .NET Standard library. The warning was introduced because we changed the behavior (to not reference the trim analyzers, and to not mark the assembly with `[assembly: AssemblyMetadat("IsTrimmable", "True")]`.

The intended way to fix this in a library was by multi-targeting the library to include a TFM supported by trimming, and to set `IsTrimmable` only on the supported TFMs. We got feedback (see #35528) that the warning was a painful break for multitargeted libraries, and that the warning shouldn't be produced in the first place for libraries following our guidance.

This change addresses it by silencing the warning for correctly multitargeted projects. The warning is silenced only when the multitargeting set includes a low enough supported TFM to ensure that the `IsTrimmable` assets will be consumed (instead of non-trimmable .NET Standard assets) in any supported app that references the library.

## Testing

Existing unit tests passed without changes. Added tests to validate the behavior for various multitargeted projects. Added tests to ensure that the changes don't break the behavior of projects that use custom `TargetFramework` values as aliases for `TargetFrameworkIdentifier` and `TargetFrameworkVersion`.

## Risk

Low to medium. This change reduces overall impact of the breaking change by limiting the scope of the warning, but adds complexity to the behavior. The complexity is unlikely to be surfaced to the developer, but is observable (it would be confusing to any developer who tried to understand the specific circumstances under which the warning was produced).

## Original description


The warnings introduced by
 #34077 and described in
 https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/8.0/trimming-unsupported-targetframework
 are unnecessary noise for projects that multitarget to include a
 TargetFramework that is compatible with
 trimming/AOT/single-file, and is a low enough version to ensure
 that it will be picked over any other TFM's assets when the
 library is consumed in a trimmed/AOT'd/single-file app.

This fixes the issue by detecting correctly multi-targeted libraries and suppressing the warnings in these cases.

Note that prior to the .NET 8 SDK, netstandard libraries would still get the `IsTrimmable` attribute embedded in the assembly when setting `IsTrimmable`, and could use trim analysis despite incomplete warnings due to unannotated ref assemblies. With the .NET 8 SDK this is no longer the case. Even for correctly multi-targeted libraries, the netstandard output assembly will no longer contain the `IsTrimmable` attribute. We debated whether it was worth warning in this case, but decided that the negative impact of the warning on library developers following the "golden path" was too large to justify keeping this as a warning.

This solution addresses a correctness problem that we wanted to avoid (the correctness problem: allowing an inadvertently non-"trimmable" netstandard asset to be consumed in a trimmed app that uses a supported TFM).

It's still possible to get into that situation in an app that explicitly references the netstandard assembly, or that targets an EOL TFM (causing it to consume the netstandard asset from a library that multitargets `netstandard2.0;net6.0` for example).

Fixes #35528
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-ILLink untriaged Request triage from a team member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

SDK reports a number of new errors when multi-targeting and enabling AOT/Trim analyzers
4 participants