Dry run mode for Ant (ant -n, ant --dry-run)

January 31, 2013 [Java, Tech, Test Driven]

I am working on the problem of writing Ant build files in a test-driven way. One thing I found myself needing was a "dry run" mode, like many Unix tools have. For example, make has the -n or --dry-run option, which shows what it would have done, but doesn't really do it.

Today I found a partial solution to this problem, so that you can at least see which dependencies will be run when you run a particular ant target.

It's an horrific hack, but it's the best I can do at the moment.

We write some code in a <script> tag to hack all the targets in our project (at runtime). We modify the targets so they all have an "unless" attribute, set to a property name of "DRY-RUN". Then we set the "DRY-RUN" property, and execute our target.

Ant prints out the names of all the targets in the dependency chain, even if they are not executed because of an unless attribute.

Note: this code makes use of the Ant <script> script tag, which is an Ant 1.8+ feature. Using JavaScript inside this tag seems to be supported in Oracle, OpenJDK and IBM versions of Java, but is not guaranteed.

<?xml version="1.0" encoding="UTF-8"?>
<project default="build">

    <target name="targetA"/>
    <target name="targetB" depends="targetA">
        <echo message="DON'T RUN ME"/>
    </target>
    <target name="targetC" depends="targetB"/>

    <target name="build" depends="targetB"/>

    <target name="dry-run">
        <do-dry-run target="build"/>
    </target>

    <macrodef name="do-dry-run">
        <attribute name="target"/>
        <sequential>
            <script language="javascript"><![CDATA[

                var targs = project.getTargets().elements();
                while( targs.hasMoreElements() ) {
                    var targ = targs.nextElement();
                    targ.setUnless( "DRY.RUN" );
                }
                project.setProperty( "DRY.RUN", "1" );
                project.executeTarget( "@{target}" );

            ]]></script>
        </sequential>
    </macrodef>

</project>

Running this build file normally, the tasks in the targets execute, so we can see that the <echo> happens:

$ ant
Buildfile: build.xml

targetA:

targetB:
     [echo] DON'T RUN ME

build:

BUILD SUCCESSFUL
Total time: 0 seconds

But when we run the dry-run target, only the target names are printed, and the <echo> task (and any other tasks) don't:

$ ant dry-run
Buildfile: build.xml

dry-run:

targetA:

targetB:

build:

BUILD SUCCESSFUL
Total time: 0 seconds

A lot of pain, for a partial implementation of very simple functionality that you'd expect to be a built-in feature? I couldn't possibly comment.