Symphonious

Living in a state of accord.

Importing an ant buildfile multiple times

According to the ant documentation:

It is possible to include the same file more than once by using different prefixes, it is not possible to import the same file more than once.

Unfortunately, it turns out this isn’t quite true. In most cases, ant will indeed ignore a request to import a file for the second time, so:

<import>
<file name="utils.xml"/>
<file name="utils.xml"/>
</import>

 

will only import utils.xml once. This is true even if there’s a chain of files being imported (so A imports B and C, then B imports C as well, C will only be imported once).

The problem arises if you’re importing the file via two different methods, you might wind up having it actually be imported twice. In my case I was loading build files from a zip archive (why is a long story that I should write up some time) and those build files references other files also within the zip file. So my main build file had:

<import>
<javaresource classpath="bootstrap.zip"
name="basic-build.xml"/>
</import>

Then basic-build.xml included another utils file from the same zip. Being the well-coded ant file that it is, basic-build.xml doesn’t depend on knowing where it’s actually located, so it imports the utils file via:

<import>
<url baseurl=${ant.file.basic-build}" relativePath="ivy-utils.xml"/>
</import>

This all works just fine. Then there came a requirement in our main build file to use one of the macros from ivy-utils.xml to set a version number before basic-build.xml was imported. So the main build file wound up with something like:

<import>
 <javaresource classpath="bootstrap.zip"
name="ivy-utils.xml"/> </import>
<!-- Do some stuff --> <import>
<javaresource classpath="bootstrap.zip"
name="basic-build.xml"/>
</import>

Suddenly the build started failing because ivy-utils.xml was being executed twice and attempting to redefine the ivy configuration which was explicitly disallowed.

Looking at the ant -debug output clearly showed the ivy-utils.xml was being imported twice and not skipped, and even that it came from exactly the same URL. It appears that resolving the same file using two different mechanisms (javaresource and url in this case) caused ant to believe they were different files.

The answer was fairly straight-forward, use url resources consistently when importing the files:

<import>
 <url url="jar:file:${ant.file}/bootstrap.zip!/ivy-utils.xml"/>
</import>
<!-- Do some stuff --> <import>
<url url="jar:file:${ant.file}/bootstrap.zip!/basic-build.xml"/>
</import>

Building a jar: URL like this is a little unpleasant but not unreasonable. Strictly we could have kept importing basic-build.xml using the javaresource approach but being consistent will save surprises in the future.