code blog foo - tag line bar

.Net Build Automation using Ruby Make (Rake)

This is a step by step (albeit simple) guide to using RAKE for building your .Net solutions. Before we get into how to use Rake, lets start with answering a few questions about why in the world you would want to use RAKE to build your .Net solutions.

Why use Rake?

Why in the world would I use Rake to build my .Net solutions?
Cause it's cool to use Ruby!
No really...I don't just want to bring in yet another programming language for the hell of it.
Rake gives you access to Ruby's programming constructs, which have benefits over build strategies that currently exist for .Net.
But I can just hit Shift+Ctrl+B in my Visual Studio IDE to build my projects.
Having a build script does more than build the project. It can build your project, run unit tests, deploy to staging environments, and all that other good stuff. It also positions your code base for continuous integration.
Yea, I can see how having a build script is a good idea. But I can just use batch files to do all that stuff.
Batch files are a good starting point, but you can't put "logic" per se in a batch file. It can't do for-loops, if-else-branches or anything else that a programming language provides.
Well if I need to have programming logic in my build automation, I can just use MSBuild scripts. If I need to do something custom, I can just create a custom build task and add it to my build script. All I have to do is create a new project, reference some dll's from the .Net Framework, add my .cs files, inherit from BuildTask, code it, compile, take the output assemblies and put it in a discoverable location, add the assembly dll reference in my MSbuild script with a fully qualified name, and then I can easily use the custom build task I created. And it's nicely formatted in XML on top of that.
You could do that I guess...or you could skip all that painful stuff and just do your custom logic in a Ruby script. Ruby is easy enough to read when compared to XML (you don't have to deal with the all the XML noise, which can take away from what the build is actually trying to do). Also, having the compiled assembly makes it difficult to see what the custom task is actually doing. It is equally difficult to change it and re-deploy.
What about NAnt scripts?
Refer to previous answer. NAnt (and MSBuild for that matter) are by no means bad solutions. In fact, there is nothing stopping you from using all of these in conjunction.
Okay so from what I gather, Ruby Make (Rake) gives me the benefits of a batch file and at the same time gives me access to a robust programing language, but without all the overhead of having to deal with compiled assemblies and deployment.
Correct.

Installing Rake

Now that you are somewhat intrigued, let me get you started with Rake. Here is a step by step guide for getting Rake installed:

  1. Download Ruby
  2. After installing Ruby, select "Start Command Prompt with Ruby" from the Start Menu.
  3. Execute the following commands in the command prompt (these commands will install Rake and supporting plugins for .Net compilations):
    • gem install rake
    • gem install albacore
    • gem install win32-file

After Rake is installed, you can start using it. All you need to do is:

  1. Create a text file called "Rakefile" (with no extension) inside of your working directory.
  2. You can then start up Command Prompt with Ruby, navigate to the working directory and run the following command: rake
  3. Ruby Make will look for a file called Rakefile, and execute its contents.

Creating a Rakefile

Now that you have Rake installed, we can start creating a Rakefile.

Simple Rake

Here is really a simple Rakefile that builds a .Net solution called SampleApp.sln. This Rakefile takes an MSBuild path, a working directory, a solution name and calls "sh" to execute the build. When executing the rake command from command line, the task named "default" will automatically be executed.


# location of the MSBuild on this computer
@msBuildPath = "#{ENV['SystemRoot']}\\Microsoft.NET\\Framework\\v4.0.30319\\msbuild.exe"

#this is how you get the working directory
@workingDirectory = pwd 

task :default do
    #shell out to msbuild giving it the solution path
    @solutionFile = "#{@workingDirectory}\\SampleApp.sln"
    sh "\"#{@msBuildPath}\" \"#{@solutionFile}\""
end

Multiple Tasks

This Rakefile executes two separate tasks. One of them builds the project and the other runs unit tests in the solution.


# location of the MSBuild on this computer
@msBuildPath = "#{ENV['SystemRoot']}\\Microsoft.NET\\Framework\\v4.0.30319\\msbuild.exe"
@workingDirectory = pwd #this is how you get the working directory
@msTestPath = "C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\mstest.exe"

#this is the default tasks that executes the other tasks
task :default => [:build, :runtests]; 

task :build do
    @solutionFile = "#{@workingDirectory}\\SampleApp.sln"
    sh "\"#{@msBuildPath}\" \"#{@solutionFile}\""
end

task :runtests do
    cd "#{@workingDirectory}\\SampleApp.UnitTests\\bin\\Debug"
    sh "\"#{@msTestPath}\" /testcontainer:SampleApp.UnitTests.dll"
end

Composing Tasks

This Rakefile has a task called "integration" that runs all of the default tasks and also runs integration tests. To call the integration task, pass the task name as a command line argument, for example: "rake integration" will execute the integration task as opposed to the default task.


# location of the MSBuild on this computer
@msBuildPath = "#{ENV['SystemRoot']}\\Microsoft.NET\\Framework\\v4.0.30319\\msbuild.exe"
@workingDirectory = pwd #this is how you get the working directory
@msTestPath = "C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\mstest.exe"

task :default => [:build, :unittests];
task :integration => [:default, :integrationtests];

task :build do
    @solutionFile = "#{@workingDirectory}\\SampleApp.sln"
    sh "\"#{@msBuildPath}\" \"#{@solutionFile}\""
end

task :unittests do
    cd "#{@workingDirectory}\\SampleApp.UnitTests\\bin\\Debug"
    sh "\"#{@msTestPath}\" /testcontainer:SampleApp.UnitTests.dll"
end

task :integrationtests do
    cd "#{@workingDirectory}\\SampleApp.IntegrationTests\\bin\\Debug"
    sh "\"#{@msTestPath}\" /testcontainer:SampleApp.IntegrationTests.dll"
end

Raising Errors

Sometimes you want to fail the build if something doesn't go as planned. In this example, the build will be failed if a file doesn't exist in a particular location.


task :default do
    @someFile = "#{@workingDirectory}\\README.txt"
    if !File.exists?(@someFile)
    	raise "#{@someFile} does not exist."
    end
end

Using Albacore

Albacore is a suite of Rake Build Tasks for .Net Solutions. Los Techies has a nice little blog post on it. (The script below came from Los Techies, I've added it here for conveniance).

require 'albacore'
 
desc "Run a complete Albacore build sample"
task :sample => ['albacore:assemblyinfo', 'albacore:msbuild']

desc "Run a sample build using the MSBuildTask"
Rake::MSBuildTask.new(:msbuild) do |msb|
    msb.properties :configuration => :Debug
    msb.targets [:Clean, :Build]
    msb.solution = "lib/spec/support/TestSolution/TestSolution.sln"
end

desc "Run a sample assembly info generator"
Rake::AssemblyInfoTask.new(:assemblyinfo) do |asm|
    asm.version = "0.1.2.3"
    asm.company_name = "a test company"
    asm.product_name = "a product name goes here"
    asm.title = "my assembly title"
    asm.description = "this is the assembly description"
    asm.copyright = "copyright some year, by some legal entity"
    asm.custom_attributes :SomeAttribute => "some value goes here", :AnotherAttribute => "with some data"

    asm.output_file = "lib/spec/support/AssemblyInfo/AssemblyInfo.cs"
end

Rake can do some pretty neat stuff. Hopefully this blog post helped you get started. Here is the Rake home page.


Written: 9/22/2010