Click here to Skip to main content
Licence Public Domain
First Posted 20 Jun 2009
Views 8,212
Bookmarked 8 times

MSBuild: Factorial!

By Stephen Cleary | 20 Jun 2009 | Technical Blog
Using MSBuild Targets as general-purpose functions, complete with parameters and return values.

1

2

3
1 vote, 100.0%
4

5
4.00/5 - 1 vote
μ 4.00, σa 5.00 [?]

I've been doing some exploring of MSBuild as a programming language. There are some interesting results regarding mutability/immutability, but that's for another post.

This post is about functions. In particular, a Target may be invoked using the MSBuild task, so I'm exploring using Targets as functions. MSBuild can pass parameters to a Target by sending it Properties. Property changes are not propogated back to the caller, though, so getting a return value is a bit trickier.

It turns out that MSBuild does return one bit of information from a Target: its Outputs. It's possible to set the Outputs of a Target to a Property, and have that Target depend on another Target that sets that Property. In this way, it is possible to create a pair of Targets that can "calculate" the outer Target's Outputs.

By combining these approaches (setting Properties for arguments, and using the Target's Outputs as a return value), it is possible to treat a Target as a function.

To demonstrate, I wrote this program, which uses MSBuild to recursively calculate the factorial of the $(Input) property. Have fun playing!

 <Project ToolsVersion="3.5" xmlns="<a href="http://schemas.microsoft.com/developer/msbuild/2003">http://schemas.microsoft.com/developer/msbuild/2003</a>">
    <Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.tasks"/>

    <!-- Factorial program using MSBuild recursively -->
    <Target Name="Default">

        <!-- Display usage -->
        <Error Condition="'$(Input)' == ''" Text="Usage: msbuild factorial.proj [/nologo] [/clp:v=minimal] /p:Input=nnn"/>

        <!-- Argument error checking -->
        <MSBuild.ExtensionPack.Science.Maths TaskAction="Compare" P1="$(Input)" P2="1" Comparison="LessThan">
            <Output TaskParameter="LogicalResult" PropertyName="InputCheck"/>
        </MSBuild.ExtensionPack.Science.Maths>
        <Error Condition="'$(InputCheck)' != 'False'" Text="Input cannot be less than 1."/>

        <!-- Invoke the Factorial target with the current Input property -->
        <MSBuild Projects="$(MSBuildProjectFile)" Targets="Factorial" Properties="Input=$(Input)">
            <Output TaskParameter="TargetOutputs" ItemName="FactorialResult"/>
        </MSBuild>

        <!-- Display the result -->
        <Message Importance="high" Text="Result: @(FactorialResult)"/>
    </Target>

    <!-- The Factorial target uses FactorialCore to do the calculation, storing the result in FactorialResult -->
    <Target Name="Factorial" DependsOnTargets="FactorialCore" Outputs="$(FactorialResult)" />
    <Target Name="FactorialCore">

        <!-- If the input is 1, then the factorial is 1 -->
        <PropertyGroup Condition="'$(Input)' == '1'">
            <FactorialResult>1</FactorialResult>
        </PropertyGroup>

        <!-- If we don't know the result yet (i.e., the input is not 1), then calculate the factorial -->
        <CallTarget Condition="'$(FactorialResult)' == ''" Targets="CalculateFactorial"/>
    </Target>
    <Target Name="CalculateFactorial">

        <!-- Subtract 1 from $(Input) -->
        <MSBuild.ExtensionPack.Science.Maths TaskAction="Subtract" Numbers="$(Input);1">
            <Output TaskParameter="Result" PropertyName="InputMinus1"/>
        </MSBuild.ExtensionPack.Science.Maths>

        <!-- Determine the factorial of $(Input) - 1 -->
        <MSBuild Projects="$(MSBuildProjectFile)" Targets="Factorial" Properties="Input=$(InputMinus1)">
            <Output TaskParameter="TargetOutputs" ItemName="SubResult"/>
        </MSBuild>

        <!-- Multiply !($(Input) - 1) by $(Input) to get the result-->
        <MSBuild.ExtensionPack.Science.Maths TaskAction="Multiply" Numbers="@(SubResult);$(Input)">
            <Output TaskParameter="Result" PropertyName="FactorialResult"/>
        </MSBuild.ExtensionPack.Science.Maths>
    </Target>

    <!-- Maybe I just have way too much time on my hands... -->
</Project>

msbuild factorial.proj /nologo /clp:v=minimal /p:Input=5

Default:
Result: 120

msbuild factorial.proj /nologo /clp:v=minimal /p:Input=7

Default:
Result: 5040

Useless, but cool nonetheless.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication

About the Author

Stephen Cleary

Software Developer (Senior)
Nito Programs
United States United States

Member
Stephen Cleary is a computer programmer living in Northern Michigan. He enjoys reading and is actively involved in his church. On October 4th, 2008, he married Amanda Snell, and he is happy about that. Smile | :)
 
Programming blog: http://nitoprograms.blogspot.com/
(including the TCP/IP .NET Sockets FAQ: http://nitoprograms.blogspot.com/2009/04/tcpip-net-sockets-faq.html )
CodePlex project leads on:
Nito Async Library: http://nitoasync.codeplex.com/
Nito MVVM Library: http://nitomvvm.codeplex.com/
Personal home page: http://www.stephencleary.com/

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralNovel! PinmemberTrollslayer11:27 20 Jun '09  
GeneralRe: Novel! PinmemberStephen Cleary17:49 20 Jun '09  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120210.1 | Last Updated 20 Jun 2009
Article Copyright 2009 by Stephen Cleary
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid