-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Don't use Targets* helper properties in libs #64500
Conversation
Tagging subscribers to this area: @dotnet/area-infrastructure-libraries Issue DetailsTargets* properties (i.e. Until now that behavior wasn't a problem because every libraries project To allow projects to use the TargetFramework property instead of In general, the guidance by the SDK/msbuild team is to not read from the Therefore these helper properties can't be used anymore for property In the very few projects which include netstandard2.0-X as a TFM, the This change makes it possible to migrate 200+ (ref+src) projects to use
|
7319bba
to
bbaf32a
Compare
src/libraries/System.Diagnostics.Process/tests/System.Diagnostics.Process.Tests.csproj
Show resolved
Hide resolved
Similar to your other PR, we need to document what our rules are and if possible try to catch incorrect use. I'm supportive of the restriction here, but it will be a very subtle distinction that you can use |
Agreed and I'm a bit worried about this as well. I can definitely add/update documentation to point out this restriction but I doubt this is something that can be protected/validated without writing an msbuild formatter/analyzer. Do you have any more ideas how to best roll this change out? Ideally all platform conditions, regardless of where they are applied on (property, item, import) would use the |
Might be worth it at some point if we have enough conventions / style to justify it.
Agreed. At first this is what I thought you were doing, but then I realized you were only doing PropertyGroups
I wonder how many of these we have? If it's small enough we could just say remove them all and make the few cases that use
I could imagine all our projects have some boilerplate that parses the platform. Right now you've copy-pasted that boilerplate ( |
bbaf32a
to
9ccbd45
Compare
I believe this change is now ready as well but let me do some additional validation and verification tomorrow. Also I'm waiting for the Helix test results to see if I got something wrong. |
9ccbd45
to
7857d09
Compare
0d0f27e
to
07a9651
Compare
That could be an option, yes. That said, it is not uncommon for a project that multi-targets to use the Targets* properties both for properties and for conditionally compiling items, so I suppose in those cases it would be weird to have different conditions for properties and items. That said it would be interesting to hear what @ViktorHofer thinks about that. Also, to be fair and something I didn't comment above, is that the fact that you need to look into the <ItemGroup Condition="'$(TargetsUnix)' == 'true'">
<!-- Add my unix compile items here --> Since your Unix build was actually your AnyOS build, so you would actually need to instead use: |
Initially I just switched properties over to use TPI instead of Targets* properties. Eric then later commented that it is confusing to have TPI conditions for properties and Targets* conditions for items and I agree with him on that. In addition to that I think it's valuable to use the same concept for platform conditions that customers follow and that we document: https://docs.microsoft.com/en-us/visualstudio/msbuild/property-functions?view=vs-2022#msbuild-targetframework-and-targetplatform-functions. Before .NET 5, TPI wasn't suitable to use as target frameworks didn't support encoding platforms in them and the Targets* helper properties had to be used.
What we are observing here is a limitation that exists in the product and should be fixed there. MSBuild/NuGet doesn't support checking compatibility between platforms yet. Example:
Of course. I wanted to keep the PR "simple" but let me add the change to better demonstrate the pros of this.
@stephentoub that was an unrelated clean-up. The |
Agreed. But until we get that functionality in the product, there's nothing wrong with us having some "helper" functions to make our code clean/readable. |
@stephentoub I reverted all the PNSE change. I thought those might be easier to read but apparently it's the other way around and I introduced unnecessary complexity.
@eerhardt I think mixing platform conditions (TPI + Targets*) is more confusing than applying platform conditions with the TPI property only (even though inheritance isn't supported with TPI and they might be more verbose). Imagine the following case: A project has platform specific properties and items:
vs.
Today only two Another fundamental problem with the libraries specific Lines 12 to 14 in 1c80769
TargetOS is one of the mobile OSs but not when the target framework platform is a mobile one. That means that if you compile this library i.e. on a Windows OS the condition will be false vs. when you compile the library on a mobile OS.
|
4c71480
to
be73f90
Compare
In general this seems like a good change, I don't think getting rid of the TargetsUnix inheritance thing is a big deal apart from the potential readability concerns that Stephen mentioned. |
So it seems like we reached an agreement on taking this change? If so, I will start prepping the email to send out to the contributors to notify them about it and merge it. |
No objection from me. |
@safern The .NET Framework leg is currently failing. The root cause is understood (currently the .NETFramework leg is overbuilding which will be fixed with dotnet/arcade#8459) and I will push a commit into this PR probably tomorrow, when the arcade update is consumable. Can you meanwhile please send out the mail and let me know here on GH when that's done? (I currently don't read my work mails 🌵). Thanks a lot everybody for the continued support and your feedback. @stephentoub I agree that your readability concern is an actual issue. Still, as it only affects a small number of projects I think we should live with it for the sake of the improvement that this PR offers, but drive towards a feature in the sdk/msbuild/nuget that eventually will allow us form compatibility conditions based on platforms (as proposed earlier in this thread). |
Yup, I'll do that now. |
Actually it is already late here on a Friday, so I think my email would just be missed by a ton of people, we don't you go ahead and merge over the weekend and I can send the email on Monday morning? |
66c8d67
to
86fdf97
Compare
This change makes it possible to migrate 200+ (ref+src) projects to use TargetFramework instead of TargetFrameworks which avoids the additional outer build evaluation and invocation which ultimately makes the overall build faster. Targets* properties (i.e. TargetsWindows, TargetsAnyOS, TargetsUnix, etc.) rely on the TargetFramework property which usually are set inside a project. The TargetFramework property is only available before a project specifies it if it's explicitly set in a props file or if the project is cross-targeting and the outer-build dispatches into the inner-build. During the dispatch, the TargetFramework property is passed in as a global property. Until now that behavior wasn't a problem because every libraries project cross-targeted (by setting the TargetFrameworks property) even though many only include a single TargetFramework (i.e. NetCoreAppCurrent). To allow projects to use the TargetFramework property instead of TargetFrameworks, the Targets* helper properties can't be calculated anymore early in a props file as the TargetFramework property isn't set at that time. In general, the guidance by the SDK/msbuild team is to not read from the TargetFramework property before the project sets it (in a property group). That effectively means that the TargetFramework property shouldn't be used in props files at all. Therefore these helper properties can't be used anymore for property conditions and I'm replacing their usage with TargetPlatformIdentifier comparisons for both properties and items. In nearly all cases, the Targets* helper properties can be replaced with TargetPlatformIdentifier checks on items and in the few cases where TargetsUnix or TargetsLinux marks multiple tfms as compatible, the exact tfms must be used instead for the TargetPlatformIdentifier comparison. Whenever a project needs to condition properties on the platform, I'm first setting the TargetPlatformIdentifier the same way the SDK sets it so that the SDK later doesn't need to set it again to avoid the additional expensive msbuild function call.
Use TargetFramework instead of TargetFrameworks property whenever a projects only targets a single target framework. This avoid unnecessary outer builds and evaluations and makes the build faster.
86fdf97
to
139129b
Compare
@safern just to close the loop on this, did you already send out the notice for this change? |
Thanks for the reminder, I just sent the notice out 😄 |
Thanks for sending the mail out Santi. I just quickly read over it and I fear that it might cause unnecessary confusion and might trigger negative reactions. I don't think me responding to it would be a wise choice as I'm still on leave for the next two months and as you are the sender so I kindly ask you if you would mind sending out a quick follow-up pointing people to the doc:
Thank you. |
Thanks, @ViktorHofer, I don't know if you missed it but I did include a pointer to the doc:
|
Yes I missed that part in your mail. Then any remaining confusion on this topic should already be covered by the link to the doc. Thanks Santi |
Depends on #64610, dotnet/arcade#8459
High level goals
TargetFramework
overTargetFrameworks
even when only a single target framework is used: [Question] Why test libraries that target single TFM use TargetFrameworkS (plural)? #47155. Because of that, we add ~250 project outer-build evaluations (100ms in average per outer build) to the overall build. These evaluations impact both incremental restore, incremental build and the overall build.Targets*
properties rely on theTargetFramework
property being passed in globally during the dispatch from an outer to the inner builds. During design time builds and eventually in other scenarios, the TargetFramework property isn't passed in globally and instead is dynamically injected after the first project's property group. This change will avoid the dependency on the global property and will better support tooling like Visual Studio and Visual Studio Code. Nasty hacks likeruntime/eng/BeforeTargetFrameworkInference.targets
Line 3 in 04bb6eb
More detailed summary
This change makes it possible to migrate 200+ (ref+src) projects to use
TargetFramework instead of TargetFrameworks which avoids the additional
outer build evaluation and invocation which ultimately makes the overall
build faster. As a side effect, this removes yet another artificial difference
between the libraries projects and customer projects. The conditions being
used in our projects are mostly
TargetFrameworkIdentifier
,TargetPlatformIdentifier
andIsTargetFrameworkCompatible
. As a .NETdeveloper I would use exactly the same conditions in my own project.
Targets* properties (i.e. TargetsWindows, TargetsAnyOS, TargetsUnix,
etc.) rely on the TargetFramework property which usually are set
inside a project. The TargetFramework property is only
available before a project specifies it if it's explicitly set in a props
file or if the project is cross-targeting and the outer-build dispatches
into the inner-build. During the dispatch, the TargetFramework property
is passed in as a global property.
Until now that behavior wasn't a problem because every libraries project
cross-targeted (by setting the TargetFrameworks property) even though
many only include a single TargetFramework (i.e. NetCoreAppCurrent).
To allow projects to use the TargetFramework property instead of
TargetFrameworks, the Targets* helper properties can't be calculated
anymore early in a props file as the TargetFramework property isn't set
at that time.
In general, the guidance by the SDK/msbuild team is to not read from the
TargetFramework property before the project sets it
(in a property group). That effectively means that the TargetFramework
property shouldn't be used in props files at all.
Therefore these helper properties can't be used anymore for property
conditions and I'm replacing their usage with TargetPlatformIdentifier
comparisons for both properties and items.
In nearly all cases, the Targets* helper properties can be replaced with
TargetPlatformIdentifier checks on items and in the few cases where
TargetsUnix or TargetsLinux marks multiple tfms as compatible, the exact
tfms must be used instead for the TargetPlatformIdentifier comparison.
Whenever a project needs to condition properties on the platform, I'm
first setting the TargetPlatformIdentifier the same way the SDK sets it
so that the SDK later doesn't need to set it again to avoid the
additional expensive msbuild function call.