Ant Practices

In regards to a rant about ApacheAnt being sub-optimal, StevenNewton had the following to say:

Indeed, declarative semantics are what differentiate Ant. Trying to make Ant do things it wasn't meant to do, like looping through a list of subprojects rather than declaring the dependencies and letting Ant work out the details, is driving screws with a hammer. As a member of the ant-dev list and a contributor to the project, I regularly see this nature misunderstood, and calls for a looping task or some sort of if/then/else construct get rejected with a polite, "That's not the Ant way". Learning to use Ant after make is like learning a functional language after C. But once the real meaning of <target name="all" depends="subproject1, subproject2, subproject3... subprojectN"/> sinks in, you will give literal-minded make the boot it so richly deserves. -- StevenNewton

For those who just don't want to follow the Ant Way(tm), ThereIsAnotherWay (sorry, couldn't help it!!). Check out http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/ant-contrib/ant-contrib/. -- a [BrokenLink]

See AntTheDefinitiveGuide for why it is the way it is.

This is all fine and good, and I understand the concept of depends and all, but what if subprojectN is only there under certain conditions? Our system had about 15 "modules" which may, or may not be checked out (in fact, it's highly likely that only 2 at a time would be in a developers environment). What is the Ant Way (tm)? Conditional execution based on the presence of the subproject?


The <available> task (or the <condition> task for more recent versions), combined with property checking in the target should do this for you.
  <available file="./subprojectAdir" type="dir" property="subprojectA.present"/>
  <target name="all" depends="build-project-A"/>
  <target name="build-project-A" if="subprojectA.present"/>
In a real world example:
  <available file="/enCommerce/getAccess/bin/AccLic.dll" property="getaccess.available"/>
    <target name="test" depends="compile" description="run unit tests">
        <mkdir dir="${junit.results}"/>
        <junit fork="true"
               dir="${basedir}"
               printsummary="true">
            <classpath refid="test.path"/>
            <formatter type="xml"/>
            <batchtest todir="${junit.results}">
                <fileset dir="${builddir}/classes">
                    <exclude name="**/security/jndi/tests/*.class" unless="getaccess.enabled"/>
                    <exclude name="**/security/tests/*SOAP*.class" unless="getaccess.enabled"/>
                    <exclude name="**/security/tests/GetAccess?*.class" unless="getaccess.enabled"/>
                </fileset>
            </batchtest>
        </junit>
    </target>

Thanks for the fine example! Unfortunately, I can't see how it fixes the nastiness of having to keep a list of modules hanging around, which you can't iterate over. Instead, one is required to write out an "available" property for each project. Am I missing something obvious here?

ex:

 	<available file="./subprojectBdir" type="dir" property="subprojectB.present"/>
	<available file="./subprojectCdir" type="dir" property="subprojectC.present"/>
	<available file="./subprojectDdir" type="dir" property="subprojectD.present"/>
	<available file="./subprojectEdir" type="dir" property="subprojectE.present"/>
Each of which would require a corresponding target invoked conditionally later on as well. Is looping so evil in comparison?

If you need to do this, you can always write a custom task to do it. The NetBeans project has (had?) a task to do exactly that; given a list of modules, go away and build them. The custom task option is a damn sight cleaner than the JavaScript option. This is why the decision to put scripting into Ant was a very controversial one on the list (you could do anything you wanted to in a custom task, after all...).

So I guess that the Ant Way stipulates that we write special Java code to create custom tasks for each and every ant-based project that requires any sort of iterative or conditional build recipes that we don't want to be byzantinely (is that a word?) complex in our antfiles. And if, during the course of our project, packages are moved, deleted, or added, it could then be necessary to code, compile, install, and debug new versions of these Java-based custom tasks to accommodate these changes. And since many of these custom tasks are going to be extremely similar to other such tasks that people are writing for other projects, there is going to be a worldwide duplication of effort to write, compile, install, and debug these nearly (or totally) identical tasks. And we have all this extra time and duplicated effort simply for the purpose of maintaining the Ant Way.

The ant-contrib package is an example of some custom tasks that were written to be simple and generic, and which can be used to alleviate the need for spending valuable time "reinventing the wheel" by writing custom Java code every time a complex conditional or an iteration might be necessary to build some software. Sure, ant-contrib and others of its ilk indeed deviate from the Ant Way. But few companies want to spend time and money simply to adhere to an abstract ideal (such as the Ant Way or Pure Declarative Programming) that contributes nothing but added time and maintenance costs to their software development efforts.

I say that the following two things can and should happily exist side by side:

It would be nice if the ant development team would be more supportive of productivity tools like ant-contrib, and actually encourage people to use them when needed. To religiously advocate the current formulation of the Ant Way, even in cases where that would lead to increased development and maintenance costs for projects, seems to me to be a sure-fire way to lower the probability that project managers would elect to use ant at all. -- LloydZusman


See also: AntUsageTask
CategoryAnt

EditText of this page (last edited January 20, 2009) or FindPage with title or text search