Click here to Skip to main content
12,893,816 members (53,756 online)
Click here to Skip to main content
Add your own
alternative version

Stats

21.4K views
10 bookmarked
Posted 20 Jun 2009

MSBuild: Factorial!

, 20 Jun 2009 Public Domain
Rate this:
Please Sign up or sign in to vote.
Using MSBuild Targets as general-purpose functions, complete with parameters and return values.

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"/>

    <!--<span class="code-comment"> Factorial program using MSBuild recursively --></span>
    <Target Name="Default">

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

        <!--<span class="code-comment"> Argument error checking --></span>
        <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."/>

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

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

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

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

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

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

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

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

    <!--<span class="code-comment"> Maybe I just have way too much time on my hands... --></span>
</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

Share

About the Author

Stephen Cleary
Software Developer (Senior)
United States United States
Stephen Cleary is a Christian, husband, father, and programmer living in Northern Michigan.

Personal home page (including blog): http://www.stephencleary.com/

You may also be interested in...

Comments and Discussions

 
GeneralNovel! Pin
Trollslayer20-Jun-09 10:27
memberTrollslayer20-Jun-09 10:27 
GeneralRe: Novel! Pin
Stephen Cleary20-Jun-09 16:49
memberStephen Cleary20-Jun-09 16:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    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 | Terms of Use | Mobile
Web02 | 2.8.170424.1 | Last Updated 20 Jun 2009
Article Copyright 2009 by Stephen Cleary
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid