Skip to content

Instantly share code, notes, and snippets.

@Nirmal4G
Last active September 29, 2023 10:04
Show Gist options
  • Save Nirmal4G/4f37c56d601c92a8e53927a3ee32c1ef to your computer and use it in GitHub Desktop.
Save Nirmal4G/4f37c56d601c92a8e53927a3ee32c1ef to your computer and use it in GitHub Desktop.
MSBuild Output Configurations
<Project>
<Target
Name="_FixupOutputPathsCheck"
BeforeTargets="_CheckForInvalidConfigurationAndPlatform">
<PropertyGroup>
<CheckForBuildPathMismatch>true</CheckForBuildPathMismatch>
<EnableBaseIntermediateOutputPathMismatchWarning>false</EnableBaseIntermediateOutputPathMismatchWarning>
</PropertyGroup>
</Target>
<Target
Name="_CheckForBuildPathMismatch"
Condition="'$(CheckForBuildPathMismatch)' == 'true'"
DependsOnTargets="_FixupOutputPathsCheck"
AfterTargets="_CheckForInvalidConfigurationAndPlatform"
BeforeTargets="Restore;Build;Clean;Rebuild;Test;Publish;Run">
<PropertyGroup>
<_BuildPathMismatchWarningText>The value of the property `BuildPath` was modified after it was used by MSBuild which can lead to unexpected build results.</_BuildPathMismatchWarningText>
<_BuildPathMismatchWarningText>$(_BuildPathMismatchWarningText) Tools such as NuGet will write outputs to the path specified by the `MSBuildProjectExtensionsPath` instead.</_BuildPathMismatchWarningText>
<_BuildPathMismatchWarningText>$(_BuildPathMismatchWarningText) To override this property safely, you must do so before it is defined, there by using `Directory.Build.props`.</_BuildPathMismatchWarningText>
<_BuildPathMismatchWarningText>$(_BuildPathMismatchWarningText) For more information, please visit https://go.microsoft.com/fwlink/?linkid=869650</_BuildPathMismatchWarningText>
</PropertyGroup>
<!--
Log a warning if `$(BuildPath)` was set in the body of a project after its default value was set in respective props.
Things might build in ths case but only when the other properties that depend on `$(BuildPath)` had not been set before the targets.
MSBuild cannot know if `$(BuildPath)` changing would cause problems, so tools like NuGet must set `$(CheckForBuildPathMismatch)` to `true`.
-->
<Warning Code="MSB3539" Text="$(_BuildPathMismatchWarningText)" Condition="'$(_InitialBuildPath)' != '$(BuildPath)'"/>
</Target>
</Project>
<Project>
<!--
We need to initialize `$(BuildPath)` separately and before the `MSBuild.OutputPaths.targets` import,
since `$(MSBuildProjectExtensionsPath)` uses it to import custom props from Package Managers and Tools.
-->
<PropertyGroup Label="Build">
<BuildFolder Condition="'$(BuildFolder)' == ''">build</BuildFolder>
<BuildPath Condition="'$(BuildPath)' == ''">$(BuildFolder)</BuildPath>
</PropertyGroup>
<PropertyGroup Label="Build" Condition="$([System.IO.Path]::IsPathRooted('$(BuildPath)'))">
<AppendProjectNameToBuildPath Condition="'$(AppendProjectNameToBuildPath)' == ''">true</AppendProjectNameToBuildPath>
<BuildPath Condition="$(BuildPath.Contains('..'))">$([System.IO.Path]::GetFullPath('$(BuildPath)'))</BuildPath>
<BuildPath Condition="'$(AppendProjectNameToBuildPath)' == 'true' AND !$(BuildPath.StartsWith('$(MSBuildProjectDirectory)'))">$([System.IO.Path]::Join('$(BuildPath)', '$(MSBuildProjectName)'))</BuildPath>
</PropertyGroup>
<PropertyGroup Label="Build">
<BuildPath>$([MSBuild]::EnsureTrailingSlash('$(BuildPath)'))</BuildPath>
<_InitialBuildPath>$(BuildPath)</_InitialBuildPath>
</PropertyGroup>
<!-- Set `$(DefaultItemExcludes)` property for output paths that should be excluded from the default items only when under project directory. -->
<PropertyGroup Condition="!$([System.IO.Path]::IsPathRooted('$(BuildPath)')) OR $(BuildPath.StartsWith('$(MSBuildProjectDirectory)'))">
<!-- Exclude build directory, by default -->
<DefaultItemExcludes>$(BuildPath)**;$(DefaultItemExcludes)</DefaultItemExcludes>
</PropertyGroup>
<!--
Prepare to import project extensions which usually come from packages. Package management systems will create a file at:
`$(MSBuildProjectExtensionsPath)\$(MSBuildProjectFile).<SomethingUnique>.<props/targets>`
Each package management system should use a unique moniker to avoid collisions. It is a wild-card import so the package
management system can write out multiple files but the order of the import is alphabetic because MSBuild sorts the list.
-->
<PropertyGroup>
<ProjectExtensionsFolder Condition="'$(ProjectExtensionsFolder)' == ''">ext</ProjectExtensionsFolder>
<MSBuildProjectExtensionsPath Condition="'$(MSBuildProjectExtensionsPath)' == ''">$([System.IO.Path]::Join('$(BuildPath)', '$(ProjectExtensionsFolder)'))</MSBuildProjectExtensionsPath>
<!--
Import paths that are relative default to be relative to the importing file. However, since `$(MSBuildProjectExtensionsPath)`
defaults to `$(BuildPath)` we expect it to be relative to the `$(BuildPath)` directory. So if the path is relative
it needs to be made absolute based on the `$(MSBuildProjectDirectory)` directory.
-->
<MSBuildProjectExtensionsPath>$([System.IO.Path]::Combine('$(MSBuildProjectDirectory)', '$(MSBuildProjectExtensionsPath)'))</MSBuildProjectExtensionsPath>
<MSBuildProjectExtensionsPath>$([MSBuild]::EnsureTrailingSlash('$(MSBuildProjectExtensionsPath)'))</MSBuildProjectExtensionsPath>
<_InitialMSBuildProjectExtensionsPath>$(MSBuildProjectExtensionsPath)</_InitialMSBuildProjectExtensionsPath>
</PropertyGroup>
<!-- Set Legacy properties only when we're not using the new SDKs with different output layouts -->
<PropertyGroup Label="Legacy" Condition="'$(UsingMSBuildSdk)' != 'true'">
<BuildDir>$(BuildPath)</BuildDir>
</PropertyGroup>
<PropertyGroup>
<ImportedMSBuildOutputPathsProps>true</ImportedMSBuildOutputPathsProps>
</PropertyGroup>
</Project>
<Project>
<!--
These are different than the defaults set by either `Microsoft.Common.targets` or the `Microsoft.NET.Sdk`.
These follow the following style, one within the Project and one common to all Projects within a Solution.
:Project
├───<Builds>
│ ├───<Binaries>
│ │ ├───Release
│ │ └───Debug-Linux
│ └───<Objects>
| |•••
└───<Publish>
|•••
:Solution
├───<Builds>
│ ├───Project.1
│ │ ├───<Binaries>
│ │ │ ├───Release
│ │ │ └───Debug-Windows
│ │ └───<Objects>
| | |•••
│ ├───Project.2
│ └───Project.3
└───<Publish>
├───Project.2.pkg (file)
└───Project.3.pub (folder)
|•••
-->
<Import Project="MSBuild.OutputPaths.props" Condition="'$(ImportedMSBuildOutputPathsProps)' != 'true'"/>
<PropertyGroup Label="Build">
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">Any</Platform>
<InterimOutputFolder Condition="'$(InterimOutputFolder)' == ''">obj</InterimOutputFolder>
<InterimOutputPath>$([System.IO.Path]::Join('$(BuildPath)', '$(InterimOutputFolder)'))</InterimOutputPath>
<InterimOutputPath>$([MSBuild]::EnsureTrailingSlash('$(InterimOutputPath)'))</InterimOutputPath>
<FinalOutputFolder Condition="'$(FinalOutputFolder)' == ''">bin</FinalOutputFolder>
<FinalOutputPath>$([System.IO.Path]::Join('$(BuildPath)', '$(FinalOutputFolder)'))</FinalOutputPath>
<FinalOutputPath>$([MSBuild]::EnsureTrailingSlash('$(FinalOutputPath)'))</FinalOutputPath>
</PropertyGroup>
<!-- Append `$(BuildContext)` with the properties that's used in multi-targeting to identify targeted builds -->
<PropertyGroup Condition="'$(Configuration)' != ''">
<BuildContext Condition="'$(BuildContext)' != '' AND !$(BuildContext.EndsWith('-'))">$(BuildContext)-</BuildContext>
<BuildContext>$(BuildContext)$(Configuration)</BuildContext>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)' != '' AND !('$(Platform)' == 'Any' OR '$(Platform)' == 'AnyCPU' OR '$(Platform)' == 'AnyOS')">
<BuildContext Condition="'$(BuildContext)' != '' AND !$(BuildContext.EndsWith('-'))">$(BuildContext)-</BuildContext>
<BuildContext>$(BuildContext)$(Platform)</BuildContext>
</PropertyGroup>
<!-- Append `$(BuildContext)` to Output paths, so that the output from the multi-targeted builds don't overwrite each other -->
<PropertyGroup Label="Build">
<InterimTargetPath>$([System.IO.Path]::Join('$(InterimOutputPath)', '$(BuildContext)'))</InterimTargetPath>
<InterimTargetPath>$([MSBuild]::EnsureTrailingSlash('$(InterimTargetPath)'))</InterimTargetPath>
<FinalTargetPath>$([System.IO.Path]::Join('$(FinalOutputPath)', '$(BuildContext)'))</FinalTargetPath>
<FinalTargetPath>$([MSBuild]::EnsureTrailingSlash('$(FinalTargetPath)'))</FinalTargetPath>
</PropertyGroup>
<!-- Set `$(PublishPath)` here, before `Microsoft.Common.targets`, to avoid a competing default there. -->
<PropertyGroup Label="Publish">
<PublishFolder Condition="'$(PublishFolder)' == ''">publish</PublishFolder>
<PublishPath Condition="'$(PublishPath)' == ''">$(PublishFolder)</PublishPath>
</PropertyGroup>
<PropertyGroup Label="Publish" Condition="$([System.IO.Path]::IsPathRooted('$(PublishPath)'))">
<AppendProjectNameToPublishPath Condition="'$(AppendProjectNameToPublishPath)' == ''">true</AppendProjectNameToPublishPath>
<PublishPath Condition="$(PublishPath.Contains('..'))">$([System.IO.Path]::GetFullPath('$(PublishPath)'))</PublishPath>
<PublishPath Condition="'$(AppendProjectNameToPublishPath)' == 'true' AND !$(PublishPath.StartsWith('$(MSBuildProjectDirectory)'))">$([System.IO.Path]::Join('$(PublishPath)', '$(ProjectName)'))</PublishPath>
</PropertyGroup>
<PropertyGroup Label="Publish">
<PublishPath>$([MSBuild]::EnsureTrailingSlash('$(PublishPath)'))</PublishPath>
</PropertyGroup>
<!-- Set `$(DefaultItemExcludes)` property for output paths that should be excluded from the default items only when under project directory. -->
<PropertyGroup Condition="!$([System.IO.Path]::IsPathRooted('$(PublishPath)')) OR $(PublishPath.StartsWith('$(MSBuildProjectDirectory)'))">
<!-- Exclude publish directory, by default -->
<DefaultItemExcludes>$(DefaultItemExcludes);$(PublishPath)**</DefaultItemExcludes>
</PropertyGroup>
<PropertyGroup Label="NuGet">
<RestoreOutputPath Condition="'$(RestoreOutputPath)' == ''">$(MSBuildProjectExtensionsPath)</RestoreOutputPath>
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$([System.IO.Path]::Join('$(PublishPath)', '$(Configuration)'))</PackageOutputPath>
<PackageOutputPath>$([MSBuild]::EnsureTrailingSlash('$(PackageOutputPath)'))</PackageOutputPath>
<NuSpecOutputPath Condition="'$(NuSpecOutputPath)' == ''">$([System.IO.Path]::Join('$(FinalOutputPath)', '$(Configuration)'))</NuSpecOutputPath>
<NuSpecOutputPath>$([MSBuild]::EnsureTrailingSlash('$(NuSpecOutputPath)'))</NuSpecOutputPath>
</PropertyGroup>
<!-- Set Legacy properties only when we're not using the new SDKs with different output layouts -->
<PropertyGroup Label="Legacy" Condition="'$(UsingMSBuildSdk)' != 'true'">
<!-- Build output paths -->
<BaseIntermediateOutputPath>$(InterimOutputPath)</BaseIntermediateOutputPath>
<IntermediateOutputPath>$(InterimTargetPath)</IntermediateOutputPath>
<BaseOutputPath>$(FinalOutputPath)</BaseOutputPath>
<OutputPath>$(FinalTargetPath)</OutputPath>
<!-- Publish output paths -->
<PublishDir>$(PublishPath)</PublishDir>
</PropertyGroup>
<PropertyGroup>
<ImportedMSBuildOutputPathsTargets>true</ImportedMSBuildOutputPathsTargets>
</PropertyGroup>
</Project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment