This repo demonstrates a common hazard of evaluating $(Configuration)
and $(Platform)
too early in MSBuild.
These properties don't have any special meaning, so they're empty by default just like anything else.
They will always get assigned a value when building a .sln
due to solution configuration mapping, but never when building a project file directly.
This creates a hazard that not a lot of people are aware of, and it can be hard to notice if you're used to working from Visual Studio rather than the command line. (And even if you use the CLI, you might only test things by building the .sln
.)
Depending on the context where the properties are used, the problems introduced by this can range from annoying (build output being barfed in weird spots) to hard to notice (like malformed NuGet package metadata) to destructive (like a clean target that unintentionally gets overscoped.)
As a bonus, there's an additional hazard caused by the convention that SDKs will typically give the properties a default value, but it happens too late for them to be used in Directory.Build.props
. (In .NET it happens in Microsoft.NET.Sdk.props
, in MSVC it happens in Microsoft.Cpp.Default.props
.)
This means if you're moving things from your csproj
or MSVC property sheets over to a common location imported via Directory.Build.props
, you might be unknowingly introducing the same non-obvious bug.
You can see all three of these for yourself using the demo in this repo.
It prints the values of Configuration
and Project
as evaluated in:
- When
Directory.Build.props
is evaluated (EARLY
) - When
EarlyConfigurationPlatform.csproj
is evaluated (PROJ
) - When the a target is run right before
Build
/Restore
(LATE
)
If you build the solution using dotnet build
, you'll see that the configuration and platform is always assigned:
EARLY Configuration = 'Debug' Platform = 'AnyCPU'
PROJ Configuration = 'Debug' Platform = 'AnyCPU'
LATE Configuration = 'Debug' Platform = 'AnyCPU'
If you build the project using dotnet build EarlyConfigurationPlatform
, you'll see that the configuration and platform was not present when evaluated in Directory.Build.props
:
EARLY Configuration = '' Platform = ''
PROJ Configuration = 'Debug' Platform = 'AnyCPU'
LATE Configuration = 'Debug' Platform = 'AnyCPU'
Overall, this is why you'll always find this snippet in Directory.Build.props
written by me (or the first file it imports):
<!-- Default configuration and platform when not present -->
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<Platform Condition="'$(Platform)' == ''">AnyCPU</Platform>
</PropertyGroup>