Jeremy Lindsay

.net, Cake, Performance

Calling a custom executable from Cake using StartProcess and ProcessSettings

I’ve previously written about how I’ve used Cake to orchestrate my build and deployment processes, and write the code for these processes in C# rather than PowerShell. This time I’ll write about how I’ve improved the speed of my deployment process by using custom tools which aren’t yet built into Cake.

Some background about my deployment process

A common part of a deployment process is copying files repetitively from a source to a destination, and Cake provides a good way to do this – the CopyFiles static method.

To use this, we just need to specify the source directory, the remote destination directory, and plug these in as parameters. I’ve written some sample code below showing how a “Deploy” task might move an application from a “publish” directory to a remote machine.

Task("Deploy")
    .Does(() =>
    {
	// The files I want to copy are in the publish directory - I use the
	// wildcard character to specify that I want to copy everything
	var source = @".\publish\*";
 
	// The destination is on my local network and accessible on the path below
	var destination = @"\\192.168.1.125\c$\ConsoleApps\DeployedApplication";
 
	// Now just plug in the source, destination
	// The boolean parameter ensures we preserve the folder structure
	CopyFiles(source, destination, true);
    });

This works well, but it also copies across every file, every time – it doesn’t matter whether the file has changed or not – and this the slowest part of my deployment process.  I would prefer to mirror my source and destination files, and only copy across files that have changed. This would speed up deployments across my local network.

Using RoboCopy to mirror directory structures

Microsoft have created a command line file copy utility which allows me to copy or mirror directory structures called RoboCopy (Robust File Copy) – it’ll only copy the files/directories that have changed, and this sounds like exactly what I need.

In copying files from source to destination, I’ve chosen the word “mirror” because RoboCopy needs to be passed a switch “/MIR”, which is short for mirror. From Microsoft’s TechNet documentation:

/MIR is an option to ROBOCOPY where you mirror a directory tree with all the subfolders including the empty directories and you purge files and folders on the destination server that no longer exists in source.

The command I’d need to mirror files has the format:

robocopy /MIR source_directory destination_directory

And to copy from my source directory

".\publish\"

to the destination on the C drive of a machine with IP address 192.168.1.125:

"\ConsoleApps\DeployedApplication"

I just need to plug these values as arguments to the robocopy executable, as shown below:

robocopy /MIR ".\publish\" "\\192.168.1.125\c$\ConsoleApps\DeployedApplication"

But how can I use RoboCopy with Cake?

Turns out it’s quite easy with a few things in Cake which can help us.

  • We can use the StartProcess method – I can pass in executable that I want to run (i.e. robocopy.exe), and I can also pass in the arguments for this executable.

I don’t need to pass in the full path to the executable because robocopy.exe is in a folder which is in my machine’s path, i.e. C:\Windows\System32

  • I can also clean up my code a little by keeping this code in its own method in the Cake.build file.
private void MirrorFiles(string source, string destination)
{
    StartProcess("robocopy", new ProcessSettings {
        Arguments = new ProcessArgumentBuilder()
            .Append(@"/MIR")
            .Append(source)
            .Append(destination)
        }
    );
}

Now I can adjust the Task shown previously (i.e. “Deploy”) to use this method instead:

Task("Deploy")
    .Does(() =>
    {
	// The files I want to copy are in the publish directory
	var source = @".\publish\";
 
	// The destination is on my local network and accessible on the path below
	var destination = @"\\192.168.1.125\c$\ConsoleApps\DeployedApplication";
 
	// Now just plug in the source, destination
	MirrorFiles(source, destination);
    }

What practical difference does this make?

In my application’s Cake build script, there’s very little difference – a new method to mirror files, and a slight change to a task to copy (or mirror) files across a network.

But the real advantage comes when we look at the time taken to deploy my application.

Another nice feature of Cake is that it writes out the time taken by each task which helps with performance benchmarking.

I’ve pasted the timings for each stage of my original deployment process below for when I just copy files instead of using robocopy:

Task                  Duration 
--------------------------------------------------
Clean                 00:00:00.2378497 
Restore               00:00:03.9107662 
Build                 00:00:05.2128133 
Publish               00:00:08.0715728 
Deploy                00:00:43.1527382 
Default               00:00:00.0021827 
--------------------------------------------------
Total:                00:01:00.5879229

Notice it took 43 seconds to deploy my application’s files from source to destination – approximately 75% of the total time. And everytime I change my application and re-deploy, the time taken to carry out this operation is approximately the same, copying across files that have changed and also those that haven’t changed.

Let’s change the script to mirror files using robocopy (i.e. only copy across files that have changed since the last build) rather than just copying all files – I’ve pasted the new timings below:

Task                  Duration 
--------------------------------------------------
Clean                 00:00:00.2661543 
Restore               00:00:02.7529030 
Build                 00:00:04.7478403 
Publish               00:00:06.3981560 
Deploy                00:00:00.6685282 
Default               00:00:00.0033186 
--------------------------------------------------
Total:                00:00:14.8369004

It has gone from copying every file in 43 seconds to just copying 5 files that changed in 0.66 seconds – this is a huge difference for me, making it much quicker and more convenient for me to make an application, change, build and deploy to my test rig.

Wrapping up

In this post I wanted to share with the community how flexible Cake is by demonstrating how I’ve used Robocopy to speed my up deployments.

  • I’ve been able to switch out Cake’s built in copying feature, and instead use a local executable (that isn’t a core part of Cake or an addin) by passing it to the StartProcess method.
  • I’ve been able to write a private method in my C# Cake.build script to keep the code clean.
  • Finally I’ve been able to use Cake’s default output to benchmark performance before and after my change.

Being able to extend the core features in Cake with StartProcess is really powerful – it’s not quite as re-useable as building a dedicated add-in, but it can still allow us to quickly customise and optimise build scripts.


About me: I regularly post about .NET – if you’re interested, please follow me on Twitter, or have a look at my previous posts here. Thanks!

One thought on “Calling a custom executable from Cake using StartProcess and ProcessSettings

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s