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>