Psake: In search of a better build system for .Net
- select the contributor at the end of the page -
Setting up a good build pipeline has never been easy. For some reason, it always seems more difficult than it should be; after all, people have been building software for decades so you'd think we'd have a solution by now. But the task doesn't always have to be a daunting one thanks to alternative tools like psake. Let's take a look at how it works and why you'll probably want to start using it right away.
Where to start
First off, in the .Net world you have a few options. The core “building” part of your pipeline will typically always use MSBuild. Given this, you could write your own MSBuild “script” to orchestrate your builds, tests, test coverage, packaging, clean-up tasks, and the like. But XML is not a scripting language, and it should be used only to describe things, not do things.
You could use NAnt as an alternative. It's a bit easier to use than raw MSBuild scripts, but it still suffers from the same problems, as it's still XML-based. Moreover, it doesn’t look like it has been touched since June 2012 and the highest version of the .Net runtime it supports is 4.0, which rules it out for most people.
Rake is another alternative, implemented in Ruby. You get the same concepts of tasks and dependencies as you do with MSBuild and NAnt, but expressed in standard Ruby syntax. This is a huge step up as you avoid the “angle-bracket tax” associated with executable XML by having a real programming language at your disposal. But Ruby may be a bit too foreign to .Net developers, and also requires you to set up Ruby and all the required Ruby gems on Windows just to run Rake.
A few years ago I came across psake, another tool which seems to fix all those problems. Psake is also a build automation tool, largely inspired by Rake, but written in PowerShell. This is great for .Net teams since most people will already be somewhat familiar with PowerShell (and you can access the full .Net framework from it). It also integrates nicely with external tools like MSBuild, test runners and packaging tools, as PowerShell can basically be considered as a very powerful replacement for the command line.
How does psake work?
Just like MSBuild, NAnt or Rake, psake’s syntax provides a nice dependency pattern. A psake script would contain a bunch of tasks, which are basically just function calls. And for each of those tasks you can specify other tasks as dependencies. Once you execute your script, psake will figure out the order of each task and make sure it runs.
In this example, the Test task depends on Compile, which itself depends on Clean. So, if I run this script, the tasks will run in this order: Clean, Compile and Test. At this point, there's nothing to write home about, as this is still very basic, so let's dig deeper.
For each task, you can define a set of required variables, so your script would throw an exception if those variables are not initialized. You can also define a pre-condition in order to skip a specific task if it is not satisfied. This is what I typically use as a task to run my xUnit tests:
This task depends on the Compile task (as I need something to test), but it will run only if the path stored in “$publishedxUnitTestsDirectory” exists (this is where my test projects get built). So, if there are no xUnit projects in my solution (this should never happen, right?) this task will be skipped and the script will continue on to the next task.
I have also set up two required variables. If the “publishedxUnitTestDirectory” or “xUnitTestResultsDirectory” are not set, the script execution will stop here with an error. You can also perform those checks earlier on to make sure it will fail early if something isn't right. To do this, simply use the Assert function.
Usually, I like to start my psake scripts with an Init task which will perform some basic validation.
In this example, the first check will make sure that my build configuration is either Debug or Release. The second will check that the build platform is set to x86, x64 or Any CPU. And finally, the last three will verify that the tools required by the script are available where I said they would be. In a case where any of this is not true, the script will fail, displaying the error message I provided.
Speaking of external t2ools, psake also provides a useful helper function to handle errors when running executables. The Exec function wraps around your external tool calls and will check their exit code. If the exit code is not 0, psake will throw an exception. This is what a call to myTool would look like without the Exec function:
First, you need to reset the global last exit code to make sure you're not getting the exit code of a previous call. After that, you can run your executable. Finally, you need to check the global last exit code and throw an exception if it is not 0. As you can imagine, this can get tedious if your script has numerous calls to external tools.
Now, with the Exec function, the previous example would look like this:
Much easier to write and understand, right?
More than a build tool
You can also nest psake scripts, making psake really modular and flexible. Even though psake’s initial goal was to solve the build pipeline problem, it can be used in a variety of other scenarios, like a deployment automation and back-end processing, to name just two. It really is more of an orchestration framework.
Where to get psake
Psake is currently distributed in a few different ways. The source code and packaged releases are available on GitHub. I think the more powerful option is to get it directly from NuGet. Using this option, you could create your own NuGet package containing a generic psake script and define the psake NuGet package as a dependency. Then, you could just drop in your entire build pipeline to a new solution every time you need it with minimal setup. This is where psake really shines. It's just a PowerShell module, and all Windows machines would have PowerShell installed, making your script super portable and easy to use.
If you're interested in learning more about psake, check out my Pluralsight course Continuous Integration with psake and TeamCity: Getting Started.