Tuesday, May 17, 2011

Executing Ant Build File Targets from Groovy

In the post AntBuilder: Groovy Meets Ant, I talked about how AntBuilder combines the best of Ant with Groovy. The Groovy Almanac post Using Ant build.xml and Antbuilder demonstrates how easy it is to use Ant's ProjectHelper and Project classes to execute Ant targets from Groovy code. In this, post I adapt and slightly expand that example to show how Groovy can be used to run any target in a specified Ant build file.

applyBuildFileInGroovy.groovy
#!/usr/bin/env groovy
/**
 * applyBuildFileInGroovy.groovy
 *
 * This is an example of executing an Ant build file from Groovy code using
 * Ant's Project and ProjectHelper classes.
 *
 * Usage: applyBuildFileInGroovy.groovy _buildFilePathName_ [target1] [target2] ... [targetn]
 *
 * where _buildFilePathName_ is the path and file name of the build file to be
 * used by this script and zero or more targets in that build file can be
 * specified (default target used if no targets specified).
 */

import org.apache.tools.ant.Project
import org.apache.tools.ant.ProjectHelper

if (args.length < 1)
{
   println "You must provide an Ant build file as the first parameter."
   System.exit(-1)
}

def antBuildFilePathAndName = args[0]
def antFile = new File(antBuildFilePathAndName)
def project = new Project()
project.init()
ProjectHelper.projectHelper.parse(project, antFile)
if (args.length > 1)
{
   def antTargets = args - antBuildFilePathAndName
   antTargets.each
   {
      project.executeTarget(it)
   }
}
else
{
   // use default target because no targets were specified on the command line
   project.executeTarget(project.defaultTarget);
}

The simple Groovy script shown above will run the default target of the provided Ant build file if no other target is specified. If any arguments beyond the build file are provided to the script, they are treated as target names and processed.

In general, it might be just as easy to use Ant directly against the specified build file. However, the benefit of calling these targets from Groovy is that Groovy scripts and other code have access to pre-existing targets. In my post AntBuilder: Groovy Meets Ant, I focused on leveraging AntBuilder's DSL capabilities to build Ant calls in the code. The approach shown in this post uses targets already set up in an external Ant build file. This allows for easy reuse of custom targets from Groovy code without necessarily having to execute an Ant build. A side benefit of this is consistency between the direct Ant build and the Groovy code using the same targets.

Groovy's handy AntBuilder class is not used in the simple script above. In fact, although there is some Groovy syntax in the example, the main classes used (org.apache.tools.ant.Project and org.apache.tools.ant.ProjectHelper) are Java classes that could be used in any Java application or code. Groovy is a particularly nice fit, though, because of its concise, script-friendly syntax.

It is always possible to call Ant from Groovy code just as one would call any command from the operating system command line, but the tighter integration of using Project and ProjectHelper directly from Groovy code is preferable for several reasons. An example of this is the Project.getTargets() method. This method can be used in calling Groovy code to determine which targets are available. This list could be used to generate an interactive script that listed available targets and executed the selected one rather than passing them in on the command-line.

Conclusion

When pre-existing Ant build files contain build logic that is needed in a Groovy script, Ant's class for representing a project (Project) and Ant's class for configuring a project (ProjectHelper) are easy to use. When combined with Groovy, these classes enable concise scripts for calling Ant targets programmatically.

4 comments:

Chompie said...

Brilliant, really helpful. It took me a while to find this info,its not obvious on how to do it looking at the Groovy docs

Dustin said...

Chompie,

I'm glad that the post was helpful. Thanks for taking the time to let me know that it was helpful.

Dustin

CRC said...

Hello, very good example. A question : I've used your script and it seems to work but I could't see any output at the console (I'm calling a echo task in build.xml), why?

@DustinMarx said...

CRC,

Thanks for the great question. You can use the "set" methods on the org.apache.tools.ant.Project class to set the output and error print streams as described in my new post (inspired by your question) Listening and Logging Ant Output in Groovy and in the article Invoking Apache Ant programmatically.

Dustin