Project Dependencies Using Ant

Overview

This article discusses a technique for managing the build order of separate sub-projects in a large software system purely using task dependencies within Ant scripts.

Unlike other solutions, this technique for managing dependencies does not need any external tasks to those already distributed with Apache Ant, as it leverages Ant’s inbuilt target dependency behaviour. See the Ant Related Projects page for many examples of other alternative solutions to the method presented in this article.

Example System

For the purposes of describing this technique, I’ll use an example application that has been split into four components:

web

Views and user interaction code. Depends upon the model and common components.

admin

Functionality to administer the application data. Depends upon the model and common components.

model

Objects that encapsulate data and associated behaviour. Depends upon the common component.

common

Shared utilities and methods. Has no other dependencies.

Based upon this description, the dependencies between components can be illustrated as follows:

These projects are contained in sibling directories underneath a top level “example” directory.

Building a Component

The standard way to build a component from Ant is to declare a build or compile target and use the javac task. Other targets can build a distributable or clean up generated files. For example:

Traditionally, a script similar to the example above would have been copied into each of the component directories. However, Ant 1.6 introduced the <import> task, which greatly simplifies writing scripts for multiple projects with a similar structure. Making use of this feature, we will use a common build script containing all the shared targets, and a much simplified script in each component.

admin/build.xml

The only difference for the other projects is the value of the name attribute of the project element. To build the distributable for a project, enter the following:

> ant dist

This works fine if all dependencies are already present. However, in the case of the admin, model and web components, the code depends upon jars that may not yet be built. There needs to be a way to ensure that building any component will always build dependencies beforehand.

Declaring Dependencies

Dependencies between components are described within a single Ant script called dependencies.xml, saved at the top level of the directory structure. For each component, there is a corresponding target called depend.{componentname} defined within dependencies.xml. The depends list for this target will specify the other component dependencies. The sole task for each of these targets is to recursively call Ant within the corresponding component directory. Putting this all together, the script looks like this: