Sunday, August 21, 2005

Using Rake for building and testing .net applications

The excitement concerning Ruby seems to grow on a daily basis. The Ruby on Rails framework, RubyGems, and Rake add to this excitement. Martin recently wrote a great article about Using the Rake Build Language.

Mike Ward asked me to help out with NanoContainer.net by creating a build file. This seemed like a perfect time to make use of Rake.

To get started I thought a Hello World Rake build would be appropriate.
task :helloWorld do
sh "echo HelloWorld"
end

Next I work on using Rake & csc to compile NanoContainer.net.
file "build/NanoContainer.dll" => :init do
cd "src/NanoContainer"
sh "csc /out:../build/NanoContainer.dll /target:library /recurse:*.cs /lib:../build /r:'PicoContainer.dll;Microsoft.JScript.dll;VJSharpCodeProvider.dll'"
cd "../.."
end

task :init do
buildDir = "src/build"
mkdir buildDir unless File.exists?(buildDir)

cp "lib/PicoContainer.dll", buildDir
cp "lib/Castle.DynamicProxy.dll", buildDir
cp "lib/NMock.dll", buildDir
cp "lib/NUnit.Framework.dll", buildDir
end

The "build/NanoContainer.dll" file task depends on the init task. This is expressed by:
file "build/NanoContainer.dll" => :init
The init task is used to create the build directory if it's not already been created and copy the dlls that are referenced by NanoContainer and NanoContainer.Tests. The actual sh "csc .." is fairly straight forward; however, if you need more details on csc the "csc -?" command is quite helpful.

The "build/NanoContainer.Tests.dll" file task is very similar:
file "build/NanoContainer.Tests.dll" => [:init, "build/NanoContainer.dll"] do
cd "src/NanoContainer.Tests"
sh "csc /out:../build/NanoContainer.Tests.dll /res:'TestScripts/test.cs,NanoContainer.Tests.TestScripts.test.cs' /res:'TestScripts/test.js,NanoContainer.Tests.TestScripts.test.js' /res:'TestScripts/test.java,NanoContainer.Tests.TestScripts.test.java' /res:'TestScripts/test.vb,NanoContainer.Tests.TestScripts.test.vb' /target:library /recurse:*.cs /lib:'../build' /r:'PicoContainer.dll;Microsoft.JScript.dll;VJSharpCodeProvider.dll;Castle.DynamicProxy.dll;NanoContainer.dll;NMock.dll;NUnit.Framework.dll'"
cd "../.."
end

After building NanoContainer and NanoContainer.Tests I'm ready to create a test task. NUnit provides good documentation for using the NUnit Console application; however, in my test task using the NUnit is very straight forward and easy.
task :test => :compile do
testCompVsBinDebugDir = "src/TestComp/bin/Debug"
mkdir testCompVsBinDebugDir unless File.exists?(testCompVsBinDebugDir)
cp "src/Build/TestComp.dll", testCompVsBinDebugDir

nanoTestVsBinDir = "src/NanoContainer.Tests/bin"
mkdir nanoTestVsBinDir unless File.exists?(nanoTestVsBinDir)

nanoTestVsBinDebugDir = "src/NanoContainer.Tests/bin/Debug"
mkdir nanoTestVsBinDebugDir unless File.exists?(nanoTestVsBinDebugDir)
cp "src/Build/PicoContainer.dll", nanoTestVsBinDebugDir
cp "src/Build/Castle.DynamicProxy.dll", nanoTestVsBinDebugDir
cp "src/Build/NMock.dll", nanoTestVsBinDebugDir
cp "src/Build/NUnit.Framework.dll", nanoTestVsBinDebugDir
cp "src/Build/NanoContainer.dll", nanoTestVsBinDebugDir
cp "src/Build/NanoContainer.Tests.dll", nanoTestVsBinDebugDir

cd nanoTestVsBinDebugDir
sh "../../../../lib/nunit-console.exe NanoContainer.Tests.dll"
end

The majority of the test task is about moving dlls into the NanoContainer.Tests/bin/Debug folder. This is needed because NanoContainer.Tests contains a few tests that use relative paths for testing. Eventually the tests should be refactored to remove this dependency, but I wasn't interested in taking care of this right now. Using Rake it's quite easy to move things around and that was the simplest solution.

I haven't used Rake to it's fullest. Even while I write this I can see things that I need to refactor. Unfortunately, when creating this rakefile I couldn't find much out there to help me. As more examples emerge I fully expect to see a much larger Rake adoption.

Here's my full rakefile for building NanoContainer.net
buildDir = "src/build"
nanoDll = "build/NanoContainer.dll"
nanoTestsDll = "build/NanoContainer.Tests.dll"
testCompDll = "build/TestComp.dll"
testComp2Dll = "build/TestComp2.dll"
notStartableDll = "build/NotStartable.dll"
nanoTestVsBinDir = "src/NanoContainer.Tests/bin"
nanoTestVsBinDebugDir = "src/NanoContainer.Tests/bin/Debug"
testCompBinDir = "src/TestComp/bin"
testCompBinDebugDir = "src/TestComp/bin/Debug"

task :default => [:compileInit, :compile, :test]
task :all => [:clear, :removeBuildDir, :removeVsDirs, :default]

task :clear do sh "clear" end

task :removeBuildDir => :clear do rm_rf(buildDir) end
task :removeVsDirs => :clear do
rm_rf(nanoTestVsBinDir)
rm_rf(testCompBinDir)
end

directory buildDir

task :compileInit => [buildDir] do
copyToDir(%w(lib/NUnit.Framework.dll lib/PicoContainer.dll lib/Castle.DynamicProxy.dll),buildDir)
copyToDir(%w(lib/NMock.dll lib/NUnit.Framework.dll),buildDir)
end

file nanoDll => :compileInit do
cd "src/NanoContainer"
sh "csc /out:../build/NanoContainer.dll /target:library /recurse:*.cs /lib:../build /r:'PicoContainer.dll;Microsoft.JScript.dll;VJSharpCodeProvider.dll'"
cd "../.."
end

file nanoTestsDll => [:compileInit, nanoDll] do
cd "src/NanoContainer.Tests"
sh "csc /out:../build/NanoContainer.Tests.dll /res:'TestScripts/test.cs,NanoContainer.Tests.TestScripts.test.cs' /res:'TestScripts/test.js,NanoContainer.Tests.TestScripts.test.js' /res:'TestScripts/test.java,NanoContainer.Tests.TestScripts.test.java' /res:'TestScripts/test.vb,NanoContainer.Tests.TestScripts.test.vb' /target:library /recurse:*.cs /lib:'../build' /r:'PicoContainer.dll;Microsoft.JScript.dll;VJSharpCodeProvider.dll;Castle.DynamicProxy.dll;NanoContainer.dll;NMock.dll;NUnit.Framework.dll'"
cd "../.."
end

file testCompDll => :compileInit do
cd "src/TestComp"
sh "csc /out:../build/TestComp.dll /target:library /recurse:*.cs"
cd "../.."
end

file testComp2Dll => [:compileInit, testCompDll] do
cd "src/TestComp2"
sh "csc /out:../build/TestComp2.dll /target:library /recurse:*.cs /lib:'../build' /r:'PicoContainer.dll;TestComp.dll'"
cd "../.."
end

file notStartableDll => [:compileInit, testCompDll] do
cd "src/NotStartable"
sh "csc /out:../build/NotStartable.dll /target:library /recurse:*.cs /lib:'../build' /r:TestComp.dll"
cd "../.."
end

task :compile => [nanoDll,nanoTestsDll,testCompDll,testComp2Dll,notStartableDll]

directory testCompBinDir
directory testCompBinDebugDir
directory nanoTestVsBinDir
directory nanoTestVsBinDebugDir

task :testInit => [testCompBinDir,testCompBinDebugDir,nanoTestVsBinDir,nanoTestVsBinDebugDir] do
cp "src/Build/TestComp.dll", testCompBinDebugDir
copyToDir(%w(src/Build/NMock.dll src/Build/PicoContainer.dll src/Build/Castle.DynamicProxy.dll),nanoTestVsBinDebugDir)
copyToDir(%w(src/Build/NUnit.Framework.dll src/Build/NanoContainer.dll src/Build/NanoContainer.Tests.dll),nanoTestVsBinDebugDir)
end

task :test => [:compile,:testInit] do
cd nanoTestVsBinDebugDir
sh "../../../../lib/nunit-console.exe NanoContainer.Tests.dll"
end

def copyToDir(fileArray, outputDir)
fileArray.each { |file| cp file, outputDir }
end

1 comment:

  1. Anonymous9:11 AM

    I'm not sure if requiring developers to have Ruby and rake makes sense on a .net project. But, since rake is much better than NAnt, I'd be willing to give it a try. I do totally agree with your comment though.

    ReplyDelete

Note: Only a member of this blog may post a comment.