Reusable Build Scripts

I started last week at Open Space Coding Day 1 and I had builds on my mind. I proposed a lot of different topics to code one but reusable build scripts picked up a bit of traction. I ran a quick tutorial on how to get an application compiling and tests running. We used NAnt because that’s what people wanted to learn but there’s plenty of alternatives including Powershell, Rake and MSBuild.
Here’s the repository from our session:
reusable build scripts subversion
When I first started off with build scripts it didn’t take long before they got ugly. Your build script goes from a simple compilation to a monstrous 1000 line file if your not careful. I remember adding all kinds of email notifications and deployment steps that I ended up cutting out during the hand over. I recommend if your new to build scripts and NAnt to read through with JP Boodhoo’s NAnt Starter Series which is a really good starting point.
The main tips for simplifying your build scripts are
- remove application specific information to a common properties file
- separate out common targets into separate files which you can include (ie: tests, compile, deploy)
- keep it simple
I maintain a number of projects for a data mining job and it started to bug me how across different projects on a few scattered lines of the build changed. Even after the tips I mentioned you can still find that you have application specific configurations in your build.
The problem I had was I was repeating myself in the build script and that defeats DRY. The steps I was taking were:
- compile all the .cs files in my source dir
- manually set the references for my project
- copy the referenced DLLs into the output directory
It doesn’t seem like much but anytime you add a reference you need to edit the build file. It ends up looking like this.
<csc target="library" output="${dir.deploy}/sample.core.tests.dll" debug="true">
<sources>
<include name="${dir.src}/*.tests/**/*.cs" />
</sources>
<references>
<include name="${dir.deploy}/sample.core.dll" />
<include name="../thirdparty/tools/mbunit/MbUnit.Framework.dll" />
</references>
</csc>
<copy todir="${dir.deploy}">
<fileset>
<include name="../thirdparty/tools/mbunit/MbUnit.Framework.dll" />
<include name="../thirdparty/tools/mbunit/QuickGraph.Algorithms.dll" />
<include name="../thirdparty/tools/mbunit/QuickGraph.dll" />
</fileset>
</copy>
The benefit of using csc over msbuild is that it lets you use the Mono Framework just by setting the target framework at command line.
The other problem with this approach is that mono needs reference DLL’s compiled for Mono like MySql Connector and Log4Net.
Cowan.Commons.Nant.ProjectsTask
My solution was to take all the logic I was repeating and put it into a custom task called ProjectsTask. It allows you to use wildcards to specify your projects to compile, which is not supported by SolutionsTask. This task uses XPath on each project file to retrieve references,resources and the project name.
<projects outputdir="${target.dir}">
<fileset basedir="${sources.dir}">
<include name="**/*.csproj" />
<exclude name="*.tests/*.csproj" />
</fileset>
</projects>
To fix the mono reference problem I’ve included references for both mono and .net in my lib folder but only referenced the .net dll.
\lib\log4net\net-3.5\log4net.dll \lib\log4net\mono-3.5\log4net.dll
when you compile with mono it notices the net-3.5 directory and tries and finds a mono directory beside it. A new addition to NAnt is that it’s no longer sensitive to OS specific path seperators like / or \.
I still need to specify the build configuration (ie:debug or release) but it’s really simplified things for me.
Work Smarter not Harder
I just copy all the 3rd party DLLs to the build folder before any compile tasks. And to add reference to *.dll in the build output folder. The line for referenced should take care of everything all required references for all projects. It has been working for me. Do you forsee any problems doing it that way. It will also be great if i can loop through all the src directories using the foreach task and run the src task for all projects. Any suggestions there?
Thank You,
Vish
@Vish thats a good convention, I have seperate folders for tools (mbunit.cons.exe,etc) and references. Maybe I should have tools,lib,test.lib so I can sort of the references that way