[Source: http://geekswithblogs.net/EltonStoneman]
MSBuild is lacking in handy functions. You can check if a path exists and list the contents of a directory, but everything else has to be achieved with a task. You often find yourself choosing between clumsily stringing together tasks or writing a custom task to perform a specific function – even a simple one.
Rather than putting together a function library, I've got one simple task which takes C# code as a parameter and executes it. The code can consist of one or more statements, can read the runtime values of properties or item groups in the build, and returns a string output (currently it expects last the code statement to be like return x.ToString()).
So if you want to prefix text to a property, get the ticks of the current time or get a valid temporary file name, you can do it like this:
<Target Name="PropertyPrefix">
<CodeExecutor Statements='string prefix = ""_old.""; return string.Concat(prefix, ""$(StartDir)"");'>
<Output TaskParameter="Output" PropertyName="ExecutorOutput"/>
</CodeExecutor>
<Message Text="Output: $(ExecutorOutput)"/>
</Target>
<Target Name="NowTicks">
<CodeExecutor Statements='return DateTime.Now.Ticks.ToString();'>
<Output TaskParameter="Output" PropertyName="ExecutorOutput"/>
</CodeExecutor>
<Message Text="Output: $(ExecutorOutput)"/>
</Target>
<Target Name="GetTempFileName">
<CodeExecutor Statements='return Path.GetTempFileName();' Directives='System.IO'>
<Output TaskParameter="Output" PropertyName="ExecutorOutput"/>
</CodeExecutor>
<Message Text="Output: $(ExecutorOutput)"/>
</Target>
The task doesn't do anything complicated; it builds a class at runtime by injecting the code statements passed to it into a T4 template (as with the T4 task, in its current form it uses the TextTransform.exe tool), compiles it in memory with CodeDom and executes a static method which now contains the code statements.
You can pass it directives if your code uses anything more exotic than System, System.Collections.Generic etc, and give it the path to any existing assemblies the code needs to reference. I'll add it as part of a set of build tasks to CodePlex but until then the code for the task is here: MSBuild CodeExecutor task.