-
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
Setting BaseIntermediateOutputPath correctly in a SDK-based project is hard #1603
Comments
An easier way is to use the new <Project>
<PropertyGroup>
<BaseIntermediateOutputPath>C:\blah</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk/1.0.0" />
...
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk/1.0.0" />
</Project> See #1493 |
Has that syntax been implemented already? |
According to this diff it was: https://github.com/Microsoft/msbuild/pull/1492/files#diff-325c8a74f9ae27c1b3f8870e9cb64678L2310 |
For posterity: another option is using a |
Is there are "correct" solution for this? All I'm trying to do is move the <PropertyGroup>
<OutputPath>$(SolutionDir)\Build\$(ProjectName)\bin\$(Configuration)</OutputPath>
<BaseIntermediateOutputPath>$(SolutionDir)\Build\$(ProjectName)\obj</BaseIntermediateOutputPath>
</PropertyGroup> to the top of our .csproj files, but it appears that |
Both moving the imports as in #1603 (comment) and using a |
I don't have an imports section. This is just a simple .NET Core Console App and Lib solution. So the console app uses: <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
</Project> And I added the PropertyGroup in my first comment above this PropertyGroup. Is $(ProjectName) supposed to be valid or is there some other variable name I can use that is for the project's name? It's basically treating it like it's not set. Sorry I'm not clear on how to use the Directory.Build.props from that other comment/issue.. |
@Ziflin your two options are: Create a file named Or change your project file from the implicit imports model to explicit imports, so that you can control order. These are exactly identical: -<Project Sdk="Microsoft.NET.Sdk">
+<Project>
+ <Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
+ <Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project> After you've made the implicit imports explicit, you can add or move things around them to affect relative order. |
@rainersigwald Thanks for the help, but neither of those methods seem to have any effect on the fact that the <BaseIntermediateOutputPath>$(SolutionDir)\Build\$(ProjectName)\obj</BaseIntermediateOutputPath> And have it create a <BaseIntermediateOutputPath>$(SolutionDir)\Build\MyProject\obj</BaseIntermediateOutputPath> then it works as expected, but then I am unable to use a single |
Would |
That works perfectly! Sorry for the confusion. I saw that |
Even easier way is to generalize implicit imports placement. <Project Sdk="Custom.Sdk">
<PropertyGroup Evaluation="BeforeImplicitProps">
<BaseIntermediateOutputPath>..\..\Build</BaseIntermediateOutputPath>
</PropertyGroup>
...
<PropertyGroup Evaluation="AfterImplicitTargets">
<SomeImportantPropertyAfterTargets>Value!!!</SomeImportantPropertyAfterTargets>
</PropertyGroup>
</Project> would translate to this <Project>
<PropertyGroup>
<BaseIntermediateOutputPath>..\..\Build</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Custom.Sdk/1.0.0" />
...
<Import Project="Sdk.targets" Sdk="Custom.Sdk/1.0.0" />
<PropertyGroup>
<SomeImportantPropertyAfterTargets>Value!!!</SomeImportantPropertyAfterTargets>
</PropertyGroup>
</Project> Here, assume Custom Sdk uses common Sdk. This is helpful in creating custom .proj file with Sdk story to them like I can use any custom Sdk that uses common Sdk or itself. See Issue #1686 This is one way to fix the problem and It does it even before all the props and after all the targets, which would be useful for many debugging and logging scenarios. |
@Nirmal4G |
@jeffkl I know! But I want to set some properties (within the Project file) even before all the implicit props and have some targets after all the implicit targets, something along those lines! |
Where in the SDK props do we implicitly import .props from nuget packages? I wanted to look at it and see if I can come up with any other clever tricks for my Sdk props (trying to avoid Directory.Build.props for the time being), but I can't find the actual place where we look at the project assets and import them. |
See the lines here, the comments will tell you everything! msbuild/src/Tasks/Microsoft.Common.props Lines 22 to 63 in b38e4ce
And in the targets... msbuild/src/Tasks/Microsoft.Common.targets Lines 116 to 147 in b38e4ce
That is how nuget and other package managers (paket, etc) import the restored assets! |
You can modify |
It's not just
If those three don't point to the same directory -> 💩 The dangerous part is that those three properties are coming from different components so if you're not careful about which one is set where (=> set base..path early or set all), you won't have a good time. |
I had the same experience, and that was the solution that saved me, thank you for that, but if you are using the latest tools you can get away with those problems. But if you are using props/targets that are before the |
This was fixed with NuGet/NuGet.Client#2131 and #3059 |
To summarize, if I want to override my intermediate and output folders, it appears that following is needed in a Directory.Build.props file in my project or enclosing solution folder. Please call out if this is incorrect :) <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Common properties -->
<PropertyGroup>
<!-- SolutionDir is not defined when building projects explicitly -->
<SolutionDir Condition=" '$(SolutionDir)' == '' ">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), MySolution.sln))\</SolutionDir>
<!-- Output paths -->
<BaseIntermediateOutputPath>$(SolutionDir)bin\obj\$(Configuration)\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(SolutionDir)bin\obj\$(Configuration)\$(MSBuildProjectName)\</IntermediateOutputPath>
<MSBuildProjectExtensionsPath>$(IntermediateOutputPath)\</MSBuildProjectExtensionsPath>
<OutputPath>$(SolutionDir)bin\out\$(Configuration)\</OutputPath>
<OutDir>$(OutputPath)</OutDir>
<DocumentationFile>$(SolutionDir)bin\doc\$(Configuration)\$(MSBuildProjectName).xml</DocumentationFile>
</PropertyGroup>
</Project> |
Directory.Build.props copied from dotnet/msbuild#1603 (comment)
After reading this whole thread I cannot believe there is not a better solution. There should be a simple "ObjectPath" and "BinPath" in the project properties that can be filled out via the VS GUI. It is such a simple request to move the intermediate directories. Instead we need a separate file? It has taken me an hour of trying things and reading this thread to figure out how to do such a "simple" task. |
I feel your pain. Unfortunately that's the downside of making the common way super easy and clean - configuring things deep down get harder. mostly duet to history and the way things are layered. |
"configuring things deep down get harder. mostly duet to history and the way things are layered" Sorry but I don't consider changing the output directory for a project a "deep down" configuration. Sorry for yet another rant. I just wanted to build a simple .Core project and been trying to figure this issue out since yesterday. Could have literally written hundreds of lines in the meantime but I had to spend all this time reading threads, jumping to links, reading documentation and trying to put it all together just to figure out how to configure my build. Turns out you either let Visual Studio generate everything for you, or you need a PhD in how MS Build works internally. There's nothing, null, nada, zero in between. OK, rant over. |
@agaace You are absolutely right. I myself from .NET framework days, and I still want many changes that are proposed here. As long as there are docs documenting the migration for every new breaking change, I don't care about how many are there. Forgive me, team, but what he said, I 💯 agree. |
Makes things a little bit cleaner. Needed to modify the two project files to have different /obj folders, otherwise there are conflicts. Reference dotnet/msbuild#1603 (comment)
Has there been any progress on this? As far as I can tell, .NET 8 still requires you to use either the awkward separate file (which requires you to version control and keep track of yet more things, and doesn't let you set directories per-project) or the similarly obtuse MSBuild needs a way to set the intermediary directories of a project with a single, simple property in your .csproj file that functions as expected. Period, end of story, I don't care what needs to change to make that happen. |
I'm here in 2023 and just wanted to extend this answer, because it's quite tempting to use as-is, but I don't think all of it is needed anymore. I wanted to move the bin/ and obj/ folders outside of Source/ Source/
Output/
bin/
proj1/
...
obj/
proj1/
... There may be more efficient ways of compiling i.e. combining library output folders so avoid regenerating libs, but I've not gotten that far yet. I also have some older WPF projects in there that I'm more careful with. I've gotten the effect I wanted with the following configuration in a <OutputPath>$(SolutionDir)../Output/bin/$(MSBuildProjectName)</OutputPath>
<IntermediateOutputPath>$(SolutionDir)../Output/obj/$(MSBuildProjectName)</IntermediateOutputPath>
<BaseIntermediateOutputPath>$(IntermediateOutputPath)</BaseIntermediateOutputPath> The documentation destination doesn't have to be explicit anymore. Instead, you can use <GenerateDocumentationFile>true</GenerateDocumentationFile> With those directives, I no longer have any |
NuGet restore drops the project.assets.json file to the
$(BaseIntermediateOutpath)
. If a user customizes that by setting the property in a SDK-based project like so:Then the project.assets.json gets dropped to that folder correctly. However if there are nuget packages in this project that have tasks\targets then the generated project.nuget.g.props\targets are imported by Microsoft.Common.Props. Therefore BaseIntermediateOutputPath needs to be defined before the import of the common props and for that they have to know to expand the SDK import:
I don't know what we can do to fix this but logging an issue here so that atleast this serves as documentation for people running into this issue.
The text was updated successfully, but these errors were encountered: