Jul 05 2010

Ant 1.8, Reusable XML Scriptlets, and Ivy for Self-Containment

Category: Software DevelopmentPhil @ 8:33 am

I started reading about EasyAnt sometime ago on the Ivy developer forums. It was a very interesting concept, storing reusable Ant targets in your Ivy Repository and pulling them into projects at build time. Ivy works great for jar files, so why not Ant build scripts?  I have a set of Ant scripts in my toolbox that seem to get copied from project to project; it bothers me every time I copy them! We gave EasyAnt a quick look, but it had one show stopper; it required every developer and builder to have EasyAnt installed (rather than just Ant). This is a little thing, but when you are working with dozens of projects and hundreds of developers, it seemed like too much of a hurdle.  I wanted Ant to be the only installation requirement and be “self contained”. Just type “ant…” Nice and simple.

I started addressing the problem my making templates out of my scripts and placing them into a Subversion repository. They could easily be included into a project using the Ant <get> task. A little ugly, but it worked well enough. It did required us to land the XML files in the project directory, such that they could be “imported” into the running build.  Once again, not a problem if you remember to use SVN ignore on the .ant directory; we might have been able to put them in /tmp, but never tried it.

<property name="ant.xml.location" value="http://svn:1234/Ivy/Ant/Common/trunk/main/1.0/xml" />
<mkdir dir=".ant" />
<get src="${ant.xml.location}/ant.ivy.xml" dest="${basedir}/.ant/ant.ivy.xml" usetimestamp="true" />
<get src="${ant.xml.location}/ant.checkstyle.xml" dest="${basedir}/.ant/ant.checkstyle.xml" usetimestamp="true" />

<import file=".ant/ant.ivy.xml" />
<import file=".ant/ant.checkstyle.xml" />

With Ant 1.8, you can actually accomplish this in a much cleaner method. Look at the import task for some quick examples. Our first test was a very simple implementation, but worked like a champ.

I personally like to break my Ant scripts into multiple files, each with it own purpose. Some of my coworkers hate this approach and would prefer a single XML file. In the long run, the multi-file approach seems easier to support, especially when you factor in multiple maintainers. You would never put all of your Java code in a single class, why would you put all of your XML in a single file? I will ultimately create a single file to import; it will import all of the individual XML files. This should make the consumers of the templates a little happier!

<property name="ant.xml.location" value="http://svn:1234/Ivy/Ant/Common/trunk/main/1.0/xml" />
<import>
       <url url="${ant.xml.location}/ant.ivy.xml" />
       <url url="${ant.xml.location}/ant.checkstyle.xml" />
       <url url="${ant.xml.location}/ant.pmd.xml" />
       <url url="${ant.xml.location}/ant.clover.xml" />
       <url url="${ant.xml.location}/ant.findbugs.xml" />
       <url url="${ant.xml.location}/ant.testability.xml" />
</import>

I think the javaresource approach would be the perfect way to solve my problem. Unfortunately, I could not find any way to combine a remote URL with a classpath attribute. It is a neat little trick to remember, maybe this feature could be added to the next version of Ant.

<import>
    <javaresource name="common/targets.xml">
      <classpath location="common.jar">
<!--  <url url="http://somewhere/common.jar/> -->
    </classpath>
  </javaresource>
</import>

One final word about completeness. My personal goal is to make the build system completely self-contained, meaning no external dependencies (outside of Ivy), no custom Ant installations, no Ant-lib requirements, or any manual configuration. Ideally, a developer should be able to check out the project from the version control system and get to work. It should automatically build and execute from Eclipse, and as long as Ant is installed, the developer should just be able to type Ant <target> to accomplish where ever is required.

I have been changing all of my templates to make use of the ivy:cachepath and ivy:cachefileset tasks. In prior Ant implementations, I used Ivy retrieve to access all of the dependencies; my new goal is to only use files from the Ivy cache. Additionally, I think the Ant templates should do their own Ivy dependency management. Now applications teams only need to worry about their own dependencies, not what is required by the environment or build system. The follow example demonstrates how to add Clover to the build process, and ensure that the Clover license is added to your class path, all without really knowing where the files are… Pretty cool!

<ivy:cachepath pathid="classpath.CLOVER" conf="runtime" organisation="atlassian" module="clover" revision="3.0.2" inline="true" />
<ivy:cachefileset setid="fileset.CLOVER" conf="license" organisation="atlassian" module="clover" revision="3.0.2" inline="true" />
<pathconvert pathsep="${line.separator}-> " property="clover.license.path" refid="fileset.CLOVER" setonempty="" />
<dirname file="${clover.license.path}" property="clover.license.dir" />

<taskdef resource="cloverlib.xml">
     <classpath>
          <path refid="classpath.CLOVER" />
          <path location="${clover.license.dir}" />
      </classpath>
</taskdef>
https://www.beilers.com/wp-content/plugins/sociofluid/images/digg_48.png https://www.beilers.com/wp-content/plugins/sociofluid/images/reddit_48.png https://www.beilers.com/wp-content/plugins/sociofluid/images/dzone_48.png https://www.beilers.com/wp-content/plugins/sociofluid/images/stumbleupon_48.png https://www.beilers.com/wp-content/plugins/sociofluid/images/delicious_48.png https://www.beilers.com/wp-content/plugins/sociofluid/images/blinklist_48.png https://www.beilers.com/wp-content/plugins/sociofluid/images/blogmarks_48.png https://www.beilers.com/wp-content/plugins/sociofluid/images/google_48.png https://www.beilers.com/wp-content/plugins/sociofluid/images/facebook_48.png