/p property values are immutable - (sort of)....

Hello there - sorry for the long absence, but we're back and hopefully I'll keep this going again.

So there was a recent forum post about how property values that are specified from the command line are immutable - i.e. it is not straightforward to change them from within the project file. Well, this is mostly true, but there are exceptions.

Let's take an example:

If I've got a property in my project file defined like this,

<PropertyGroup>

  <Configuration>Debug</Configuration>

</PropertyGroup>

then, calling msbuild with /p:Configuration=Release will override this value (or alternatively set the value if it is not defined) for all projects that are being built. Properties that we pass in with a /p switch on the command line are known as "Global Properties" and so it will flow down to all projects that are being built - so if you were building a solution file, all projects will get the overridden value of this property.

Now, let's say that you have this defined in your project file:

<PropertyGroup>

<AppPath Condition=" '$(AppPath)' == '' ">bin\debug\</AppPath>

<AppPath Condition=" !HasTrailingSlash('$(AppPath)') ">$(AppPath)\</AppPath>

</PropertyGroup>

What I am doing here is that I am defining a default value for AppPath property if AppPath is not defined. Additionally, I want to set it up so that if someone has specified an AppPath property, then if it does not already have a trailing slash, I'd like to add it. Looks reasonable, correct?

Actually this will not work as you expect - and is something to watch out for. What happens when you specify something with a /p:AppPath=blah\ is that all in-project property definitions for AppPath are ignored (or essentially overridden), and so none of the in-project property definitions take effect - even the ones with the condition. This makes sense if you think about it because otherwise what exactly are we supposed to override? Would that be the first definition of the property? Or should we override only those values of the property that are checking against an empty value? There isn't a clear answer. We override all property definitions and what is passed in is the effective value.

Fortunately, there is a way around this if it causes you grief - but it isn't declarative like PropertyGroup. You have to use the CreateProperty task inside a target where you intend to process the property, like so:

<CreateProperty Value="$(AppPath)\" Condition=" !HasTrailingSlash('$(AppPath)') ">

  <Output TaskParameter="Value" PropertyName="AppPath" />

</CreateProperty>

Alternatively (and perhaps as a better approach), you can use a property name like AppPath as the public property you expect to override from th command line, but filter it out into a different property inside your targets that you then use for doing the actual work - like this:

<PropertyGroup>

  <_validatedAppPath>$(AppPath)</_validatedAppPath>

  <_validatedAppPath Condition=" '$(_validatedAppPath)' == '' ">bin\debug\</_validatedAppPath>

  <_validatedAppPath Condition=" !HasTrailingSlash('$(_validatedAppPath)') ">$(_validatedAppPath)\</_validatedAppPath>

</PropertyGroup>

In this case, you validate the /p value and store it in an internal property that is validated, and never directly overridden from the command line.

Hope this helps, and clears some confusion on "strange behavior" if you ever encountered it.

[ Author: Faisal Mohamood ]