Ant Best Practices: The Clean Test

Ant Best Practices: The Clean Test. Last time, we discussed the ZipFileSet. Today’s article is much more important – it’s a subject very close to my heart: only dong necessary work. If you don’t execute the clean target, (do tell me your build has a clean target and you didn’t change any of files in your project, what should happen?

I’ll tell you what should happen. Nothing. Diddly. Well, no compilation. If no compilation happened, should it bother packaging up artefacts or anything? I don’t think so. If you want to run tests, so be it; you might have an unreliable test. But for the most part, nothing should happen if nothing changed. This is the clean test.

Why? Think of it like this. If you change a single CSS file on your project, do you want to go and recompile all your code and run your tests? Hold that thought.

On the first and largest project that I ever did at ThoughtWorks, the fast build was 45 minutes. Don’t even go there with the build that did automated functional tests. One evening I was working late with one of the performance testers. The CruiseControl build was firing all the time. We realised that every time he did a performance test run, he checked in the results into the VCS alongside the code. That triggered a build. Whoops. We didn’t always have thatcapacity to spare. I have since sat there on many a project and watched builds trigger because a developer had to make the tiniest change. It’s dull to sit through a tedious CI build, just because the build didn’t discriminate between the files that had changed.

This stuff can make a difference. When your project has grown to a larger size, rest assured that it will be hard to change these things. So how do you convince Ant to be less zealous? Start by making sure that your default target doesn’t depend on your ‘clean’ target. When you refactor code and move classes around, you might need to execute the clean target. But treat that as the exception and not the rule.

That’s fine until you start packaging up the code in zip or jar files. What happens then? The uptodate task is your friend. This little guy will take a look at the sources and output of a task, and tell you if you should bother running it. Take a look:


<project default="archive">
<property name="build.directory" location="build" />
<property name="source.directory" location="src" />
<property name="archive" location="${build.directory}/stooges.zip" />
<fileset dir="${source.directory}" includes="*.xml" id="archive.files"/>
<target name="clean">
<delete>
<fileset dir="${build.directory}" includes="**/*"/>  </delete>
</target>
<target name="check-archive">
<echo message="Checking zip file time stamp" />
<uptodate property="-archive.is.unchanged" targetfile="${archive}">
<srcfiles refid="archive.files"/>
</uptodate>
</target>
<target name="archive" unless="-archive.is.unchanged" depends="check-archive">
<echo message="Making a zip file"/>
<zip file="${archive}" >
<fileset refid="archive.files" />
</zip>
</target>
</project>

The first time that you run this build, it does what you expect: makes a zipfile. The clever bit is what it does the second time you run it. It doesn’t bother. Here’s the first run:

Detected Java version: 1.6 in: /usr/lib/jvm/java-6-sun-1.6.0.06/jre
Detected OS: Linux
parsing buildfile /home/jsimpson/Documents/workspace/playpen/code/up2date.build.xml with URI = file:/home/jsimpson/Documents/workspace/playpen/code/up2date.build.xml
Project base dir set to: /home/jsimpson/Documents/workspace/playpen/code
[antlib:org.apache.tools.ant] Could not load definitions from resource org/apache/tools/ant/antlib.xml. It could not be found.
Build sequence for target(s) `archive' is [check-archive, archive]
Complete build sequence is [check-archive, archive, clean, ]

check-archive:
[echo] Checking zip file time stamp
[uptodate] The targetfile "/home/jsimpson/Documents/workspace/playpen/code/build/stooges.zip" does not exist.

archive:
[echo] Making a zip file
[zip] Building zip: /home/jsimpson/Documents/workspace/playpen/code/build/stooges.zip
[zip] adding entry iggy.xml
[zip] adding entry dave.xml

BUILD SUCCESSFUL
Total time: 1 second

You can tell that it was checking the lie of the land before it executed the main task. You might like the output from the second task:


Detected Java version: 1.6 in: /usr/lib/jvm/java-6-sun-1.6.0.06/jre
Detected OS: Linux
parsing buildfile /home/jsimpson/Documents/workspace/playpen/code/up2date.build.xml with URI = file:/home/jsimpson/Documents/workspace/playpen/code/up2date.build.xml
Project base dir set to: /home/jsimpson/Documents/workspace/playpen/code
[antlib:org.apache.tools.ant] Could not load definitions from resource org/apache/tools/ant/antlib.xml. It could not be found.
Build sequence for target(s) `archive' is [check-archive, archive]
Complete build sequence is [check-archive, archive, clean, ]

check-archive:
[echo] Checking zip file time stamp
[uptodate] iggy.xml omitted as /home/jsimpson/Documents/workspace/playpen/code/build/stooges.zip is up to date.
[uptodate] dave.xml omitted as /home/jsimpson/Documents/workspace/playpen/code/build/stooges.zip is up to date.
[uptodate] No sources found.
[uptodate] File "/home/jsimpson/Documents/workspace/playpen/code/build/stooges.zip" is up-to-date.

archive:
Skipped because property '-archive.is.unchanged' set.

BUILD SUCCESSFUL
Total time: 1 second

There you have it. It didn’t bother creating the zip file because the sources hadn’t changed. That’s useful. But what about the duplication?

Admittedly you do need to define the sources and target of the task twice. This could lead to duplication. If you look at the example, you’ll see how I got around that:

  • the target of the zip task is a property. So that’s fine.
  • the sources of the zip task are all types of FileSet. So I defined the FileSet once and passed the reference to both tasks.

Everyone’s build should undertake the Clean Test.

Tagged

3 thoughts on “Ant Best Practices: The Clean Test

  1. sudheendra says:

    where can i find antlib.xml file since the same message is appearing as ……. [antlib:org.apache.tools.ant] Could not load definitions from resource org/apache/tools/ant/antlib.xml. It could not be found.

  2. builddoctor says:

    IIRC, Ant will look for an XML file in it's classpath for this. You'd need
    to tell me what the actual problem is, as this message is probably a red
    herring.

  3. Elena says:

    Easier said than done. Ant is not powerful enough to let you write needed checks without duplication. Your example shows a very simple case. What do you do when you need a dependencies list of both absolute paths, relative paths, and path patterns? You can use neither “fileset” nor “filelist” for that. What do you do when you’d have to merge the content of two or more “filelist” or “fileset” elements? Not possible.

    Ant does not pass the “clean” test. How could your build script do that?

    Cheers.

Comments are closed.

%d bloggers like this: