A piece of Cake!

22 September 2017 - .NET , Cake

This is something that's been on my "to blog about" list for quite a while - so I thought it was about time! I've been using the Cake Build system for a few different projects now, and it has quickly become my go-to method of building and deploying my projects.

Logo

So what is it?

First of all, unfortunately, it's not about food. It did take a lot of will-power to not make the obvious cake jokes, but I'll refrain!

At a very basic level, you have a file with a list of tasks to be run. Each task should do a single discrete job. Typically, this will be building and deploying your project, as well as running various automated tests.

So for example, you might have the following different tasks ...

  1. NuGet Restore
  2. Build
  3. Run Unit Tests
  4. Publish
  5. Deploy
  6. Send Slack notification

This list of tasks is defined in a file called build.cake, and each task is written in C#. If you've used other similar task runners - eg. Fake, Rake, Gulp, etc - the concept is very similar, except rather than F#, Ruby, or JS - Cake uses the awesome C#!

Each task can specify other tasks that it's dependent on - which means that rather than your typical script which runs from top to bottom - your Cake tasks actually form a dependency graph. So you can look at each task individually and think "what other tasks are a prerequisite for this task?". The order that each task appears in the build.cake file is irrelevant.

You can then run them from the command line using either build.ps1 or build.sh (which get created for you by Cake). Unless you explicitly say otherwise, it'll find a task called Default, then build the dependency graph to determine what tasks to build, and in what order - then it'll run them and provide a really nice breakdown with timings.

You can also explicitly specify a different task than Default using the -Target argument.

Once a build has finished, you'll be presented by some stats of which tasks were run, in which order, and how long each one took ...

buildsummary

Examples

Okay, so some examples will probably help at this stage. Here's a very simple one ...

Task("NuGetRestore")
    .Does(() => {
        DotNetCoreRestore("MyProject.sln");
    });

Task("Build")
    .IsDependentOn("NuGetRestore")
    .Does(() => {
        DotNetCoreBuild("MyProject.sln");
    });

Task("Default")
    .IsDependentOn("Build")
    .Does(() => { });

RunTarget(target);

You can see that each task has a name and a body, as well as some optional chainable methods - eg. IsDependentOn. The task body appears inside the Does lambda, and is just standard C#. Cake refers to the helpers methods (eg. DotNetCoreBuild) as aliases, which can be used to extend Cake (see the addins sections below).

You can see that the bottom task is called Default, which is the "target" it'll use unless you explicitly specify otherwise when calling build.ps1. By target, this means the last task that will be run after resolving the task dependency graph and running all tasks that this task is dependent on. Notice the IsDependentOn call in most of the tasks? So in the example above, Build will be run before Default, and NuGetRestore will be run before Build.

An example of explicitly specifying the target argument might be build.ps1 -Target Build. This would run NuGetRestore and Build, but not Deploy.

Addins

Cakes comes with a whole bunch of built-in commands and addins, as well as many more community addins - providing a wide range of wonderful things. Some examples are MSBuild, NuGet, MSTest, GitVersion, Slack, Azure, SpecFlow, Docker, Swagger, Android, FTP, HockeyApp, and many many more.

If the thing you want to integrate with doesn't have a Cake addin - then you can always just run it directly with the StartProcess alias, or make an API call with the HTTP aliases.

Build Server Integrations

So nowadays, build servers and continuous integration are all the rage. For most multi-developer teams, you want your build server to automatically kick off your build pipeline when someone pushes to source control. I discussed above running build.ps from the command line, but what about on a build server?

Cake has support for most of the popular CI solutions, eg. AppVeyor, TeamCity, TFS, VSTS, Jenkins. The idea being that you can run the same set of tasks in your CI system as you can locally, and vice-versa. So for example, in VSTS where you would normally have a VSTS build step for each step (nuget restore, build, publish, etc) - when using Cake, you'd just have one Cake task which runs your Cake file.

Debugging

You can even debug and step through your Cake files using either Visual Studio Code or Visual Studio. Both have Cake extensions to help you manage Cake - the VS Code extension includes debugging support, and debugging instructions can be found here. The Visual Studio extension doesn't itself include debugging support, but you can easy debug in Visual Studio by running Cake with the --debug argument, and then just attaching the VS debugger to the Cake process. Full instructions for debugging in Visual Studio can be found here.

Cake!

I was unsure how to wrap up this blog post, so thought I'd end by making everyone feel rather hungry with a picture of some cake! ...

Cake

Useful Links