MSBuild 4.0 Property Functions

 

In a community presentation I gave more than a year ago, I made the case for MSBuild being a somewhat complete programming language. The rationale was and still is MSBuild’s support for (global) variables, methods and (faked) recursion. I always wanted to build a simple "calculation” example but never did, since to my knowledge MSBuild was lacking basic arithmetic primitives and I did not want to fake it by using tasks or shell commands.

With MSBuild 4.0, support for so-called property functions is added, i.e. functions which can be used to determine the value of a property. A simple example of such a property function is put forward on the MSDN’s MSBuild page:

 <Today>$([System.DateTime]::Now)</Today>

This sample illustrates how a property called Today can be filled with the value of the current timestamp – Now.

Supported property functions fall into three different categories:

  • String (instance)
  • Static
  • MSBuild

property functions. Luckily, the MSBuild category, among other functionality, provides support for basic arithmetic computation (subtraction, division, etc.). This built-in supported helped drastically to devise an MSBuild project script, which allows the (somewhat naive) computation of the faculty of a positive integer number. Surely, the sample provided is somewhat simplistic and as mentioned in the source by no means optimised. However, I always wanted to build a calculator using MSBuild and maybe … :)

 <Project DefaultTargets="FacultyOf" 
         xmlns="https://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <!--
    Which faculty do we want to compute.
    At the moment there is no optimisation going on, such as:
      -early out if faculty to be computed is for number 1
      -keeping results in a storage to speed up computation of large faculties
    -->
    <Faculty>$(Faculty)</Faculty>
    <!--The intermediate result-->
    <IntermediateFaculty Condition="$(IntermediateFaculty) == ''">1</IntermediateFaculty>
    <!--The Loop-->
    <Loop Condition="$(Loop) == ''">$(Faculty)</Loop>
    <!--Debug Messages yes or no-->
    <Debug>False</Debug>
    <!--For pretty printing-->
    <InitialCall Condition="$(Loop) == '$(Faculty)'">True</InitialCall>
    <ResultCall Condition="$(Loop) != '0'">False</ResultCall>
    <ResultCall Condition="$(Loop) == '0'">True</ResultCall>
  </PropertyGroup>

  <Target Name="FacultyOf">
    <Message Text="Compute The Faculty Of $(Faculty)" 
             Importance="High" 
             Condition="$(InitialCall) == 'True'"/>
    
    <!--Debugging stuff-->
    <Message Text="IntermediateFaculty: $(IntermediateFaculty)" 
             Condition="$(Debug) == 'true'"/>
    
    <Message Text="$([MSBuild]::Multiply($(IntermediateFaculty), $(Loop)))" 
             Condition="$(Debug) == 'True'"/>
    
    <!--
    Invoke ourself until the stopping criterion is met
    until then multiply the intermediate faculty with the current loop
    -->
    <MSBuild 
        Condition="$(Loop) != 0"
        Properties="
                    Debug=$(Debug);
                    IntermediateFaculty=$([MSBuild]::Multiply($(IntermediateFaculty), $(Loop)));
                    Loop=$([MSBuild]::Subtract($(Loop), 1))"
        ContinueOnError="true"
        UnloadProjectsOnCompletion="True"
        Projects="recursiveMSBuild.proj"
    />
    <!--Output the final result-->
    <Message Text="Faculty of $(Faculty) Is $(IntermediateFaculty)" 
             Importance="high" 
             Condition="$(ResultCall) == 'True'"/>
    
  </Target>
</Project>