The binary dependency boogy

Sooner or later, every software project ends up relying on some binary module, which could could be a third-party library or another project in compiled form. In the Java world, these are jar files (remember back in the Age of Aquarius when they were zip files too?!) and on the Microsoft platform they’re dlls.

Given that it’s pretty much a universal truth that eventually you’ll have to come up with a binary dependency management scheme, I’m amazed with the myriad different ways I’ve seen this handled. Two of the most common techniques I’ve seen are:

  • Place all libraries on a shared drive
  • Place all libraries in a lib folder underneath the project’s root

Of course, these strategies work; however, they aren’t optimal. Placing all libraries on a shared drive becomes a maintenance nightmare if they are haphazardly placed there– like in one giant directory. Then of course, you have the issue with drive mapping, which works fine if everyone’s on the same OS.

Placing libraries in a specific project folder (and of course, adding them to an SCM) is a good strategy in that a project’s dependencies are managed within the project itself. If you end up having multiple projects that have the same dependencies; however, you end up with the same jar file, for example, copied throughout an SCM, which has always seemed inelegant to me.

I, instead, prefer a binary dependency management mechanism spearheaded by the Maven project, which can be emulated in Ant, NAnt, and any other build platform that supports HTTP gets. Using this scheme, dependencies are placed on a web server and are versioned via a naming scheme. During the build process, required libraries are downloaded to the local machine and are included in the classpath for compilation, runtime execution, etc.

The beauty here is the naming scheme. For example, say I’m building a project which uses JUnit for its test framework; consequently, to compile and run resulting tests, the build process requires JUnit’s jar file in the classpath. If my project requires version 3.8.1, then I can create a few properties to represent the dependency as follows:

<property name="junit-jar" value= "junit-3.8.1.jar" />

Then I need a disco place to download it from– I can either set up my own web server for the project, or use a publically available one, such as ibiblio.org, which in that case, the download URL would be the base: http://www.ibiblio.org/maven/junit/jars/ and then the jar file: junit-3.8.1.jar.

I also need to specify a place to download dependencies to, which in my case is set via a property:

<property name="libdir" value="target/lib" />

Note too, I can have multiple repositories from which to obtain dependencies from, so I can create a repository property as well. Putting it all together yields a get that looks like this:

<get dest="${libdir}/${junit-jar}" usetimestamp="true"
       ignoreerrors="true"
       src= "${repo-base}/junit/jars/${junit-jar}" /> 

Creating a classpath is as easy as including all the jars in the target/lib directory as I’ve done below.

<path id="build.classpath">
  <fileset dir="${libdir}">
   <include name="**/*.jar">
   </include>
  </fileset>
</path>

Of course, targets which compile code or run tests, would then depend on the dependencies being present; accordingly, they can specify a depends clause which links back to a target that performs a series of gets.

<target name="compile" depends="get-deps">....</target>

The get-deps target also has the hip ability to skip downloads should the libraries already be in place; therefore, a noget property is available as shown below:

<target name="get-deps" unless="noget" depends="init">...</target>

I should add that there are more sophisticated mechanisms available for Ant which handle dependency management– Ivy is one such project that is probably the most feature rich. Both Ivy and Maven2 support the notion of transitive dependencies, which is way disco and limits a lot of the grunt work when specifying dependencies. If you’ve ever done this with something like Hibernate or Spring, you’ll know what I mean.

What I’ve shown here, by the way, is applicable in NAnt as well using its get task.

The next time you find yourself unable to build because the link to the share drive is down or you are unsure what exact version of a library is required for compilation, consider using the Maven scheme for dependency management– it’ll free up some copasetic time for more disco dancing.

Related odds and ends
 

Trackback this Post | Feed on comments to this Post

Leave a Reply