Bending .NET - Improved Common Flat Build Output
or how a human rebel helped contain renegade program.
In this post, part of the Bending .NET series, I improve upon Bending .NET -
Corrected Common Flat Build Output, which had the
issue of having to include a common props
-file at the end of each
csproj
-file. This is no longer necessary and hence the coast is clear.
Source: pixabay
Third time is the charm 😅. A kind reader of the previous blog post
reached out to me on
github with a simple
trick involving BeforeTargetFrameworkInferenceTargets
, so this is a quick
update for that. Thanks, Stefan!
To recap, we had to add a new props
-file OutputBuildProject.props
and
then include this in each csproj
-file as for example shown below.
1
2
3
4
5
6
7
8
9
10
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)..\OutputBuildProject.props" />
</Project>
This was unfortunate since it means we can’t just copy/paste the new props into
a parent directory to get common flat build output, but have to “dirty up” the
project files. Fortunately, the SDK MSBuild files have various import points or
hooks that we can leverage. One of them is the
BeforeTargetFrameworkInferenceTargets
property that let’s us define a file to
be imported just before target framework inference occurs in
Microsoft.NET.TargetFrameworkInference.targets.
1
2
3
<!-- Hook for importing custom target framework parsing -->
<Import Project="$(BeforeTargetFrameworkInferenceTargets)"
Condition="$(BeforeTargetFrameworkInferenceTargets) != ''" />
The key here is this a normal import and hence we can define properties as we
would normally. It also occurs after the TargetFramework
property is
defined.
1
2
Note that this file is only included when $(TargetFramework)
is set and so we do not need to check that here.
And after the project file is imported. Import order is partially covered in Document the import order of the common msbuild extension points.
Therefore, we can simply change OutputBuildProps.props
to define this
property to point to the OutputBuildProject.props
as shown below.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<Project>
<PropertyGroup Label="OutputBuildProps">
<Platform Condition="$(Platform) == ''">AnyCPU</Platform>
<Configuration Condition="$(Configuration) == ''">Debug</Configuration>
<!-- Custom properties -->
<BuildDir>$(MSBuildThisFileDirectory)..\build\</BuildDir>
<ProjectBuildDirectoryName>$(MSBuildProjectName)_$(Platform)_$(Configuration)</ProjectBuildDirectoryName>
<OutputPathWithoutEndSlash>$(BuildDir)$(ProjectBuildDirectoryName)</OutputPathWithoutEndSlash>
<BaseOutDir>$(OutputPathWithoutEndSlash)</BaseOutDir>
<BasePublishDir>$(MSBuildThisFileDirectory)..\publish\</BasePublishDir>
<!-- MSBuild defined properties redefined -->
<BaseIntermediateOutputPath>$(BuildDir)obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
<BaseOutputPath>$(BuildDir)bin\$(MSBuildProjectName)\</BaseOutputPath>
<PackageOutputPath>$(BaseOutDir)</PackageOutputPath>
<BeforeTargetFrameworkInferenceTargets>$(MSBuildThisFileDirectory)OutputBuildProject.props</BeforeTargetFrameworkInferenceTargets>
</PropertyGroup>
</Project>
With this we can remove the imports in the csproj
-files so these are nice and
clean again.
1
2
3
4
5
6
7
8
9
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
The build output is exactly the same as discussed in the previous blog post and code navigation still works in Visual Studio. Great.
Back to siphoning energy off rebel humans (thanks for helping out!).
PS: Example source code can be found in the GitHub repo for this blog nietras.github.io and as a zip-file CommonFlatBuild.zip.