Evaluating build tools for altdotmono.org - part two

Three days ago I thought about which build tool to use for the altdotmono.org projects. I came up with the conclusion, that CMake, SCons and Autotools should be on my short list.

First a small correction: I wrote, that Rant doesn’t have any native CSharp support. That’s not true! It has a CSharp generator, that can be used as simple as this:

import 'csharp'
gen CSharp, "example.dll", :sources => sys["**/*.cs"]

Unfortunately it doesn’t work very well. Simply adding a reference makes it fail:

gen CSharp, "example.dll", :sources => sys["**/*.cs"],
                           :libs    => ["System.Web"]

rant: [ERROR] in file `/usr/lib/ruby/1.8/rant/rantlib.rb', line 577: 
              in prerequisites: no such file or task: `System.Web'

This should be easy to fix in Rant, I just added a bug report. Sadly Rant doesn’t seem to be very popular. There has been no commit within a year, only ten bug reports within 3 years and although Rant is packaged in Debian, not a single package in Lenny uses Rant as a build tool. (But I should also add, that not more than six packages build-depend on Rake compared to more than 100 packages depending on CMake and more then 50 packages depending on SCons.)

Rake is much more popular but misses some features, that Rant provides (e.g. MD5 checksums instead of timestamps for dependency checking). I haven’t really considered Rake yet, because I couldn’t find any project using it for building C#/Mono projects. But after playing around with SCons and CMake I didn’t had the feeling of having found the right tool, so I tried Rake anyway.

I wrote a custom TaskLib supporting gmcs within half an hour, so that my Rakefile for building two libs now looks like this:

require 'rake'
require 'rake/gmcs'

task :default => ['StructureMap.dll', 'StructureMap.AutoMocking.dll']

signingKey = 'structuremap.snk'


#
# The Core StructureMap Assembly
#
sources    = FileList['StructureMap/**/*.cs']
references = ['System.Web', 'System.Configuration']

Rake::GmcsTask.new 'StructureMap.dll' => {:sources => sources,
  :references => references, :keyfile => signingKey}


#
# StructureMap Rhino.Mocks automocking support
#
sources    = FileList['StructureMap.AutoMocking/**/*.cs']
references = ['#../../Rhino.Mocks.dll', '#StructureMap.dll']

Rake::GmcsTask.new 'StructureMap.AutoMocking.dll' => {:sources => sources,
  :references => references, :keyfile => signingKey}

This comes pretty close to what I would like to have. The Gmcs task basically reads:

“Build StructureMap from the list of sources, linking to the list of references and sign it with the signingKey.”

GmcsTask will create a Rake file task and will automatically make it pre-depend on the sources, the local references and the key file. This means, that StructureMape.Automocking.dll will only be built. if the timestamp of the sources, of StructureMap.dll, RhinoMocks.dll or the key file change. Because I can’t make a dependency to a reference called “System.Web”, I needed a way to specify which references are local file dependencies and which are not. This is simply done by prefixing the reference with ‘#’, something I saw in the SCons build files of Diva.

I think this is pretty easy and it can probably be made even easier by writing a Visual Studio *.csproj-Parser, which extracts the sources, references and resources from the project files. And by even adding a solution parser, the whole build of multiple projects might be stripped down to something like:

task :default => SolutionParser.new('StructureMap.sln').ProjectTasks

“Easy Peasy” as Jamie Oliver would say. I’m not sure if it is a good idea to completely rely on the Visual Studio solution and project files, but I think I will try to drive the Rake approach for building the Alltdotmono projects a little bit further.

Tags , , ,

Posted in | Posted on 27 Sep 2008 12:35by Tobi | no comments

StructureMap on Mono - only two failing tests left

After struggling with some tiny incompatibilities in StructureMap and bugs in Mono, StructureMap now passes nearly all of it’s tests. The final two failing tests are caused by a compiler bug, which I hope will be fixed soon (Marek Safar seems to be very fast in fixing compiler bugs - thanks a lot!).

Tags , ,

Posted in | Posted on 12 Sep 2008 00:04by Tobi | no comments

Unit test of the day

5 unit tests covering a single line of code:

[TestFixture]
public class A_SwissRounding
{
    private SwissRounding _rounding = new SwissRounding();

    [Test]
    public void Should_round_down_to_zero_when_having_zero_to_two_and_a_half()
    {
        Assert.AreEqual(1.00m, _rounding.Round(1.0010m));
        Assert.AreEqual(1.00m, _rounding.Round(1.0249m));
    }

    [Test]
    public void Should_round_up_to_five_when_having_two_and_a_half_to_five()
    {
        Assert.AreEqual(1.05m, _rounding.Round(1.0250m));
        Assert.AreEqual(1.05m, _rounding.Round(1.0490m));

    }
    [Test]
    public void Should_round_down_to_5_when_having_five_to_seven_and_a_half()
    {
        Assert.AreEqual(1.05m, _rounding.Round(1.0510m));
        Assert.AreEqual(1.05m, _rounding.Round(1.0749m));
    }

    [Test]
    public void Should_round_up_to_10_when_having_seven_and_a_half_to_ten()
    {
        Assert.AreEqual(1.10m, _rounding.Round(1.0760m));
        Assert.AreEqual(1.10m, _rounding.Round(1.0990m));
    }

    [Test]
    public void Should_not_round_multiples_of_five()
    {
        Assert.AreEqual(1.00m, _rounding.Round(1.0000m));
        Assert.AreEqual(1.05m, _rounding.Round(1.0500m));
        Assert.AreEqual(1.10m, _rounding.Round(1.1000m));
    }
}

public class SwissRounding
{
    public decimal Round(decimal value)
    {
        return Math.Round(value*20, MidpointRounding.AwayFromZero)/20;
    }
}

:-)

Tags ,

Posted in | Posted on 26 Aug 2008 18:16by Tobi | no comments