Make building your project a piece of Cake
“In software, when something is painful, the way to reduce the pain is to do it more frequently, not less.” ― David Farley
Between developers using different IDEs (Visual Studio, VS Code, Vim) and the wide range of continuous build services available it can be hard to get your project building the same everywhere.
Cake allows a project to describe its build steps and store them in the repository alongside the code and then run those steps with a simple Powershell or Linux shell “bootstrap” script, making it a breeze to build everywhere consistently.
What is Cake?
Cake (C# Make) is a cross-platform build automation system with a C# DSL (Build language) for tasks such as compiling code, copying files and folders, running unit tests, compressing files and building NuGet packages.
The Cake project is open-source, with a wide range of plug-ins available that cover all but the most unusual build tasks. The extra plug-ins are pulled down using NuGet making it easy to get the features you need or to publish your own plug-in and use it in your scripts.
The Basics
To start with Cake you just need two files in the root of your project, so let’s look at each of these.
The Bootstrapper
The bootstrapper makes it easy to run Cake by providing a script that does the following things:
- Downloads a copy of nuget.exe
- Uses NuGet to restore Cake
- Restores any extra NuGet packages needed by the script
- Runs Cake
With this script you can run Cake on any computer without having to install anything up-front. The files it downloads are restored to a “tools” directory alongside the script, so they don’t pollute the rest of the system and cached so you don’t need to download everything again the next time you run Cake. Pretty neat if you ask me.
The bootstrapper comes as both a Powershell and a Bash script, for use on windows and Linux respectively, and is designed (actually, encouraged) to be changed to allow things like pinning the version of Cake or NuGet being used.
The Cake Script File
Now we get to the heart of the matter, the Cake script itself. This is where you are going to put all your configuration for Cake. This is a file named “build.cake” and it contains C# code which Cake compiles with Roslyn so all those awesome features of .NET can also be used in the script.
Cake has its own DSL for writing the Cake file itself which focuses on defining the tasks needed in the build and the order these must be run through dependencies. Without further ado, time to see this all in action.
Build and Test HelloWorld
Note: I have made sample .NET framework and .NET Core versions of this project so you can try this out yourself
Let’s start with a simple cake script to build and test a simple project, where the .sln file is in the same directory and walk through how it works.
This script defines two tasks “Build” and “Test”. A task is the main building block of a cake script and can perform actions as well as define which other Tasks which must be run first.
- The Build task is runs NuGet restore on the solution and then uses MSBuild to build it.
- The Test task requires the Build task runs first and then runs `VSTest` on the DLL generated by the test project which is in a HelloWorldTest directory. Cake has support for file globing built in making it easy to avoid using specific file paths in your scripts.
- The `RunTarget()` makes the script run the Test task by default but the Argument() allows the task to be easily selected when running Cake.
Each of the `NuGetRestore`, `MSBuild` and `VSTest` commands takes in a custom settings class that allows for fine tuning of the way they operate — a pattern that is common to all Cake Tasks. In the example the `MSBuild` action has a `MSBuildSettings` object that configures `MSBuild` to run the “Build” target in the “Release” configuration.
This simple script represents the starting point for a lot of projects and fits in 19 lines. Execution is as simple at running the bootstrapper with no arguments, `.\build.ps1`. The -target argument can be used to specify the task to run eg `.\build.ps1 -target Build` will run the “Build” task rather than the “Test” task.
Extending Your Cake Script
The basics are all well and good but there is a lot more with which Cake can help in automating the whole build, test, version, package and deploy process of a project. Cake has built-in support for many tools, including:
- xUnit/NUnit — Alternative to MSTest
- SpecFlow — Run SpecFlow user acceptance tests
- OpenCover — Code coverage reports
- GitVersion — Generate a version string based on the Git history and GitVersion rules
- Chocolatey — Package an application for deployment using Chocolatey
- NuGet — Restore, pack or push packages via NuGet
- Octopus Deploy — Pack, push and deploy with Octopus
Additional tools are provided as Add-ins that make it quick and easy to replace existing scripts and build configuration for a project, bringing it all into a single Cake script that can (and should) be version controlled right alongside the code in the repo.
Integrating With CI
One of the main reasons I started using Cake on my project was to overcome some of the limitations with our CI system. Our build steps were configured directly into Atlassian Bamboo and the web UI made it hard to change the scripts as well impossible to have different build scripting for different Git branches.
With Cake I was able to reproduce all the steps from the existing CI as Cake Tasks in our Git repo, replacing all the CI steps with a couple of calls to Cake. Now, as the code changes, it is the Cake script that needs to change rather than the CI script. I no longer need to worry about choosing which branch builds are going to break when I need to update the Bamboo plan. It allows developers to run the exact same commands locally that the CI system will run; the chances are that if it works on your computer, it will work on your CI system too.
Cake makes your build scripting agnostic of the actual CI system, but does also contain integrations with many of the popular build systems:
- Atlassian Bamboo
- Jenkins
- Team Foundation Server
The full list can be found on the Cake website (https://cakebuild.net/dsl/build-system/).
These integrations allow a task to access variables from the build system or permit a task to run only if it is (or is not) currently running on a CI build agent with the `BuildSystem.IsLocalBuild` variable.
Adding Cake to an existing Project
I hope by this point you are thinking how you might use Cake in your own project to give it a try. The great news is that you don’t have to change a single line of your existing code to have a go. Just go to https://cakebuild.net/docs/tutorials/setting-up-a-new-project to grab the bootstrapper and then create a simple Cake script file. That really is all there is to it.
Once you feel ready to share Cake with the rest of your team you can commit the bootstrapper and Cake script to your repo so everyone can get comfortable with it.
Simply adding the bootstrapper and cake script the to the repo won’t force any changes to your existing CI process but to unlock the full potential of Cake you should update your build pipeline to use Cake by calling the bootstrapper. The more complicated your build process currently is the more benefit there is to be had with cake.
Conclusion
With the introduction of .NET Core and VS Code, Microsoft is moving into the world of cross platform development and now, more than ever, being a C# developer is more than just using Visual Studio and Windows. The tooling modern projects demand need to work for everyone. Cake will free your build scripting from Visual Studio or any specific CI platform and move all that config into code that anyone can run no mater their choice of development environment.
If you’re still looking for a little more information the you can check out one of its developers talking about Cake at the very presentation which introduced me to Cake: A Piece of Cake — C# Powered Cross Platform Build Automation
Or check out the Cake project website