Blogtastic

Mike Roberts on Life and Technology


Common Absolute Paths Anti-Pattern

Created 23 June 2004

[ Tech / Build Engineering ]

Some projects I've worked on in the past assume that certain files can always be found at an absolute path, eg 'c:\program files\some cool library\library.dll'. Such files may be dependencies, or deployment target locations. This is a situation to be avoided!

The main reason this is so bad is that you may not actually have control over such paths when your application gets deployed. For example, many IT operations departments use 'C:' just for the Windows O/S, and put all other files on a secondary drive (e.g. for backup purposes). If your project assumes absolute paths, such problems may not arise until you finally reach production.

However there are other problems, like what happens when you want to work on 2 versions of the app at the same time (e.g. trunk, and branch)?

But there is good news - you don't need to hard code absolute paths into your system! Alternatives are:
- Use relative paths. As an example, setup your source control directory structure such that you always know where your dependencies can be found relative to your project's source code. (This may mean you save third-party binaries in source-control but this is not a sin!)
- Use deploy-time configuration. Get your build environment to generate environment-specific distribution files where parameterised paths are expanded to absolute paths at the last moment.
- Use environment variables, like path. This is a 'last-chance' option since it is system-wide, rather than application-wide in scope, but stops you having to hard code paths in your application at least.

To check that a project I'm working on isn't using absolute paths, I tend to do one of the following things
- Have 2 copies of the development tree checked out on my machine. Switch between them occasionally and check nothing breaks.
- Have my continuous integration and 'development test' environments specifically use different paths to that of the development team.


Handling Continuous Integration Failures

Created 19 January 2004

[ Tech / Build Engineering ]

My blogging activity has been subdued recently. To try and remedy this I've decided to write some entries on build engineering, a subject I've looked at in some depth over the last few years.

To start off with, here's an entry about Automated Continuous Integration. Automated CI is normally widely accepted when introduced to a team. However there's often confusion about what to do when the automated build breaks but developers are able to build without problem on their own machine.

The 2 reasons I've seen most often that cause this are:

1 - Problems retrieving updates from Source Control
2 - The part of the build that's failing is not used by developers

The first of these is normally easy to diagnose since it happens early in the build process before anything else has a chance to run. It can occur when a file checked into source control is overwritten by the build process. Often the simplest solution is to delete the build server's copy of the source tree totally, and check it out cleanly from Source Control.

The second part can be caught much more easily using the following pattern:

Be able to run the complete integration build from any development machine

Typically a full integration build should include the following tasks:
- Full, clean compile
- Deployment to local app server
- Running all the tests
- Publishing of distributable files to file server
- Labelling of source control

People sometimes setup specific scripts that are only available on the build server that perform some of these functions. This causes pain in exactly the situation when the build script starts failing. If you can run the entire build on a developer machine using sensible default values (e.g. using a 'test' name for labelling and distributable locations), you can debug the problem without having to go onto the build server.

Moreover, If you have the ability to easily to perform a full, clean, compile; deployment; and test in one easy step from a developer machine, then developers will likely use this and catch any problems before checking in broken code.

Here's an example of this using NAnt and CruiseControl.NET.

In NAnt, we can have the following in a build script to define some properties and specify how to publish a distributable file:

  <property name="publish.basedir" value="publish" />
  <property name="label-to-apply" value="temp" />

  <target name="dist.publish" depends="dist">
    <property name="publish.dir" value="${publish.basedir}\${label-to-apply}" />
    <mkdir dir="${publish.dir}" />
    <copy todir="${publish.dir}">
      <fileset basedir="dist">
        <includes name="*"/>
      </fileset>
    </copy>
  </target>

The 'dist.publish' target relies on the 2 properties publish.basedir and label-to-apply. By setting them in the script we give these values sensible defaults for running on a developer machine. However, we want them to be more specific when running on the real build machine. We can do this by specifying values for the properties when NAnt is started which override these defaults.

If we're using CruiseControl.NET, the 'label-to-apply' property is always set to the build number for us. We can override other variables using the following CCNet Configuration option:

  <cruisecontrol>
    <project name="MyProject">
      <build type="nant">
        <buildArgs>-D:publish.basedir=d:\MyProject\PublishedBuilds</buildArgs>
        .
        .
      </build>
      .
      .
    </project>
  </cruisecontrol>

So, summing up, automated CI build failures are bound to occur. However, if we set up a build process that is repeatable on machines other than the build server it is easier to solve such problems.


Older entries can be found in the Entry Index