-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
net5.0 TFM defines both NET5_0 and NETCOREAPP3_1 #13377
Comments
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. |
Simpler repro:
Output:
It looks like both NETCOREAPP3_1 and NET5_0 are being defined by the net5.0 TFM. |
This also reproduces with the 5.0.100-rc.1.20452.6 SDK. |
Note that there is no need to multi target to repro the issue. Any app with |
Is is so apps with |
|
Tag @marcpopMSFT |
Wow, it looks like this behaviour (which violates the Principle of Least Surprise) is intentional sdk/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.BeforeCommon.targets Lines 214 to 221 in 9b383d6
Why❔ And why isn't there an |
PR: #11236 "In order to make it easier to update code, especially when doing multi-targeting, we should make them additive, so that when targeting net6.0 both NET6_0 and NET5_0 are defined." Surprise! |
It's fine if the SDK defines both |
ae54a52 introduced this behaviour. |
As @jaredpar asked, how should we detect the TFM is exactly The options aren't pretty:
or use #if !NET5_0 && NETCOREAPP3_1 everywhere you'd previously use #if NETCOREAPP3_1 and be very careful when attempting to detect new TFMs e.g. the following won't error out from here on #if NETCOREAPP5_0
internal static readonly Version Http2Version = HttpVersion.Version20;
internal static readonly Version Http11Version = HttpVersion.Version11;
#elif NETCOREAPP3_1
internal static readonly Version Http2Version = new Version(2, 0);
internal static readonly Version Http11Version = new Version(1, 1);
#else
#error A target framework was added to the project and needs to be added to this condition.
#endif |
To clarify why we thought this was a good idea: We think that having compilation constants that mean “version X or greater” will lead to more maintainable code than having constants that are exact matches. If you write conditional code today for .NET 5: #if NET5
// Call .NET 5 API
#else
// Fall back to .NET Core 3.1 API
#endif Then you should expect that the .NET 5 code would continue to work for .NET 6. You shouldn’t need to write an #if today that targets exactly .NET 5 but no later versions. When .NET 6 comes out, you should be able to retarget your project to it (or add it as a new target) without having to audit all of your conditional compilation code. With the old way of defining these constants, if you compiled the code for .NET 6 then you would have gotten the .NET Core 3.1 behavior unless you updated the #if statements. We recognize that a lot of developers won’t expect the new behavior because it is a change. However, we aren’t changing what gets defined for existing target frameworks such as .NET Core 3.1 or .NET Framework 4.8. So they will encounter the new behavior when they are adding support for .NET 5, rather than us breaking their existing code. |
And the previous iteration of the spec did just that, defined a 5.0 or greater definition. |
This looks like a bad idea. If we introduce this it should be done with a different pattern. |
This isn't a problem in theory, but it only works if applied from the beginning. Since the pattern did not apply to |
Given the new model how can I use an #if !NET6_0 && NET5_0 Is this the recommendation that we are giving to our customers? That seems odd because it's essentially getting us to commit to future releases and future TFM at the moment we release a given SDK. This point has been raised a few times but I haven't seen a good answer to it. |
Presumably the So the question is when adding a newer TFM should it undo all everything that was applied for the previous latest and you need to revisit all the code to add the newer framework e.g.
Or to only disapply those ones which no longer apply?
Which would be the higher count? |
Woe be unto our customers if we release a |
Or from the alternative approach would |
VB supports MATH in #If and #Constants have values beyond True and False but I don't know what value NET5_0 actuals has. If its True and False writing reasonable code is extremely ugly. For the record at least for VB 3.x is not a superset of 4.X. nor is it a subset, 3.x has features not in Framework and is missing a lot that is in Framework. |
.NET Core 3.0 is EOL. The .NET 5 SDK defines both NETCOREAPP3_1 and NET5_0 for net5.0 builds, so we have to work around that: dotnet/sdk#13377.
.NET Core 3.0 is EOL. The .NET 5 SDK defines both NETCOREAPP3_1 and NET5_0 for net5.0 builds, so we have to work around that: dotnet/sdk#13377.
.NET Core 3.0 is EOL. The .NET 5 SDK defines both NETCOREAPP3_1 and NET5_0 for net5.0 builds, so we have to work around that: dotnet/sdk#13377.
This has got to be handled in a different way, somehow. It's a breaking change in people's expectations of how things work @dsplaisted - even though the new behavior is better, that's not sufficient to change how it has behaved from the start without addressing the concerns listed by people above. If we had a time machine, it would be a different story : ) I'd advocate for defining a new convention like |
@ericsampson I love the concept assuming that NET5_0 means all the . releases of .NET5_0 including 5.01 but not NET5_1. Maybe there is an option for NET5_*+ and NET5_0+. Or for VB define NET5_1 as a Number and let developers/compiler do the Math. I am assuming that something in the toolchain is doing the Math and what is in Source it is NET5_1 but I do think that also will lead to confusion as that would be invisible to most developers unless it was exposed by Visual Studio in the Framework selection. |
We've reviewed the feedback both from external and internal customers and for net5.0, we will remove the change that defines NETCOREAPP3_1 for this release. @terrajobst will own developing a new proposal for a future SDK release. For now, customers can use Based on our analysis of existing projects, most customers use these symbols as >= so we want to provide that option. However, we recognize that changing the previous behavior that existed for 3.1 and before will impact some customers and cause confusion. Thanks all for the feedback. |
Thanks @marcpopMSFT, definitely appreciate that the feedback was listened and taken into account. The goal is laudable, and I look forward to the new proposal. Cheers |
Fixed in #13615 |
Not sure I like this change. I can't think of a single line of code in my huge multi-targeted project where NETCOREAPP3_1 code isn't meant to be compiled for NET5_0 as well. This change really wreaks havoc on my code base.
That just doesn't make any sense what so ever. |
I put code in the |
We got strong feedback that the change wasn't what people expected and did not work for some coding patterns. We still want to offer something like NET5_OR_HIGHER. @terrajobst is supposed to drive that design. |
I'm grateful that this problem was fixed. As a opposite anecdote, I had multiple lines of code where I was able to simplify Based on the behaviour of previous SDKs I had landed on a solution similar to the one @tmds described: an
This is highly compatible with all future TFMs and (now) doesn't break when |
Description
When building a project that cross compiles between net5.0 and netcoreapp3.1 there's an ordering problem with the
#if
's. If the NETCOREAPP3_1 condition is put first then the net5.0 TFM will compile using it, but if the NET5_0 condition is first then that is choosen.Configuration
5.0.100-preview.8.20417.9 SDK
Windows 10 2004 (19041.450) x64
Regression?
Yes. This was working in prior preview SDKs when using NETCOREAPP5_0.
Other information
Program.cs:
csproj
Output:
The text was updated successfully, but these errors were encountered: