Saturday, January 31, 2009

Ant Snippets : expand-to-single-dir

I try to feign ignorance about anything Ant-related, but it hasn't been working very well for me. It seems that no matter which way I turn, I am elbows deep in some Ant-based project. I guess I just like automating the tedious aspects of software engineering and I hate shell scripts even more than I dislike Ant. So I decided to do something very evil. I figure if I start spreading some of the knowledge around, other people will pick up some of these automation tasks and I can take a break from Ant for a while.

First, the most powerful tool in your Ant arsenal is the Ant-Contrib package from SourceForge. Pure Ant is declarative in nature and lacks basic code flow constructs such as loops and if statements that most engineers live by. This is intentional. The core Ant premise is that you should describe what you want to accomplish rather than spelling out how to accomplish it. That works for some people, but I am not one of those people. I take my Ant with a good dose of Ant-Contrib.

Now that we got preliminaries out of the way, I will share a little snippet of Ant that I wrote a while ago to help wrangle various zip-based distributions into usable form. One of the more common chores that you run into is to take a bunch of zip files in a specified directory and expand them all into a single directory (overwriting files as necessary). In the context of Eclipse this scenario comes up often when setting up target platforms from integration builds of various relevant projects or when packaging your Eclipse-based product for distribution (aka the all-in-one kit). It is nice to be able to have a utility in your arsenal that can do this easily and without hard coding the names of all the zips that need to be extracted.

<target name="expand-to-single-dir">
  <check-required-property name="src"/>
  <property name="dest" value="${src}"/>
  <property name="includes" value="*.zip"/>
  <property name="excludes" value=""/>
  <property name="strip.path.prefix" value=""/>
  <expand-to-single-dir 
    src="${src}" dest="${dest}" includes="${includes}" 
    excludes="${excludes}" strip-path-prefix="${strip.path.prefix}"/>
</target>
  
<macrodef name="expand-to-single-dir">
  <attribute name="src"/>
  <attribute name="dest"/>
  <attribute name="includes" default="*.zip"/>
  <attribute name="excludes" default=""/>
  <attribute name="strip-path-prefix" default=""/>
  <sequential>
    <mkdir dir="@{dest}"/>
    <for param="component.archive">
      <path>
        <fileset dir="@{src}" includes="@{includes}" excludes="@{excludes}"/>
      </path>
      <sequential>
        <unzip src="@{component.archive}" dest="@{dest}" overwrite="true">
          <mapper type="regexp" from="^@{strip-path-prefix}(.*)$$" to="\1"/>
        </unzip>
      </sequential>
    </for>
  </sequential>
</macrodef>
  
<macrodef name="check-required-property">
  <attribute name="name"/>
  <sequential>
    <if>
      <not><isset property="@{name}"/></not>
      <then>
        <fail message="Property '@{name}' must be specified."/>
      </then>
    </if>
  </sequential>
</macrodef>

The above snippet defines expand-to-single-dir macro that you can easily use when writing your Ant targets and it also defines a target with the same name. The target is useful as a stand-alone tool that can be invoked directly from the command line. Since I am an Eclipse developer these days, the second part of this snippet adds a convenience target that calls the above utility in a way that also strips the typically-unwanted leading “eclipse” directory that is present in all Eclipse distributions.

<target name="expand-eclipse-zips">
  <check-required-property name="src"/>
  <property name="dest" value="${src}"/>
  <expand-to-single-dir 
    src="${src}" dest="${dest}" includes="*.zip" strip-path-prefix="eclipse/"/>
</target>