5
votes

The question is likely VERY trivial for anyone familiar with ant, of which I only use the basics thus far.

I know how to rename files, e.g. I already use:

<copy todir="build/css/">
    <fileset dir="css/">
        <include name="*.css"/>
    </fileset>
    <globmapper from="*.css" to="*-min.css"/>
</copy>

I know how to calculate an MD5:

<checksum file="foo.bar" property="foobarMD5"/>

I don't know how to include the second into the first, to rename all those files to include their MD5 - the purpose is to serve as webbrowser cache buster. The other cache-busting option, to append "?[something]" is not as good, as is explained on some Google webmaster pages, having the MD5 as part of the name is better.

2

2 Answers

2
votes

I managed to produce a somewhat strange solution using for from ant contrib.
But you have to install ant contrib first.

The copy in the sequential does not seem to accept/evaluate mappers (it wouldn't work, I tried with ant 1.7.0), so I had to create an extra move with a filtermapper to create the results.

It does the following:

  • for each file create an md5sum and save it in property foobarMD5
  • the property has to be unset before each iteration
  • I create a new file in the same dir named example.java_foobarMD5.java (Notice that the filename contains the fileextension)
  • I move all files with .java_ in its name to a new Folder and remove the .java_

I leave this example with .java.

<for param="file">
  <path>
    <fileset dir="src/" includes="**/*.java"/>
  </path>
  <sequential>
    <echo>Letter @{file}</echo>
    <var name="foobarMD5" unset="true"/>
    <checksum file="@{file}" property="foobarMD5"/>
    <echo>${foobarMD5}</echo>
    <copy file="@{file}" tofile="@{file}_${foobarMD5}.java"/>
  </sequential>
</for>

<move todir="teststack" verbose="true">
  <fileset dir="src/">
    <include name="**/*java_*"/>
  </fileset>
  <filtermapper>
    <replacestring from=".java_" to="-"/>
  </filtermapper>
</move>

1
votes

You could do this without having to include ant contrib. I had to implement this for work and was not allowed to introduce that extension for security reasons. The solution I came to was this:

<target name="appendMD5">
    <copy todir="teststack">
        <fileset dir="css/" includes="**/*.css"/>
        <scriptmapper language="javascript"><![CDATA[
            var File = Java.type('java.io.File');
            var Files = Java.type('java.nio.file.Files');
            var MessageDigest = Java.type('java.security.MessageDigest');
            var DatatypeConverter = Java.type('javax.xml.bind.DatatypeConverter');

            var buildDir = MyProject.getProperty('builddir');
            var md5Digest = MessageDigest.getInstance('MD5');
            var file = new File(buildDir, source);
            var fileContents = FIles.readAllBytes(file.toPath());

            var hash = DatatypeConverter.printHexBinary(md5Digest.digest(fileContents));
            var baseName = source.substring(0, source.lastIndexOf('.'));
            var extension = source.substring(source.lastIndexOf('.'));

            self.addMappedName(baseName + '-' + hash + extension);
        ]]></scriptmapper>
    </copy>
</target>

It is worth noting that I wrote this for Java 8 but with some minor tweaks it could work on Java 7. Sadly this won't work for earlier versions of Java without more effort.