-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
[Broken Build]: Property defined in .user project file (csproj.user) not available in build process #9131
Comments
With a single target framework:
However, if It has behaved this way since Visual Studio 2017 at least. I expect changing it now would break people's projects. Instead, you could perhaps add logic to the project file or to Directory.Build.props or Directory.Build.targets, to import a per-user file if it exists. |
Thanks for the insight. Just out of curiosity, where did you get that import order from? So, this is the default behavior for some time now, but is it really the expected one? Am I the only one to stumble upon this? And why does Microsoft.CSharp.CrossTargeting.targets not import $(MSBuildProjectFullPath).user? Is this a bug that became a feature? The documentation for user profiles does not say anything special about multi targeting projects, even though they are common nowadays. It basically only says:
Being silly my, I tried it this way and it obviously failed. |
One thing just came to my mind. It cannot be that the user project file is not imported, as its custom target is actually getting called. It is just, that the whole file (and therefore its properties) seem to be inside its own scope. Could it be, that the import order (the one listed above) is used for each defined target framework, but the the overall build process then ignores the user file? I mean, build is seemingly executed once for every target framework using the normal import order where Microsoft.CSharp.CurrentVersion.targets imports the user file thus executing the custom targets specified in it (hence the USER output). Those seem to run in their own scope. The overall build process on the other hand uses Microsoft.CSharp.CrossTargeting.targets that does not import the user file and therefore custom variables are empty. Did I get this right? |
By searching for file names in *.targets and *.props files of MSBuild, and by searching for "Importing project" in |
The DispatchToInnerBuilds target in Microsoft.Common.CrossTargeting.targets does that. msbuild/src/Tasks/Microsoft.Common.CrossTargeting.targets Lines 123 to 133 in 971bf70
Surprisingly, the search at https://source.dot.net/ does not find DispatchToInnerBuilds, although it finds other MSBuild targets. |
Microsoft.Common.CrossTargeting.targets also reads the |
Note that the /pp switch can show what is imported. If Visual Studio is setting global properties that influence what is imported you would have to pass those too. (I can't help with the discussion just making sure you're aware of that switch) |
Okay, thanks to the both of you for the answers and insights. I still think this is awfully complicated and the documentation is...well...lacking. As KalleOlaviNiemitalo already suggested above, I decided to go the route with a custom Directory.Build.targets file in the root of my repository that will load user project files for projects targeting multiple frameworks if they exist. In my opinion this should be the default behavior, whether a single or multiple frameworks are defined for a project. Here the code of the Directory.Build.targets file (the relevant part is line 12): <Project>
<!-- Import other Directory.Build.targets: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-by-directory?view=vs-2022#use-case-multi-level-merging -->
<PropertyGroup>
<ParentDirectoryBuildTargetsPath>$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)..\'))</ParentDirectoryBuildTargetsPath>
</PropertyGroup>
<ImportGroup>
<Import Condition="$(ParentDirectoryBuildTargetsPath) != ''" Project="$(ParentDirectoryBuildTargetsPath)" />
</ImportGroup>
<!-- Since the user project file is not imported for projects targeting multiple frameworks (https://github.com/dotnet/msbuild/issues/9131), manually import the file. -->
<Import Project="$(MSBuildProjectFullPath).user" Condition="'$(IsCrossTargetingBuild)' == 'true' and Exists('$(MSBuildProjectFullPath).user')" />
</Project> |
I'm curious, what settings do your users store in those files that need to be loaded in a crosstargeting build? |
Actually the properties in those user files have nothing to do with the An example would be a SignPackage property. It is evaluated after the regular |
Fixes #9131 Context As described on the issue, muti-targeted builds did not import the .user file on the outer build. This change makes the outer build import the .user file. Changes Made Added import reference to .user file in Microsoft.Common.CrossTargeting.targets . Testing Test is in SDK repo (dotnet/sdk#37192)
Fixes dotnet#9131 Context As described on the issue, muti-targeted builds did not import the .user file on the outer build. This change makes the outer build import the .user file. Changes Made Added import reference to .user file in Microsoft.Common.CrossTargeting.targets . Testing Test is in SDK repo (dotnet/sdk#37192)
Fixes #9131 Context As described on the issue, muti-targeted builds did not import the .user file on the outer build. This change makes the outer build import the .user file. Changes Made Added import reference to .user file in Microsoft.Common.CrossTargeting.targets . Testing Test is in SDK repo (dotnet/sdk#37192)
Issue Description
I am defining custom properties via a user project file as descripted here. The purpose is exactly the one outlined in the above documentation: I need to make temporary changes (that influence the build process down the line). Those changes mustn't be checked into source control. I have done this a couple of times already and it was always working as I would expect it to.
Today I started to work on a multi-target-framework library (e.g. .NET 6 and .NET Standard 2.0), where properties defined in my user file where not properly applied. If custom targets (e.g.
AfterTargets="Build"
andAfterTargets="Pack"
) that are defined outside of the user project file try to access those properties, they are only seeing empty strings.After testing and digging around for some time, I narrowed the strange behavior down to a project defining target frameworks via
TargetFrameworks
(with s) and not the singular oneTargetFramework
.Sample project
I attached a sample project ProjectPropertyTest that reproduces the behavior. It does not contain code, it only centers around its own and a user project file.
Project file
The
ProjectPropertyTest.csproj
contains all three possible combinations on how to define target frameworks. They can be (un)commented to check the different behaviors. Additionally it has a custom target WriteValueAfterBuild executedAfterTargets="Build"
that outputs a variable namedMyProperty
. The output is prefixed with DIRECT.User-Project file
The
ProjectPropertyTest.csproj.user
simply initializes the propertyMyProperty
with MyValue and also outputs it via another custom target WriteValueAfterBuildFromUser again executedAfterTargets="Build"
. The output is prefixed with USER.Tests
I checked this with three different ways to define target frameworks:
Single target framework defined via
TargetFramework
MyProperty
that is only defined inProjectPropertyTest.csproj.user
is properly written to the output for both targets.Multiple target frameworks defined via
TargetFrameworks
ProjectPropertyTest.csproj.user
is executed for every target framework and outputsMyProperty
properly.ProjectPropertyTest.csproj
is executed for every target framework and an additional time seemingly after all those targets where finished. The output ofMyProperty
is only correct for the execution of the after-build-target for each target framework, but it is missing for the additional execution.Single target framework defined via
TargetFrameworks
The problem is, that when using multiple target frameworks, I can not specify (or override) variables via a user project file.
Steps to Reproduce
Sample project: ProjectPropertyTest.zip
Manual steps:
TargetFrameworks
(with s) property to define the target frameworks for your project (even if it is just one).AfterTargets="Build"
in the project to output the property.Expected Behavior
The value of the property is written to the output.
Actual Behavior
The value of the property is not written to the output. It is just an empty string.
Ask us questions
Is this expected behavior? And if it is, how is the proper way to define properties via a user project file in multi-target libraries?
The text was updated successfully, but these errors were encountered: