The Stochastic Game
Ramblings of General Geekery

CreateProperty task in a skipped target

In MSBuild, you can specify inputs and outputs for target so that the target is skipped when the outputs are more recent than the inputs. However, you get a strange behaviour when you put a <CreateProperty> task inside such a target.

Let’s look at the following MSBuild project:

<Project DefaultTargets="DoIt" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <WhyIsThisCreated>everything is okay</WhyIsThisCreated>
  </PropertyGroup>

  <Target Name="DoIt" DependsOnTargets="CreateTestFiles; DoStuffMaybe">
    <Message Text="I say: $(WhyIsThisCreated)" />
  </Target>

  <Target Name="CreateTestFiles">
    <WriteLinesToFile Lines="input" File="input.txt" Overwrite="true" />
    <WriteLinesToFile Lines="output" File="output.txt" Overwrite="true" />
  </Target>

  <Target Name="DoStuffMaybe" Inputs="input.txt" Outputs="output.txt">
    <Error Text="We shouldn't be there!" />
    <CreateProperty Value="oh my" Condition="">
      <Output TaskParameter="Value" PropertyName="WhyIsThisCreated"/>
    </CreateProperty>
  </Target>

</Project>

This project creates two files, “input.txt” and “output.txt”. Then, the “DoStuffMaybe” target is tentatively run, but is skipped because “output.txt” is newer. We make super sure that this target won’t be run by adding an <Error> task in there.

But when you run this project, you get the following output:

msbuild output

Somehow, the <CreateProperty> task was processed, and the value of $(WhyIsThisCreated) was set to “oh my”!

I don’t really know what’s going on, but the solution to this problem is, as it is the case most of the time, to read the documentation. The <CreateProperty> page on MSDN informs us that another property is available on the task:

ValueSetByTask

Optional String output parameter.

Contains the same value as the Value parameter. Use this parameter only when you want to avoid having the output property set by MSBuild when it skips the enclosing target because the outputs are up-to-date.

If we replace TaskParameter=“Value” by TaskParameter=“ValueSetByTask” in the MSBuild project, we finally get the expected result:

msbuild output

This RTFM saying never gets old, does it?