In this chapter:

Book: The Judo Language 0.9

Chapter 20. Ant Scripting and Scripting Ant

By James Jianbo Huang


non-printer version
Synopsis: Ant is a popular software build tool based on XML and Java. The two interesting points about Ant and Judo are:

  1. Judo provides a custom Ant task, <judoscript>, that makes using Judo within Ant build script easy and convenient. Embedded Judo code can intimately interact with the Ant environment, and invoke another Ant target with the native system function antcall(), allowing Judo to be a flow control language in the Ant build script.
  2. Because Ant employs an extensible architecture, anyone can provide a custom Ant task. Many practical operations have been built as Ant tasks and this collection of Ant tasks comprise a special form of extremely useful software library. Judo can take advantage of this software resource via its Ant scripting support, i.e., the anttask::XMLLiteral syntax. The task itself is the same XML code you would use in an Ant build script.

 
Introduction to Ant

What is Ant?

Ant is an XML-based make tool, build in Java. It is a Java counterpart for the make utility on Unix and other platforms. It uses an XML document to specify rules, actions and targets. Each action is realized by a Java class that implements a particular Task interface.

Key advantages of Ant over traditional make tools include:

  1. Ant is built in Java and runs anywhere Java runs.
  2. Ant is XML-based, so the build files are more descriptive.

The flip side of these advantages are:

  1. Traditional make utilities use shell commands for tasks; this gives them the liberty to run any commands available in your operating system shell. Ant actions, however, must be implemented in a Java class. If you have a new native tool to be added to the build process, you would have to create a new Java class following the Ant protocol and define a set of XML syntax (such as attribute names and meanings of values, embedded elements, etc.)
  2. Because each task is arbitrarily defined by a developer, that developer defines new rules on top of the original task rules (e.g., command-line parameter format); hence, there are as many new rules as there are tasks. Although the basic build rules such as dependencies and targets are easy to memorize, mastering all the task-specific rules is virtually impossible. Some may argue that the task XML rules are more readable and user-friendly; other may wonder, "I need to know the original tool anyway, why a new set of rules on top of that?"
  3. Because of XML, it is verbose. However, since build files are created once and sparsely modified, this is not a huge concern.

Like it or not, Ant's popularity has soared with that of Java. Today, most Java projects are built with Ant; so do many non-Java or even non-software projects. Ant has become the de facto build tool and an important part of Java application development.

Let's first understand what Ant build scripts are and how they work, and clarify the use of terminologies.


Ant Build Scripts

An Ant build script is an XML document with a root node called <project>, which contains one or more <target> tags. Targets can depend on other targets, and may contain its own tasks as <task> tags. It is the tasks that performs various actions of the build process.

The Ant package has defined a number of core tasks and a number of optional tasks. Developers can also define new tasks for any purposes. In the core tasks, just a handful are for the build process control, such as <ant>, <antcall> and <taskdef>. Let's call them as process control tasks. All other tasks perform specific practical actions, and let's call them action tasks.

Today, the core and optional tasks alone include well over 100 Ant action tasks defined, documented and shipped in the Ant package. Many third parties have supplied support for their own action tasks. Some implementations of the action tasks are in pure Java; many others are simply Java wrapper classes that run native commands, such as tasks for Rational ClearCase, Visual Source Safe (VSS), etc. This creates a uniquely interesting phenomenon, that a Java-based build tool has actually amassed a huge repository of practical utilities! In software engineering parlance, Ant tasks comprise a special type of software library that is uniquely useful.

In Judo, the anttask:: operator allows you to take advantage of any Ant action task in your scripts. This is detailed later in this chapter.

Ant has a <script> tag to embedded scripting languages as a flexible task. The <script> supports any scripting languages, including Judo, that conform to the Bean Scripting Framework (BSF) API. Judo provides a simple <judoscript>, making Judo code scripting in Ant easier. What are the benefits of scripting in Judo rather than using Ant tasks? You can do a lot of work with minimal amount of code, and sometimes Judo scripts carry out many sophisticated tasks. For instance, you can:

  • use JDBC scripting to set up, test and clean up your data-driven applications,
  • transform file contents during various build steps,
  • do complicated archiving and file processing,
  • launch Judo scripts to automate document creation,
  • etc., etc.

The <judoscript> is discussed in detail next. Before we go into introducing Judo support for Ant, let's take a look at a sample Ant build script and see how it works, and what matters between the Ant environment and Judo's Ant support.

TODO: provide the example and explanation.

Judo and Ant

Ant task scripting in Judo
Ant task scripting is to take advantage of this sector of software resources for practical uses. Different languages and tools can have different ways to do this. Judo provides a native, intuitive way to script Ant tasks. It simply uses the XML format to run an Ant task in your script. The benefit is, all Ant tasks are documented with examples in XML; you can simply cut-and-paste the examples into a Judo program for use and modify. There is no extra step in between to convert the XML rules to another syntax, hence no chance for ambiguity or mistakes.



 
Embed Judo in Ant Build Script

Judo code and programs can be used in Ant build scripts in two ways:

  1. through Ants support for the Bean Scripting Framework (BSF) mechanism; this will be discussed as a case study for BSF Support for Judo. Or
  2. through the <judoscript> ant task. This is what we discuss here.

The Ant <judoscript> Task

Judo provides an Ant task implementation to run a Judo program, either as embedded code or in an external script file. The Ant task class is com.judoscript.AntJudoScriptTask, and is mapped to the task tag name <judoscript>:

<taskdef name="judoscript" classname="com.judoscript.AntJudoScriptTask"/>

This Ant task is very simple; it can take a src attribute as the external script file and a params attribute for the program attributes. The src attribute is optional; if not specified, the embedded text, which is likely to be quoted in <![CDATA[ and ]]>, is expected as program code. In either case, the params can be used to specify command-line parameters. The following Ant build file demonstrates both usages:

Listing 20.1 snoop.xml
<?xml version="1.0"?>

<project name="test" default="target1" basedir="."
         description="Illustrates both ways of using the <judoscript> tag.">

  <taskdef name="judoscript" classname="com.judoscript.AntJudoScriptTask"/>

  <target name="target1" description="Run Judo code specified herein.">
    <judoscript params="abc -x:1 def ghi -y:something">
      // This is the code of snoop.judo copied verbatim.

      println '#cmd_args: ', #cmd_args;
      for x in #cmd_args {
        println '  #cmd_args[', loopIndex(), '] = ', x;
      }

      println nl, '#args: ', #args;
      for x in #args {
        println '  #args[', loopIndex(), '] = ', x;
      }

      println nl, '#options: ', #options;
      for x in #options {
        println '  #options[', x, '] = ', #options.(x);
      }

      println nl, 'Original os.name = ${os.name}';
      #sysprops.('os.name') = 'XXXXXXXXXXXXXX';
      println     'Now,     os.name = ${os.name}';
    </judoscript>
  </target>

  <target name="target2" description="Run a Judo script (in the same JVM).">
    <judoscript src="snoop.judo" params="abc -x:1 def ghi -y:something" />
  </target>

</project>

The first <judoscript> tag runs a segment of code embedded in the tag itself, and passes some command-line parameters. The code simply prints out values of different kinds. The second <judoscript> tag runs an external program, build.judo, whose content is exactly the same as the embedded code in the first tag.


Passing Information between Ant and the <judoscript> Tag

Although the format of the <judoscript> tag is simple, this tag is different from other Ant action tasks by nature. Most other Ant tasks perform a specific job, whereas <judoscript> contains code of a general scripting language. One of the most important aspect of embedding code is, how to pass values between the Ant build environment and the embedded code.

The <judoscript> tag can, like any Ant tags, use the ${name} in attribute values. Note, however, in the embedded Judo code, the ${name} notation is for accessing Judo variables, not as expansion of Ant property values as you might assume. Programmatically, the code used in <judoscript>, whether is directly embedded or in an external file, exchanges information through the system properties. The following example demonstrates this:

Listing 20.2 pass_value.xml
<?xml version="1.0"?>

<project name="test" default="main" basedir=".">
  <taskdef name="judoscript" classname="com.judoscript.AntJudoScriptTask"/>

  <target name="main" depends="step1,step2,step3" />

  <target name="step1">
    <property name="user.prop" value="Ant is everywhere." />
    <echo message=" ${user.prop}" />
  </target>

  <target name="step2">
    <judoscript>
      println ${user.prop}; // returns the system property value

      #sysprops.('user.prop') = 'Judo is fun!';
    </judoscript>
  </target>

  <target name="step3">
    <echo message=" ${user.prop}" />
  </target>

</project>

The test build script runs three steps:

  1. Set a property in the project.
  2. The embedded Judo code prints out the current property, then set that property to a different value.
  3. Back in the project, the last task prints out the property value again.

The output is:

Buildfile: pass_value.xml

step1:
     [echo]  Ant is everywhere.

step2:
[judoscript] Ant is everywhere.

step3:
     [echo]  Judo is fun!

main:

BUILD SUCCESSFUL

Note that the same ${user.prop} expression occurred twice in this build script: once in step 1 as embedded in the message attribute of <echo>, and once in step 2 in the Judo code. Although syntactically they appear to be the same and so are the results, the meanings are totally different. In step 1, ${user.prop} is expanded by Ant with its system property value; in step 2, ${user.prop} is a Judo expression that returns that system property value within the Judo language engine.

Accessing environment variables
You can not set any environment variables from Judo, Ant or Java itself; hence, regardless of how you access environment variables in Ant build scripts, the embedded Judo code, like Judo code in a stand-alone program, still accesses environment variables as global variables. The following build script prints out the environment variable TEMP from Ant and embedded Judo:

Listing 20.3 show_env.xml
<?xml version="1.0"?>

<project name="test" default="main" basedir=".">
  <taskdef name="judoscript" classname="com.judoscript.AntJudoScriptTask"/>

  <target name="main" depends="step1,step2" />

  <target name="step1">
    <property environment="env" />
    <echo message=" ${env.TEMP}" />
  </target>

  <target name="step2">
    <judoscript>
      println ${TEMP};
    </judoscript>
  </target>

</project>

Call Other Ant Targets and Access Other Resources

Judo provides a special-purpose system function, antcall(), to call Ant tasks in the same build script from the embedded Judo code. The following example demonstrates this:

Listing 20.4 antcall.xml
<?xml version="1.0"?>

<project name="test" default="main" basedir=".">
  <taskdef name="judoscript" classname="com.judoscript.AntJudoScriptTask"/>

  <target name="t1" depends="t1.1">
    <echo message="Target One." />
  </target>

  <target name="t1.1">
    <echo message="Target One'." />
  </target>

  <target name="t2">
    <echo message="Target Two." />
  </target>

  <target name="main">
    <judoscript>
      println '-------------';
      antcall 't1', 't2';
      println '-------------';
    </judoscript>
  </target>

</project>

The output is:

Buildfile: callant.xml

main:
[judoscript] -------------

t1.1:
     [echo] Target One'.

t1:
     [echo] Target One.

t2:
     [echo] Target Two.
-------------

BUILD SUCCESSFUL

The antcall() function can take variable number of parameters, which are Ant target names. If no parameters are provided, it will have no effect. This function effectively makes Judo a flow-control language for Ant build scripts.

Accessing other Ant services
Other than system properties, there is a predefined variable, $$anttask, available to both the embedded Judo code or the external program. That variable holds the Ant task object. It is the actual Ant task, which is an instance of com.judoscript.AntJudoScriptTask; this class extends org.apache.tools.ant.Task, so that you can call any of its methods, such as the getProject(), which in turn provides access to most services within Ant. Please refer to Ant's documentation for its Java APIs.



 
Script Ant Tasks

As we discussed earlier, Ant tasks comprise a unique type of library of extremely practical utilities. This is a great resource for scripting language users. Judo provides an easy and intuitive way to take advantage of this. The syntax is very simple:

AntTaskStatement ::= anttask::XMLLiteral

The XMLLiteral specifies the Ant task as documented in XML format. These tasks should all be action tasks, and the following tags are prohibited:

  • <ant>
  • <anttask>
  • <fail>
  • <target>

The following example uses the <echo> Ant task twice:

Listing 20.5 ant_echo.judo
a = 'ants';

anttask::<echo message="Hello, ${a}!"/>

anttask::<echo>
  Hello, more ${a}!
</echo>

In this example, ${a} is used in both the attribute value and in the embedded tag text. This is the way to pass values to an Ant task. Such expression can't be used to create tag names or tag attribute names. The output of this program is:

     [echo] Hello, ants!
     [echo]
     [echo]   Hello, more ants!

Ant tasks used in Judo should be individual action tasks, and they are not expected to contain other subtasks. However, they can contain embedded elements, as demonstrated in this example:

Listing 20.6 ant_exec.judo
src = '.';

anttask::
  <exec dir="${src}" executable="cmd.exe" os="Windows 2000" output="dir.txt">
    <arg line="/c dir"/>
  </exec>

As you would expect, Ant tasks have to "live" in an Ant project; Judo maintains a single project for all the Ant tasks invoked.

Passing Information between code and Ant tasks

Different Ant tasks can define different ways to communicate their results back to the Ant environment (i.e., the Ant project;) the most common way is through setting system properties, such as Ant core tasks <basename>, <condition>, <uptodate>, etc. Other may choose to use more ad hoc means; for instance, the core Ant task <checksum> returns the checksum of a file in a text file. Let's see an example that uses system properties to pass values:

Listing 20.7 ant_basename.judo
filename = '/usr/bin/judo';

anttask::<basename property="base.name" file="${filename}"/>

println 'Base name: ', ${base.name};

The output is:

Base name: judo

Handle Ant task execution failures

Ant tasks may fail by throwing an instanceof org.apache.tools.ant.BuildException, which is a wrapper of another exception. Judo captures this BuildException, retrieves and throws the original one. You can capture this exception:

Listing 20.8 ant_excpt.judo
anttask::<exec executable="do_not_exist" />

catch:
  println <err> '--- Ant task deliberately failed:';
  $_.printStackTrace();
  println <err> '--- Stack trace printed.';



When and why to script Ant tasks in Judo

As we have discussed earlier, Ant tasks comprise a special type of software library of many practical utilities. Ant tasks are designed for use by the Ant build system, but can also be utilized by Java and Java scripting languages. Judo provides an easy language facade for scripting Ant tasks. This turns Ant tasks into truly practical utilities.

Judo is a rich functional scripting language; it embeds a lot of useful, practical utilities right in the language itself. Many Ant core action tasks have their counterpart in Judo, where the Judo counterparts are generally easier to use, more flexible and intuitive. Such tasks include those that deal with file systems, file archives, RDBMSs, native execution, e-mail, and any data and file processing.

Hence, Ant tasks that do not have Judo counterparts are the best to be used. These include configuration management system access (e.g., CVS, ClearCase, Microsoft Visual Source Safe, etc.) Tasks such as FTP, SCP, etc. can also be good candidate, as these usages are more challenging and system-dependent; sharing the same code (Ant scriptlet) between Judo code and Ant build scripts may be desirable. There are also many other tasks for performing compilation, javadoc generation, etc., that can be useful at times.

Ant allows developers to develop new tasks. Now that you can script Ant tasks in Judo, this can also be a great way to extend Judo language. You can register a custom task just like you would in an Ant build script, assuming your new task is not shipped with the Ant software package. The following is an example that registers a new task and uses it:

Listing 20.9 antburger.judo
anttask::<taskdef name="js" classname="com.judoscript.AntJudoScriptTask"/>

anttask::<js>
  println 'This is Judo-in-Ant-in-Judo, a.k.a. AntBurger!',
          nl, nl, 'AntBurger is delicious!';
</js>

This program registers a <js> to embed Judo code, then uses this <js> to invoke the embedded Judo code. The output is;

This is Judo-in-Ant-in-Judo, a.k.a. AntBurger!

AntBurger is delicious!

If you have a custom Ant task, you can do something similar.



 
back to top
 


Copyright c 2001-2021 JudoScript.COM. All Rights Reserved.