Introduction
Cake (C# Make) is a build automation system with a C# DSL, used for tasks such as compiling code, copying files/folders, running unit tests, compressing files, and building NuGet packages.
URL: https://cakebuild.net/docs
Build
This tutorial uses Cake Frosting, which allows you to write your build as a standard console application as part of your solution. For other possibilities on how to run Cake builds.
The following instructions require .NET Core 3.1.301 or later to run Cake Frosting 1.0.0 or later. You can find the .NET SDK at https://dotnet.microsoft.com/download
To create a new Cake Frosting project, you need to install the Frosting template:
dotnet new --install Cake.Frosting.Template
Create a new Frosting project:
dotnet new cakefrosting
This will create the Cake Frosting project and bootstrapping scripts.
Initial Build Project
The Program class contains the code to configure and run Cake:
public static class Program
{
public static int Main(string[] args)
{
return new CakeHost()
.UseContext<BuildContext>()
.Run(args);
}
}
The BuildContext class can be used to add additional custom properties. The default template contains a sample Delay property that can be set via the --delay argument. You can remove this property and customize it to your specific needs.
public class BuildContext : FrostingContext
{
public bool Delay { get; set; }
public BuildContext(ICakeContext context)
: base(context)
{
Delay = context.Arguments.HasArgument("delay");
}
}
The file also contains three task classes:
[TaskName("Hello")]
public sealed class HelloTask : FrostingTask<BuildContext>
{
public override void Run(BuildContext context)
{
context.Log.Information("Hello");
}
}
[TaskName("World")]
[IsDependentOn(typeof(HelloTask))]
public sealed class WorldTask : AsyncFrostingTask<BuildContext>
{
// Tasks can be asynchronous
public override async Task RunAsync(BuildContext context)
{
if (context.Delay)
{
context.Log.Information("Waiting...");
await Task.Delay(1500);
}
context.Log.Information("World");
}
}
[TaskName("Default")]
[IsDependentOn(typeof(WorldTask))]
public class DefaultTask : FrostingTask
{
}
The Default task depends on World. The World task is an asynchronous task that, if the Delay property is set, waits for one and a half seconds.
Example Build Pipeline
The following example creates a simple build pipeline with a task for cleaning directories, a task for compiling an MSBuild solution, and a task for testing the solution.
The following example assumes a Visual Studio solution located at src/Example.sln in the repository root folder.
Add the required using statements:
using Cake.Common;
using Cake.Common.IO;
using Cake.Common.Tools.DotNet;
using Cake.Common.Tools.DotNet.Build;
using Cake.Common.Tools.DotNet.Test;
Remove the Delay property from the BuildContext class and add a MsBuildConfiguration property that stores the configuration to build the solution:
public class BuildContext : FrostingContext
{
public string MsBuildConfiguration { get; set; }
public BuildContext(ICakeContext context)
: base(context)
{
MsBuildConfiguration = context.Argument("configuration", "Release");
}
}
The HelloTask and WorldTask classes can be removed.
Create a new CleanTask class for the task that cleans directories:
[TaskName("Clean")]
public sealed class CleanTask : FrostingTask<BuildContext>
{
public override void Run(BuildContext context)
{
context.CleanDirectory($"../src/Example/bin/{context.MsBuildConfiguration}");
}
}
Create a new BuildTask class for building the solution:
[TaskName("Build")]
[IsDependentOn(typeof(CleanTask))]
public sealed class BuildTask : FrostingTask<BuildContext>
{
public override void Run(BuildContext context)
{
context.DotNetBuild("../src/Example.sln", new DotNetBuildSettings
{
Configuration = context.MsBuildConfiguration,
});
}
}
Create a new TestTask class for testing the solution:
[TaskName("Test")]
[IsDependentOn(typeof(BuildTask))]
public sealed class TestTask : FrostingTask<BuildContext>
{
public override void Run(BuildContext context)
{
context.DotNetTest("../src/Example.sln", new DotNetTestSettings
{
Configuration = context.MsBuildConfiguration,
NoBuild = true,
});
}
}
Update the DefaultTask class to depend on the new tasks:
[IsDependentOn(typeof(TestTask))]
public sealed class Default : FrostingTask
{
}
Running the Build Script
Run the build script:
./build.ps1
For more documentation, visit the Cake Build website: https://cakebuild.net.
Finally, if you like my articles, please follow me. Hope the .NET ecosystem gets better and better!