viebel
viebel

Reputation: 20670

How to avoid running ant tasks on source files that have not changed?

I have an ant task that executes some command on a list of files. I would like, on consecutive builds, to avoid from re-running the command on files that have passed the command with success and haven't changed.

For example: (here the command is xmllint)

 <target name="xmllint-files">
    <apply executable="xmllint">
        <srcfile/>
        <fileset dir="." includes="*.xml">
            <modified/>
        </fileset>

    </apply>
</target>

The problem is that even the files where xmlint fails are considered as modified and therefore xmllint will not be re-run on them on consecutive builds. Obviously, this is not the desired behavior.

Two remarks:

  1. I am looking for a general solution and not only a solution for xmllint.
  2. I want to solve the problem totally inside ant without creating external scripts.

Upvotes: 3

Views: 990

Answers (1)

Mark O&#39;Connor
Mark O&#39;Connor

Reputation: 77961

This code uses the Groovy ANT task to do the following:

  • Implement a custom groovy selector, selecting the XML files to be processed based on a MD5 checksum check.
  • Invoke xmllint on each file and store it's checksum upon successful completion (This prevents re-execution of xmllint unless the file's contents are changed.

Example:

<project name="demo" default="xmllint">

    <!--
    ======================
    Groovy task dependency
    ======================
    -->
    <path id="build.path">
        <pathelement location="jars/groovy-all-1.8.6.jar"/>
    </path>
    <taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="build.path"/>

    <!--
    ==============================================
    Select files to be processed 
    MD5 checksums located in "checksums" directory
    ==============================================
    -->
    <target name="select-files">
        <fileset id="unprocessedfiles" dir=".">
            <include name="*.xml"/>
            <exclude name="build.xml"/>
            <scriptselector language="groovy" classpathref="build.path">
                def ant = new AntBuilder()
                ant.checksum(file:filename, toDir:"checksums", verifyProperty:"isMD5ok")

                self.selected = (ant.project.properties.isMD5ok == "false") ? true : false
            </scriptselector>
        </fileset>
    </target>

    <!--
    =============================================================
    Process each file 
    Checksum is saved upon command success, prevents reprocessing
    =============================================================
    -->
    <target name="xmllint" depends="select-files">
        <groovy>
            project.references.unprocessedfiles.each { file ->
                ant.exec(executable:"xmllint", resultproperty:"cmdExit") {
                    arg(value:file)
                }
                if (properties.cmdExit == "0") {
                    ant.checksum(file:file.toString(), toDir:"checksums")
                }
            }
        </groovy>
    </target>

</project>

Note:

  • This complex requirement cannot be implemented using the original apply ANT task. One call to xmllint command might succeed whereas another might fail.
  • A subdirectory called "checksums" is created to store the MD5 checksum files.
  • The groovy jar can be downloaded from Maven Central

Original answer

Use the ANT modified selector

<project name="demo" default="xmllint">

    <target name="xmllint">
        <apply executable="xmllint">
            <srcfile/>
            <fileset dir="." includes="*.xml">
                <modified/>
            </fileset>
        </apply>
    </target>

</project>

A property file called "cache.properties" will be created in the build directory. It records file digests, used determine if the file has been changed since the last build run.

Upvotes: 3

Related Questions