It’s pretty easy to create an Ant file for a simple project. A simple Ant script typically contains ubiquitous “init”, “compile”, “test”, “war” (or “jar), “build” targets all wired together. It’s easy to change and easy to understand and the script’s flow has a declarative, rule-based feel to it. The problem is, projects and their build files rarely stay simple for long. Soon we need to add “validate.xml” target, junit reports, deployment to your application server and so on. Then we begin supporting multiple environments; we discover that our local desktop configuration is different from how integration environment is configured so we add numerous property files, “ftp” tasks and multiple “copy” targets for various application files. Before we know it, the build script becomes a convoluted mess of XML tags and there is nothing declarative about it anymore; it’s morphed into a full-fledged, very procedural program. Perhaps we even had to resort to using ant-contrib “if” and “for” targets to implement procedural logic in Ant. And nothing is uglier than an “if” with complex conditions expressed in XML.
A better approach would be to implement “procedural” portion of the build script in Java or any of the scripting languages that Ant supports. The problem is, configuring and invoking Ant tasks from Java or a scripting language leads to verbose code. For example:
execTask = project.createTask("exec")
execTask.setOutputproperty(outputPropertyName)
execTask.setErrorProperty(errorPropertyName)
execTask.setResultProperty(resultPropertyName)
execTask.setExecutable(execName)
arg=execTask.createArg()
arg.setLine(paramString)
Doing the same thing in XML is shorter and cleaner:
<exec executable="${execName}" outputPropert="p1"
errorProperty="p2" resultProperty="p3">
<arg line="${params}" />
</exec>
So what can we do to make task invocation syntax more concise and easier to understand? In fact, the syntax could be drastically simplified with the help of simple Ant “adapters” that can be developed for popular scripting languages since Groovy, Ruby and python all have fairly intuitive syntax for supporting lists, dictionaries and other data structures. I developed such an adapter for jython. It uses python named arguments and dictionary syntax, so executing a “copy” task looks like this:
pant=PAnt(project)
pant.exTask("copy", todir="testdir", fileset={"dir":".","includes":"*.xml"} )
“PAnt” is the name of the “ant adapter” class for Jython. The class configures and executes Ant tasks based on the provided arguments using Ant task configuration API.
“pant” module also comes with a simple helper function called “nested” so that named arguments can be consistently used for nested elements. With syntax highlighting supported by most editors/IDEs (e.g., you can try PyDev for jython/python development), it allows for better visual distinctions between attribute names and values:
pant.exTask("copy", todir="testdir", fileset=nested(dir=".", includes="*.xml") )
To use “PAnt” from Ant, you can develop custom tasks using “scriptdef” or simply embed python code directly into a target:
<target name="test.pant" >
<script language="jython">
from pant import *
pant=PAnt(project)
pant.execTask(name="echo", message="foo")
</script>
</target>
The “pant” module itself is just a few lines of code as you can see from its code. Don’t forget to properly setup your “python.path” if you want to make it globally available to all your Ant scripts.
There is also an open-source project Gant that provides similar (in fact, much more extensive) capabilities for Groovy, but I have not had a chance to play with it; I specifically wanted to use python/jython because jython can also be used for WebSphere Application Server administration.
In my mind scripting language-based approach for writing build files provides for much more flexible and easier to understand and maintain scripts. When you start implementing your Ant logic in python, you’ll see that Ant targets become much more coarse-grained, since there is no need to create internal targets (the ones that are never invoked by the users) to simulate subroutines or conditional targets to simulate “if” statements . It is also nice to be able to use all the capabilities of a full-blown programming language as oppose a limited subset of procedural tasks that Ant provides (such as “condition”). Being able to user properly scoped variables instead of inherently global Ant properties is another great benefit.
At the same time, it is still possible to use Ant targets for expressing a flow wiring together major functions of the build script. I would prefer something less XML-like for this purpose too, but that’s a task for another day.
Please refer to our official PAnt project page for more information and to download PAnt
Found your blog while I was doing some research on a conference I just got back from. While I was here, I’ve read through some of your recent posts.
Have you taken a look at the new series of SOA events offered by ZapThink this year? I just got back from an event and really got a lot out of it. I thought it was worth passing on the link, if nothing else, just so you can take a look at what’s coming up.
In the hopes that your blog allows posts to contain Web addresses, here’s the link: http://www.zapthink.com/eventreg.html. And pass it on to any colleagues you think may be interested. Given that it’s still a relatively unknown event, I’m spreading the news now about it because I am hoping they can get more people there!
ZapThink’s offered me and any of my friends a $50 discount if you register with the code GAFPSOA1.
Hope you can make it, because it was definitely worth my time!
[Biased opinion warning] I use the AntXtras (antxtras.sf.net) project to get flow control and other goodies in Ant scripts without the ugliness of ant-contrib. This library combined with macrodefs, presetdefs, and antlibs (three vastly under utilized features of Ant) dispense with most of the ugliness, awkwardness, and general maintenance nightmares found in many (most?) Ant scripts I’ve encountered. A lack of understanding of anything beyond buildscript-101 seems to be the underlying cause of most issues (I see the same problem reflected in Maven POM files from hell…)
-The Wabbit
Thanks for the AntXtras pointer, I was not aware about this library. From briefly looking at the examples on the site, it looks like there is still some procedural logic (for, ifs) implemented in Ant using custom tasks. That’s what I’m trying to avoid with PAnt. BTW, you can invoke any custom task (including the ones created with macrodef or scriptdef) from PAnt, not just the ones that come with Ant, so PAnt can complement any Ant extension library.
Cheers,
Alexander.
Wow! – Cool idea. I’m running into an issue when I try to use Jython 2.5 with from pant import PAnt. The trace shows from org.apache.tools.ant can’t be found. Since apparently classpath can be an issue (Centos 5.2 with Ant 1.65 installed via Yum), could you mention what Ant jar files need to be made available?
Many thnx.
Doug
Doug,
Unfortunately, it won’t work with Ant 1.6.5. Ant API changed quite a bit in 1.7. I’d recommend installing Ant 1.7.1 manually since looks like it’s not supported by yum.
Regards,
Alexander.