Rather than building to the default folder structure, which is like
Solution.sln
Project1
bin <- project 1 output
obj <- project 1 intermediate output
Project2
bin <- project 2 output
obj <- project 2 intermediate output
I instead want to build it like
Solution.sln
bin <- project 1 AND 2 output
obj
Project1 <- project 1 intermediate output
Project2 <- project 2 intermediate output
I can do
msbuild "/p:OutputPath=../bin" "/p:IntermediateOutputPath=../obj/" Test123.sln
However, using "/p:IntermediateOutputPath=../obj/$(ProjectName)/"
does not work. Instead of creating a folder for each project, it creates one folder literally called $(ProjectName)
(I've read that most, but not all of these macros are actually a Visual Studio thing, rather than MSBuild magic).
How can I use a project-specific value (such as ProjectName
) in a property value (such as IntermediateOutputPath
) when building?
(Some background information:
Having one bin
folder on the solution level saves unnecessary copying of output files, which quickly amass over 100 MB in large solutions. Furthermore, it keeps the source folders clean so they can be read-only.
I still want separate obj
folders though, because who knows what goes in there - might be the same file names for different projects.)
You can override the CustomAfterMicrosoftCommonTargets property of the Microsoft.Common.targets file. It's allowing injecting custom targets into projects and performs some actions.
The entry point of the build process is Make.targets
:
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"; DefaultTargets="EntryMSBuild">
<ItemGroup>
<Project Include="**\*.csproj"/>
</ItemGroup>
<Target Name="EntryMSBuild">
<Message Text="-----Entry-----" Importance="high"/>
<Message Text=" Rebuild " Importance="high"/>
<Message Text="-----Entry-----" Importance="high"/>
<MSBuild Projects="@(Project)" Targets="rebuild" Properties="CustomBeforeMicrosoftCommonTargets=$(MSBuildThisFileDirectory)Setting.targets"/>
</Target>
</Project>
In Setting.targets
defined IntermediateOutputPath
and OutputPath
for each projects:
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<IntermediateOutputPath>..\Global\obj\$(MSBuildProjectName)</IntermediateOutputPath>
</PropertyGroup>
<PropertyGroup>
<OutputPath>..\Global\bin\</OutputPath>
</PropertyGroup>
</Project>
As a result, you get the desired structure:
Solution.sln
bin <- project 1 AND 2 output
obj
Project1 <- project 1 intermediate output
Project2 <- project 2 intermediate output
Tested on solution with two projects under .net46
and .netstandard2.0
. MSBuild
15.6.82.30579
You don’t need store custom targets under C:\Program Files[(x86)]\microsoft visual studio\2017\xxx\msbuild\15.0
or any predefined path. You override CustomBeforeMicrosoftCommonTargets
property that already defined in Microsoft.Common.targets
and injected in the project by default.
From Microsoft.Common.targets
comment:
This file defines the steps in the standard build process for .NET projects. It contains all the steps that are common among the different .NET languages, such as Visual Basic, and Visual C#.