Level: Introductory Jim Creasman (creasman@us.ibm.com), Advisory Programmer, IBM
09 Sep 2003 Ant is a powerful tool for scripting build processes. When combined with XSLT, Ant's power and flexibility increase dramatically. Here, Jim explains and illustrates this concept using real world examples from his previous experience.
I have led a build team for the past several years, and
during this time I have seen a fair number of
changes in how software goes from concept to code to customer.
In particular, the tools and techniques used to manage
the build activity of large software development projects have seen a lot of change.
In the mid-1990s, most of the source code was C or C++. make was the tool of choice for
scripting and managing code compilation. Sprinkle in a dash of batch files or shell scripts to
add automation and you had a build process.
Times change. Enter Java technology, XML, XSLT, extreme programming with continuous build,
and a host of
other new technologies and ideas. By the late '90s the playing field was looking a lot different.
Perhaps the single biggest addition to the set of build tools was Ant.
Ant is a Java-based build tool from the Jakarta project at the Apache Software Foundation. It has become a de facto standard for building Java projects.
Ant scripts derive their structure, along with much of their ease of use, from XML.
Also, because Ant scripts are XML, they can be parsed, modified, generated, or otherwise transformed
programatically using eXtensible Stylesheet Language Transformations (XSLT). You can even invoke the stylesheet
processor from an Ant task (See the Ant style task description in Resources).
It takes two to tango!
Consider the team of Ant and XSLT. The remainder of this article focuses on three examples, each
illustrating some problem I have encountered in my work. In each case, I demonstrate a solution
based on combining a base Ant script with one or more XSL stylesheets. XSLT adds flexibility to
what would otherwise be a static script. Watch the Ants dance as you experience:
- Easier maintenance of scripts
- Complex scripts built up from XML metadata
- Scripts that provide their own browser
The value lies in the method. You may not find any of these specific examples applicable, but I
am confident you will find ways to use this simple technique to liven up your build process.
Learning by example
Before getting started, I want to make sure you understand the bigger picture. Each of these solutions
requires making an XSL transformation happen. Figure 1 describes the general way in which XSLT works.
In the context of this article the source document is usually an Ant script,
but sometimes it is another XML data file. The transformation process applies the rules found in the
stylesheet to the source, producing the result document.
Figure 1. Overview of XSLT process
Example 1: Building with style
I mentioned working on a build team for a number of years. My group contracts with various
development organizations to provide their build support. We have simultaneously supported as many as
17 different products, providing builds for up to 50 releases on an active basis.
 |
