|
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.
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:
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.
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.
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
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.