A file is a target is a property

Recently I have adopted a new pattern in my ant scripts to ensure correct dependencies between targets.

Whenever a target references another file, it does it using a property. And the property is defined in the target that generates the file. This way it is impossible to access the file without properly depending on it as a target.

If a target has a corresponding "status" target that checks the uptodate status, the property must be defined in the status target. Otherwise the property won't be set when the target is uptodate.

However, ant causes an annoying practical problem by silently ignoring an invalid property reference and using the reference itself as the literal path. So, while this pattern ensures that missing dependencies are caught, the feedback isn't always very helpful or even immediate.

An example:


<project name="demo" default="test-report">

  <target
    name="test-report"
    depends="
      classes,
      test-classes,
      targetdir
    "
  >
    <echo message="mock running the tests"/>
    <echo message="  referencing ${test-classes}"/>
    <echo message="  and ${classes}"/>
  </target>

  <target
    name="test-classes"
    depends="
      classes,
      targetdir,
      test-classes-status
    "
    unless="test-classes-is-uptodate"
  >
    <delete dir="${test-classes}"/>
    <echo message="Mock creating ${test-classes}"/>
    <echo message="  referencing ${classes}"/>
    <mkdir dir="${test-classes}"/>
    <touch file="${test-classes-timestamp}"/>
  </target>

  <target
    name="test-classes-status"
    depends="
      classes-status,
      targetdir
    "
  >
    <property
      name="test-classes"
      value="${targetdir}/test-classes"
    />
    <property
      name="test-classes-timestamp"
      value="${targetdir}/test-classes.timestamp"
    />
    <uptodate
      property="test-classes-is-uptodate"
      targetfile="${test-classes-timestamp}"
    >
       <srcfiles dir="tests" includes="**/*"/>
       <srcfiles file="${classes-timestamp}"/>
    </uptodate>
  </target>

  <target
    name="classes"
    depends="
      targetdir,
      classes-status
    "
    unless="classes-is-uptodate"
  >
    <delete dir="${classes}"/>
    <echo message="Mock creating ${classes}"/>
    <mkdir dir="${classes}"/>
    <touch file="${classes-timestamp}"/>
  </target>

  <target
    name="classes-status"
    depends="
      targetdir
    "
  >
    <property
      name="classes"
      value="${targetdir}/classes"
    />
    <property
      name="classes-timestamp"
      value="${targetdir}/classes.timestamp"
    />
    <uptodate
      property="classes-is-uptodate"
      targetfile="${classes-timestamp}"
    >
       <srcfiles dir="src" includes="**/*"/>
    </uptodate>
  </target>

  <target
    name="targetdir"
  >
    <property name="targetdir" value="${basedir}/target"/>
    <mkdir dir="${targetdir}"/>
  </target>

</project>

Originally published on 2008-05-21 at http://www.jroller.com/wipu/entry/a_file_is_a_target under category SCM & process