Separation of Concerns in Ant

In a previous post, I wrote about separation of concerns in various software tools. One of these was Ant. Since that time, I have come up with a solution to the problem.

To recap, the problem is that an Ant script defines two perpendicular dimensions of a build: actions and projects. Actions depend upon other actions, and projects on other projects. The work occurs at the intersections of these two dimensions, where an action is applied to a project.

Here's my solution
The first step is to completely parameterize the actions so that they can be applied to any project. The locations of source files, the names of target jar files, and the classpath can all be specified as properties. Create a separate "actions.xml" file for these targets. The actions do not depend upon one another. These dependencies will be handled at a higher level. For example:

 <property prefix="project" file="${home}/project.properties" />  <!-- Get the source files. -->
 <target name="get">
  <!-- Set Defaults -->
  <property name="branch" value="trunk" />
  <property name="revision" value="HEAD" />
  <svn username="${username}" password="${password}">
   <checkout url="https://svn.mallardsoft.com/svn/productline/${branch}/${project}/" revision="${revision}" destPath="${temp}" />
  </svn>
 </target>

 <!-- Compile the java sources. -->
 <target name="compile">
  <javac srcdir="${src}" destdir="${classes}" debug="${debug}">
   <classpath>
    <pathelement path="${project.libs}" />
    <pathelement path="${project.depends}" />
   </classpath>
   <include name="**/*.java" />
  </javac>
 </target>

 <!-- Jar the classes. -->
 <target name="jar">
  <jar destfile="${dist}/${project.jar}.jar">
   <fileset dir="${classes}" />
   <fileset dir="${src}">
       <exclude name="**/*.java"/>
   </fileset>
  </jar>
 </target>

The second step is to create a properties file for each project. Paths are specified as colon-separated lists. I've already loaded two additional properties files that specify the locations of library and project jar files:

jar=business_layer
libs=${lib.logging-log4j}:${lib.xerces}
depends=${proj.database_layer}

The third step is to create a target for each project in your regular "build.xml" file. The target sets the "project" property and calls a target in "actions.xml":

 <target name="database_layer">
  <ant antfile="actions.xml" target="${target}">
   <property name="project" value="database_layer" />
  </ant>
 </target>

 <target name="business_layer" depends="database_layer">
  <ant antfile="actions.xml" target="${target}">
   <property name="project" value="business_layer" />
  </ant>
 </target>

 <target name="web_interface" depends="business_layer">
  <ant antfile="actions.xml" target="${target}">
   <property name="project" value="web_interface" />
  </ant>
 </target>

 <target name="swing_interface" depends="business_layer">
  <ant antfile="actions.xml" target="${target}">
   <property name="project" value="swing_interface" />
  </ant>
 </target>

 <!-- Invoke the same target in all projects. -->
 <target name="all" depends="web_interface,swing_interface" />

The fourth step is to define targets in build.xml that invoke the same action on all projects. These targets specify the dependencies among actions"

 <!-- Get all projects. -->
 <target name="get">
  <antcall target="all">
   <param name="target" value="get" />
  </antcall>
 </target>

 <!-- Compile all projects. -->
 <target name="compile" depends="get">
  <antcall target="all">
   <param name="target" value="compile" />
  </antcall>
 </target>

 <!-- Jar all projects. -->
 <target name="jar" depends="compile">
  <antcall target="all">
   <param name="target" value="jar" />
  </antcall>
 </target>

The designers of Ant probably never intended their tool to be used in this way, but it separates the actions from the projects. Now I can tweak an action for all projects in one place. Or I can add new projects and get all actions applied to it.

One Response to “Separation of Concerns in Ant”

  1. Adventures in Software » Blog Archive » Three degrees of freedom in ant Says:

    [...] The prior build process had two degrees of freedom. On one axis, we have the set of projects. On the other, we have the set of build steps. We represented these two degrees of freedom with two separate ant scripts. build.xml lists the many different jar and war files and the relationships among them. It calls actions.xml via the <ant> task, which lists the build steps like clean, get, compile, unit test, jar, and war. For details, please see Separation of Concerns in Ant. [...]

Leave a Reply

You must be logged in to post a comment.