Build script outline (Ant targets)
This outline represents the general set of steps each of our build processes follow.
A default action is implemented
for each step, which most processes then use by default.
- init
- start
- setup
-
step.setup_scm
-
step.setup_base
-
step.setup_delta
-
step.setup_build
-
step.setup_nls
-
step.setup_verify
- build
-
step.build_release
-
step.build_verify
- distribute
-
step.distribute_connect
-
step.distribute_copy
-
step.distribute_nls
-
step.distribute_verify
- complete
-
step.complete_scm
-
step.complete_stats
-
step.complete_distribute
-
step.complete_verify
|
|
One of our challenges is managing this much activity and diversity. Often the development groups have
little in common and no interaction with one another. Standardization is the key. At an abstract level,
our build process for each release is the same:
- Define what you plan to build.
- Extract the source code to the build machine.
- Run the compilation and packaging sub-processes.
- Publish the build output.
- Notify everyone the build is ready.
Our approach has been to develop common scripts which, if not identical to one another,
do at least follow this outline.
As with so many plans, the devil is in the details. Over time, these scripts have a tendency
to wander off in different directions if they aren't closely shepherded. Each one becomes more unique.
Common maintenance requires a great deal of effort, time, and coordination.
The solution
Recently, we decided to re-engineer our scripts, consolidating on the use of Ant as the primary scripting
tool. We agreed upon a default build process. The generalized tasks listed in the preceding process were
refined into smaller steps, each representing a unit of work likely to be common across multiple build
processes. Each step of this refined process was implemented as one or more Ant targets, each with a
default behavior that was applicable to most cases.
The sidebar "Build script outline" shows the actual sequence of steps represented by their Ant
target names.
The outline is applicable to each build process, but the default behavior may not be right
for a particular step. This is where XSLT adds the required flexibility.
Without the ability to transform the default script, we would still have to maintain many copies
of nearly identical scripts. Instead, each build process implements a local script containing
only the steps that are unique to that process. A front-end process (also an Ant script) runs the stylesheet
to transform the input documents into the Ant build file. Any steps not overridden in the local
script are defined by the default set.
Think of the process as an equation:
local targets + default targets + style sheet = build script
The local targets are contained in the primary XML file input to the transformation. It reads the
default targets from a secondary input document and merges the results, giving preference to the
local script during the merge process.
The Ant <style> task that runs this transformation is shown in Listing 1.
Listing 1. Ant style task
<style in="${local_targets}"
out="${build_script}"
style="${style_sheet}"
force="yes">
<param name="defaults" expression="${default_targets}"/>
</style> |
The default "targets" document is not a true Ant script. It does not contain a <project>
tag and uses <default> tags to group the Ant targets for each default. This allows a
group of targets to define a single step (see Listing 2).
Listing 2. Build defaults XML sample
<defaults>
<default name="init">
<!--
=========================================================================
Target: init
Purpose: Common initialization target.
=========================================================================
-->
<target name="init">
<!-- This is where my default initialization tasks would be found ... -->
</target>
</default>
<!-- Additional defaults go here ... -->
</defaults>
|
Common enhancements are applied to the default targets or as
modifications to the stylesheet. Since all
processes reference a single instance of these, changes are applied automatically to all scripts
at execution. Maintenance remains a concern if the local script has overridden the enhancement of
a common step. However, by carefully managing the design, we have defined the steps such that
those likely to be overridden do not contain any actions for their default behavior.
Example 2: Using XSL to view Ant scripts
One of the problems I have encountered frequently is attempting to understand and modify a script
someone else has written. Build scripts are often kludged together for that build by people who would rather be
writing source code than developing a process. Such scripts are notorious for
poor or misleading documentation.
While good documentation has no substitute, you can offset its absence by having the ability
to navigate the process in an abstract fashion. You can succeed if you have a point of
reference and a mental framework to guide your investigation. With good browsers, you can see a conceptual view and move around freely without loosing your context. You can see both the forest
and the trees.
A large Ant script can be daunting the first time you see it. Even if you are familiar with the script,
determining where to make a modification can be time consuming. All XML documents, including Ant
scripts, have a high degree of structure. With XSLT you can make use of this feature to transform your
Ant script into an HTML document that is easier to read and comprehend. You can use XSL to filter
out much of the noise and reveal only the critical elements.
If you are using a browser that has support for XSL stylesheets (such as Microsoft Internet Explorer,
version 6), you can even make this a permanent part of your Ant script. No mess, no fuss -- just browse the
Ant script directly and watch the results! Ant will just ignore the extra processing instruction when
executing, so you have no need to generate a separate HTML file first.
Seeing is believing
The first step is to develop a stylesheet that handles the transformation to HTML. This can be as simple
or as elaborate as you want to make it. For better or worse, here is my
stylesheet. It satisfies my basic requirements
when viewing an Ant script:
- Provide a table of contents to help users quickly find a section in the script
- Group and alphabetize all properties and targets used
- Provide navigational links between targets and their dependencies
- Show the tasks that make up a given target
Those skilled in HTML or JavaScript will no doubt have more creative ways to format and display
the data. (Please send me your results.) The basic concept is still the same.
Once you have the stylesheet, park it somewhere that's accessible to the browser. This could be on
an HTTP server or a shared drive for convenience. The final step is to add an
instruction to the Ant script. This tells the browser to use your stylesheet when displaying the
document. Otherwise, you will just see a more colorful version of the raw XML. Blink and you might miss
this step. Are you ready? Add the line highlighted in Listing 3 to the top of your Ant script,
substituting the location of your stylesheet for the value of the href attribute.
Listing 3. Stylesheet processing instruction
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl"
href="./examples/example2/ant2html.xsl"?>
<project name="Example2" default="main" basedir=".">
<!-- The rest of the Ant script goes here. -- >
</project> |
That is all there is to it. If the browser you are currently using is enabled to handle XSL stylesheets,
you can compare the results by clicking on the following links:
As with the previous example, since the stylesheet is separate from the data (that is the Ant script), any
enhancements you make to the stylesheet require no additional changes to the script. They are instantly
available to all users as soon as the changes are deployed.
If you were able to view the formatted Ant script, you might be wondering why I did not use frames to
make the navigation easier. Generating multiple frames from within a single stylesheet is not as easy as
it might appear, and requires the use of a script language (such as JavaScript). It can, however, be done.
I include a second instance of the same Ant script,
this time formatted with a stylesheet which generates
frames. It requires Microsoft Internet Explorer as the browser since it makes use of JavaScript,
MSXML, and
ActiveX controls. Chapter 14 in Jeni Tennison's book (see Resources) was particularly useful in making
this example work.
Example 3: Using XSL to extend Ant
As with any instruction set, the Ant syntax is unable to express certain concepts.
While Ant handles dependencies, references, and hierarchical
processing very nicely, it lacks any support for basic looping.
The native set of tags offers no way to repetitively execute a set of tasks.
The problem
Consider a simple problem. The release you are building ships with support for eight languages: German,
English, French, Italian, Japanese, Korean, Portuguese, and Spanish.
The language-specific properties
files are returned in their native code page, requiring that the Ant
native2ascii task (see Resources) be run
against each language-specific properties file. Sounds like a great setup for a loop, right?
Consider three solutions to this problem.
Solution 1: The brute force approach
The brute force approach is to just code the tasks
repeatedly the number of times they they are required. This gets the job done and is fine for a small
number of iterations. For larger loops with many iterations, maintenance becomes a factor. Each block
of repeated code increases the chances of introducing an error into the logic. In this example, any change
(such as adding a new properties file) is magnified by a factor of eight.
Listing 4 shows this
solution. Note the sections in bold type that highlight the slight difference in each set of lines.
Listing 4. The brute force approach
<target name="convert_native_encodings">
<native2ascii encoding="Cp850" src="${basedir}/src" dest="${basedir}/export">
<include name="**/*_de.properties/>
</native2ascii>
<native2ascii encoding="Cp850" src="${basedir}/src" dest="${basedir}/export">
<include name="**/*_en.properties/>
</native2ascii>
<native2ascii encoding="Cp850" src="${basedir}/src" dest="${basedir}/export">
<include name="**/*_es.properties/>
</native2ascii>
<native2ascii encoding="Cp850" src="${basedir}/src" dest="${basedir}/export">
<include name="**/*_fr.properties/>
</native2ascii>
<native2ascii encoding="Cp850" src="${basedir}/src" dest="${basedir}/export">
<include name="**/*_it.properties/>
</native2ascii>
<native2ascii encoding="SJIS" src="${basedir}/src" dest="${basedir}/export">
<include name="**/*_ja.properties/>
</native2ascii>
<native2ascii encoding="KSC5601" src="${basedir}/src"
dest="${basedir}/export">
<include name="**/*_ko.properties/>
</native2ascii>
<native2ascii encoding="Cp850" src="${basedir}/src"
dest="${basedir}/export">
<include name="**/*_pt_BR.properties/>
</native2ascii>
</target> |
Solution 2: The <for> tag
A better solution is to give Ant a <for> tag. A properly designed extension
enhances the language. Listing 5 shows how this solution might look.
However, it is not without drawbacks. First, it requires a deeper understanding
of how Ant works in order to implement the new tag.
Second, and of greater concern, is again maintenance.
The classes that support the new tag must be available to Ant at runtime. This requires a separate .jar
file, or a repackaging of Ant. Every system that runs the script needs access to the new tag.
Listing 5. A more elegant solution
<!-- Iterate over each language code -->
<target name="convert_native_encodings">
<loadproperties srcFile="encodings.properties"/>
<for token="%lang%"
iterate-over="de, en, es, fr, it, ja, ko, pt_BR">
<native2ascii encoding="${encoding.%lang%}" src="${basedir}/src"
dest="${basedir}/export">
<include name="**/*_%lang%.properties"/>
</native2ascii>
</for>
</target> |
The encodings.properties file
is used to create a mapping of the language code to the encoding mnemonic:
Listing 6. Encoding mappings
# File: encodings.properties
encoding.de=Cp850
encoding.en=Cp850
encoding.es=Cp850
encoding.fr=Cp850
encoding.it=Cp850
encoding.ja=SJIS
encoding.ko=KSC5601
encoding.pt_BR=Cp850 |
Solution 3: Using XSLT
The final solution extends the Ant language, as in the second approach, without creating
a maintenance problem. Instead of coding the for loop extension as a new Java class within Ant, I use
XSLT to accomplish the same goal. The result is a script that runs in vanilla Ant.
The stylesheet simply expands the <for> tag into
a sequence of tasks, similar to the first approach.
The stylesheet for this transformation is straightforward. The default template outputs the XML
exactly as it is written -- no changes required. Only when a <for> tag is
encountered is anything different output. You can view the complete stylesheet for this transformation.
It is worth noting that the original input is not a valid Ant script; the output from the stylesheet is.
When distributing the build output, only the final script is published. This is a perfectly legal Ant
script that can be run by the larger development community without requiring access to the stylesheet.
Conclusion: Everything old is new again
I hope you find this information useful, either directly or as a spark for ideas of your own.
In many ways, I am simply restating an old idea. The use of templates to preprocess source code prior to
compilation has been around since the earliest days of assembler programming. It remains a powerful
technique even today.
More applications of this technique are yet to be explored and developed. I'll leave you with a couple of
final suggestions -- just to keep those Ants dancing. Enjoy!
-
Consider a stylesheet that takes other XML data files and produces Ant scripts. For example, much
of the Java code that's written gets packaged and deployed using a plug-in architecture. This requires a
plugin.xml file as part of the package. Assembling a plug-in uses basic Ant tasks
(<javac>, <jar>, and more). Why not use XSLT to transform the plugin.xml description into an Ant script that builds the plug-in?
-
Suppose you want real-time status as the build process is running -- perhaps to update a Web page in real
time. One way to implement this is to code a logger for Ant that performs the update at the beginning
or end of each target. Once again, this requires an in-depth understanding of Ant and some Java skills.
A simpler solution uses XSLT to insert a probe task at the beginning or end of each target. The probes
act as trace points and use existing Ant tasks to capture status by writing data to a file.
Download | Name | Size | Download method |
|---|
| x-antxslsource.zip | | HTTP |
Resources
About the author  | |  | Jim Creasman is the team leader of a software build group at IBM in Research Triangle Park, NC (USA). He received a B.S. degree in Mathematics from Mars Hill College, and an M.S. degree in Applied Mathematics from North Carolina State University. He enjoys woodworking, outdoor activities, and keeping up with his three sons. He is currently working on a screenplay which he hopes to sell to a big Hollywood studio and retire. It is a semi-autobiographical story about a guy who is given an impossible job, befriends a hapless Ant, and finally goes native with the locals. He plans to call it "Dances with Ants" and hopes to cast Kevin Costner in the lead role. You can contact Jim at creasman@us.ibm.com. |
Rate this page
|