Sweet Ant Tools (SAT) are a set of Ant tools under the Apache license. They are mostly dedicated to HTML page generation from XML files.
To install SAT, you must first install a Java Virtual Machine version 1.2 or 1.3. You can download one for free on Sun web site. The latest version of SAT is available on my personal web page.
To install SAT, unzip the archive in a directory where you want to install it. Then copy files in the lib directory into the lib directory of Ant.
To compile SAT, you must install Ant version 1.3 or 1.4. You can download it on Apache web site. You must also install jar files of Ant (ant.jar) and a JAXP XML parser (for instance the one you can download on Sun's web site) in the lib directory of SAT.
To generate the SAT jar file, type ant jar in the prj directory. Then put the generated jar file (sat.jar in the lib directory) in the lib directory of Ant. You will then be able to generate the documentation and zip archive by typing ant on the command line (SAT uses itself to generate it's documentation and the zip archive).
All those tasks select files using file and dir attributes and the fileset element. These attributes and element use the following syntax:
Files or directories with relative path are relative to the Ant script. Thus, the attribute file="foo" in the script /home/casa/build.xml selects the file /home/casa/foo.
It is possible to mix the file and dir attributes and fileset elements to select files. For instance:
<foo file="file" dir="dir"> <fileset dir="dir2" includes="*.xml"/> </foo>
Selects the file file, files in the directory dir and XML files in the dir2 directory.
SAT is a set of optional tasks for Ant, thus for each task there is a taskdef element (to tell Ant which class is associated to a given task) and an XML element.
This task boosts builds in a significant way (up to 100 times faster than traditional builds).
Its pinciple is quite simple: when the task server is launched, it opens a socket and listens to incoming commands without exiting (Ant is then suspended). You can then run builds by sending commands to the server, opening a socket and sending text messages. This server task executes those commands and sends back build messages.
The benefits of this way to run builds are the following:
The performance boost is significant for small builds (such as those you run 100 times a day to rebuild few classes or transform an XML file).
The declarative element (after the <project> one) is the following:
<taskdef name="server" classname="net.sweetohm.ant.server.ServerTask"/>
Attribute | Description | Required |
port | Le port the server listens to for build queries. | No (defaults to 5393). |
ips | The coma separated list of IPs that are granted the right to send command to the server. | No (defaults to the single IP 127.0.0.1 or localhost). |
This task doesn't accept any nested text or element.
Let's say that you want to start Ant running in server mode and listening on port 1054 for incoming commands from IPs 127.0.0.1 and 192.168.0.30, we can include the following target in our build file:
<target name="server"> <server port="1054" ips="127.0.0.1, 192.168.0.30"/> </target>
We would then launch Ant in server mode with the following command line:
ant server
The console will display the following message:
[server] server: [server] Server running and listening on port 1054
That indicates that the server is ready and is waiting for incoming requests on port 1054. If we send a request to run target html, the server will display the following message on the console:
[server] Request from: /127.0.0.1 [server] Processing command: target html [server] OK
Which tells that the server received a request from 127.0.0.1 containing the command target html and that the request was understood and successfuly processed.
The client (which is here a simple Unix socket command, which send the standard input to the socket and displays the result on the standard output) prints:
$ echo "target html" | socket localhost 1054 Building Project: doc valid: Validating file 'sat.doc.en.xml'... Validating file 'sat.doc.xml'... html: Transforming: /home/casa/doc/perso/java/sat/doc/xml/sat.doc.xml BUILD SUCCESSFUL Total time: 0.54 s
If we launch the build the classic way, we obtain the following message:
$ time ant html Buildfile: build.xml valid: [valid] Validating file 'sat.doc.en.xml'... [valid] Validating file 'sat.doc.xml'... html: [xtask] Transforming: /home/casa/doc/perso/java/sat/doc/xml/sat.doc.xml BUILD SUCCESSFUL Total time: 6 seconds real 0m7.433s user 0m5.460s sys 0m0.120s
You will note that those messages are identical except that the build is 13 time faster running in server mode.
The communication protocol between the client and the server is extremely simple. A message contains a command that may be followed with arguments. The commands are summarized in the following table:
Command | Arguments | Description |
target | target1 [target2...] | Run one or more targets of the build file. |
exit | - | Stops the Ant process, thus the server itself is stopped. |
help | - | Displays an help on the build file (same result as launching Ant with the -projecthelp option). |
For the moment, this server mode is quite experimental. It is possible to comunicate with the server using one of the following methods:
This task launch a Beanshell script. This script may be nested in the element or written in a file. It is also possible to give arguments to the script with the args attribute or a nested script.
The declarative element (after the <project> one) is the following:
<taskdef name="bsh" classname="net.sweetohm.ant.bsh.BshTask"/>
This task was successfully tested with Beanshell version 1.1a18 (which jar file is included in this package). It doesn't work with version 1.1a12 or older (the interface bsh.ConsoleInterface was different for those versions) and with version 1.2b1 (due to a bug).
Attribute | Description | Required |
file | Beanshell script(s) to execute | No |
dir | Directory(ies) with Beanshell scripts to execute | No |
args | Argument list (coma separated) to pass the the Beanshell script. You can access this argument array using this.interpreter.get("bsh.args"); | No |
reset | Indicates if the Beanshell interpreter should be reset before to launch the script. Can take the true or false values. | No (defaults to true) |
You may select scripts to launch using <fileset> nested elements. See Ant documentation for more details about this element.
A script may be nested within a <bsh> element.
A Beanshell script can access Ant properties using setAntProperty() and getAntProperty() commands. See below for a short sample script.
This package also provides Beanshell commands to access Ant properties (defined using property elements) from within a Beanshell script. Those commands have the following signature:
Another command resolves a file name according to the directory where the Ant script is running. In an Ant script, a file name is relative to the directory of the script (instead of the current directory). The signature of this method is the following:
This task defines a variable called antProject in the Beanshell interpreter. This variable contains a reference to the running Ant project.
To launch a script bsh.bsh in the test directory, you may write:
<bsh file="test/bsh.bsh"/>
You can also write the script in the <bsh> element:
<bsh> print("Hello World !"); </bsh>
Note that a script nested in a bsh element is parsed when the Ant script is executed. Thus it should not contain < nor & characters or it must be nested in a in a CDATA declaration as following:
<bsh> <![CDATA[ print("<date>"+new Date()+"</date>"); ]]> </bsh>
To pass a foo argument to the script (as if it was was passed on the command line), you may use an args attribute:
<bsh file="test/bsh.bsh" args="foo"/>
Now a more complex sample:
<bsh file="test/bsh.bsh" args="foo,bar"> nested="nested"; </bsh>
Where the script test/bsh.bsh is the following:
#!/usr/local/bin/bsh // display arguments on the command line args=this.interpreter.get("bsh.args");; if(args!=null) { for(int i=0;i<args.length;i++) print("Argument "+i+": "+args[i]); } // display argument in nested script print("Argument nested: "+nested);
Will produce the following output:
bsh: [bsh] Arguments: foo bar [bsh] Executing nested script... [bsh] Executing script 'test/bsh.bsh'... [bsh] Argument 0: foo [bsh] Argument 1: bar [bsh] Argument nested: nested
This trace shows the evaluation order of the beanshell code:
The following script displays the value of foo property and give a value to the bar property.
print(getAntProperty("foo"); setProperty("bar","Hello World!");
This task uses the XT XSLT processor from James Clark to transform an XML document. This processor is, according to many benchmarks, the fastest Java processor (but the author doesn't develop it anymore).
The task declaration (after the <project> element) is the following:
<taskdef name="xtask" classname="net.sweetohm.ant.xml.XTask"/>
Attribute | Description | Required |
file | File(s) to transform. | No |
dir | Directory(ies) of the files to transform. | No |
style | The stylesheet to use for transformation. | Yes |
tofile | The generated file (if there is only one). | No |
todir | Directory(ies) of the generated files. | No |
extension | The file extension of the generated files. | No (defaults to .html) |
force | Force the transformation even if the generated file already exists and is newer than the XML file and the stylesheet. May take true or false values. | No (defaults to false) |
You may select files to transform with a nested <fileset> element. See Ant documentation for more details about this element.
It is also possible to pass arguments to the XSLT processor using <arg> elements. For instance, to assign the value bar to the parameter foo, you should nest within the <xtask> element:
<arg name="foo" value="bar"/>
This element may not contain text.
To transform a file foo.xml to bar.html using the transfo.xsl stylesheet, you may write:
<xtask file="foo.xml" style="transfo.xsl" tofile="bar.html"/>
To assign the value bar to the parameter named foo, you will write the element:
<xtask file="foo.xml" style="transfo.xsl" tofile="bar.html"> <arg name="foo" value="bar"/> </xtask>
This task was previously distributed alone on my page (version 0.1). It is now part of the SAT package.
This task validates XML files. You may validate a file against its DTD or simply check the XML syntax (check that the document is well formed). You can choose to stop Ant compilation while an error is encountered, the error level or the maximum number of parsing errors to display for each file.
The declarative element (after the <project> one) is the following:
<taskdef name="valid" classname="net.sweetohm.ant.xml.ValidTask"/>
Attribute | Description | Required |
file | File(s) to validate. | No |
dir | Directory(ies) of the files to validate. | No |
dtd | Tells the parser if the file(s) to parse have a DTD (stated in the DOCTYPE) or if it should only check the XML syntax (if the value of this attribute is false). | No (defaults to true) |
failonerror | Tells the parser to stop Ant buildfile processing when it encounters an error (if the value of this attribute is true). | No (defaults to true) |
errorlevel | The error level to check (errors of that level or higher are displayed and may interrupt Ant processing). Possible values are warning, error and fatal. | No (defaults to fatal) |
maxerrors | This is the maximum number of errors to display for an XML file. This number must be greater than 0. | No (defaults to 100) |
The files to merge can be selected using a <fileset> element. See Ant documentation for more details about this element.
This element may not contain text.
To validate all XML files of the a directory, copy the following buildfile in that directory:
<?xml version="1.0" encoding="iso-8859-1"?> <project name="xml" default="valid" basedir="."> <taskdef name="valid" classname="net.sweetohm.ant.xml.ValidTask"/> <target name="valid"> <valid dtd="true" failonerror="true" errorlevel="fatal" maxerrors="100"> <fileset dir="." includes="*.xml"/> </valid> </target> </project>
Then launch Ant typing ant on the command line. Note that attributes have their default values. Adapt this file to meet your needs.
If you want to validate all Ant buildfile on your disk, launch in the root directory the following file:
<?xml version="1.0" encoding="iso-8859-1"?> <project name="xml" default="valid" basedir="."> <taskdef name="valid" classname="net.sweetohm.ant.xml.ValidTask"/> <target name="valid"> <valid dtd="false" failonerror="false" errorlevel="warning" maxerrors="1"> <fileset dir="." includes="**/build.xml"/> </valid> </target> </project>
The dtd attribute is false because buildfile do not have a DTD, you ask the parser not to stop when it encounters an error to scan the whole disk, you put the error level on the lowest value to detect any problem and you ask to display only one error per file in order to avoid too many error messages.
This element is for merging XML files into one. This file contains the root elements within a nesting one.
The task declaration (after the <project> element) is the following:
<taskdef name="merge" classname="net.sweetohm.ant.xml.MergeTask"/>
For instance, this element may merge the following XML files:
<?xml version="1.0"?> <root> <element1/> <element2/> <element3/> </root>
and:
<?xml version="1.0"?> <root> <element4/> <element5/> </root>
In a single file like this one:
<?xml version="1.0"?> <index> <root> <element1/> <element2/> <element3/> </root> <root> <element4/> <element5/> </root> </index>
This element is useful to generate composite documents. For instance, the index page of my site is generated using this task. A presentation text, news and links (that are small distinct files) are merged in a single XML file. This file is transformed into an HTML page using XSLT.
Attribute | Description | Required |
file | File(s) to merge. | No |
dir | Directory(ies) of the files to merge. | No |
tofile | File name of the resulting file. | Yes |
encoding | This is the encoding of the resulting file. For instance, the encoding for western European languages is iso-8859-1. This gives the following XML declaration element: <?xml version="1.0" encoding="iso-8859-1"?>. | No (defaults to ASCII encoding) |
doctype | This is the second part of the doctype declaration. For instance, a doctype such as article PUBLIC "-//CASA//DTD article//FR" "article.dtd" would result in the doctype <!DOCTYPE article PUBLIC "-//CASA//DTD article//FR" "article.dtd">. The root element of the document is extracted from this doctype. If no doctype is provided, the root element is <index>. | No (defaults to no doctype) |
The files to merge can be selected using a <fileset> element. See Ant documentation for more details about this element.
This element may not contain text.
To merge XML files in the index directory using ISO 8859-1 encoding into a file called index.xml with no doctype (and a root element index), you may write:
<merge dir="index" tofile="index.xml" encoding="iso-8859-1" doctype="index PUBLIC '-//CASA//DTD index//FR' 'index.dtd'"/>
To merge the XML files in the xml directory and subdirectories, in a single index.xml file, you may write:
<merge tofile="index.xml" encoding="iso-8859-1" doctype="index PUBLIC '-//CASA//DTD index//FR' 'index.dtd'"> <fileset dir="xml" includes="**/*.xml"/> </merge>
This task is for replacing a processing instruction with the content of a given file. You can thus include an HTML file fragment in an HTML file. This is the way menus are included in the web pages of my site.
The task declaration (after the <project> element) is the following:
<taskdef name="insert" classname="net.sweetohm.ant.xml.InsertTask"/>
Attribute | Description | Required |
file | File(s) to process. | No |
dir | Directory(ies) of the files to process. | No |
pattern | This is the name of the processing instruction to replace. For instance, if the pattern is foo, the processing instructions to replace are <?foo ?>. | No (defaults to insert) |
source | File to insert to replace processing instructions. | Yes |
You can select the files to process using a <fileset> element. See Ant documentation for more details about this element.
This element may not contain text.
To replace <?menu ?> processing instructions of files in the html directory with the content of the menu.html file, you may write the following element:
<insert dir="html" pattern="menu" source="menu.html"/>
Using this task, you can nest files within a given one, replacing a given processing instruction. You may this way nest HTML fragments in a page template.
The task declaration (after the <project> element) is the following:
<taskdef name="nest" classname="net.sweetohm.ant.xml.NestTask"/>
Attribute | Description | Required |
file | File(s) to process. | No |
dir | Directory for files to process | No |
pattern | The name of the processing instruction to replace. For instance, if this pattern value is foo, the replaced processing instructions are <?foo ?>. | No (defaults to nest) |
source | The file to nest around selected files. | Oui |
You can select the files to process using a <fileset> element. See Ant documentation for more details about this element.
This element may not contain text.
To nest files in the html directory within the file page.html, replacing the <?body ? processing instruction, you may write the following element:
<nest source="page.html" pattern="body" dir="html"/>
This task splits files using processing instructions. The name of the generated files is written in the processing instruction.
The task declaration (after the <project> element) is the following:
<taskdef name="split" classname="net.sweetohm.ant.xml.SplitTask"/>
For instance, the following file:
<?xml version="1.0"?> <?split file="page1.html"?> <html> ... </html> <?split file="page1.html"?> <?split file="page2.html"?> <html> ... </html> <?split file="page2.html"?>
Will be split in two files (page1.html and page2.html):
<html> ... </html>
Most of XSLT processors (such as XT or Xalan) implement extensions to produce more than one file but this task allows you to avoid to pollute your XSLT code with proprietary one.
Attribute | Description | Required |
file | File(s) to process. | No |
dir | Directory(ies) of the files to process. | No |
pattern | This is the name of the processing instruction used to split the files. For instance, if this pattern is split, the processing instructions where to cut files look like <?split file="foo"?>. | No (defaults to split) |
You can choose the files to process with a nested <fileset> element. See Ant documentation for more details about this element.
This element may not contain text.
To split a file called foo using processing instructions that look like <?cut file="bar"?>, you would write:
<split file="foo" pattern="cut"/>
This task counts words in an XML document. Useful when you write an XML file with word count constraints (for an article for instance). The text in any element of the document is taken into account while the text in attributes is ignored. This default behavior may be changed using the task parameters.
The task declaration (after the <project> element) is the following:
<taskdef name="xwc" classname="net.sweetohm.ant.xml.WordCountTask"/>
Attribute | Description | Required |
separators | The list of separator characters (characters between words). | No (defaults to a reasonable list of separators). |
excludeElements | A coma separated list of elements to exclude from the word count. | No (defaults to no elements). |
singleElements | A coma separated list of elements that count as a single word. | No (defaults to no elements). |
includeAttributes | A coma separated list of attributes to include in the word count. Those attributes are written as element@attribute, thus an attribute is associated to an element. | No (defaults to no attributes). |
documentProperties | A file that contains the properties of the document (that is a list of elements to exclude, single elements and attributes to include). This is a properties file containing the properties separators, excludeElements, singleElements and includeAttributes. Each of these property may be empty. | No (no properties are loaded when empty). |
property | The Ant property where the word count is put. | Yes |
propertyFiles | The Ant property where to put the file count. | No (defaults to no property). |
quiet | If set to yes, the task doesn't display any word and file count. | No (default to no). |
You can choose the files to process with a nested <fileset> element. See Ant documentation for more details about this element.
This element may not contain text.
Let's say you want to count words in the XML files of the current directory except build.xml and want to exclude the source element of the word count. You may write the following target:
<target name="wc"> <xwc excludeElements="source"> <fileset dir="." includes="*.xml" excludes="build.xml"/> </xwc> </target>
This will produce the following output:
$ ant wc Buildfile: build.xml wc: [xwc] 8414 words in 2 file(s). BUILD SUCCESSFUL Total time: 4 seconds
Now let's suppose that we want to write the count rules in a properties file named document.properties:
excludeElements=comment,source singleElements=file,keyb,code includeAttributes=sect@title
This file indicates the following rules:
To count words in XML documents of the xml directory using those rules and put the result in the wc Ant property, we might write:
<target name="wc"> <xwc property="wc" documentProperties="document.properties"> <fileset dir="xml" includes="*.xml"/> </xwc> </target>
This task checks links in HTML files. It distincts two kinds of links: local links (that point to a file in the same site and are relatives) and external ones (that point to other sites and start with a protocol indicator such as http: or ftp:).
The task declaration (after the <project> element) is the following:
<taskdef name="link" classname="net.sweetohm.ant.html.LinkTask"/>
Attribute | Description | Required |
file | File(s) to process. | No |
dir | Directory(ies) to process. | No |
external | Indicates if external links should be checked. | No (defaults to false). |
interrupt | Indicates if processing should stop with an error message if a broken link is met. | No (defaults to false). |
log | The name of the file where to log link checks. | No |
You can choose the files to process with a nested <fileset> element. See Ant documentation for more details about this element.
This element may not contain text.
Let's suppose that you want to check HTML files in the html directory and it's subdirectories and want to stop processing if a broken link is met. You would write:
<link external="false" interrupt="true"> <fileset dir="html" includes="**/*.html"/> </link>
If a broken link is met, the compilation will stop with an error message that gives the file and link.
If you want to to check external links (which may be quite long) without stopping on error and log checks in a file named links.txt, you may write:
<link external="true" interrupt="false" log="links.txt"> <fileset dir="html" includes="**/*.html"/> </link>
Ant is widely used to build Java projects, and thus is integrated to many Java development tools (such as JDE, an Emacs mode for Java development). Nevertheless, Ant also proves to be helpful in many other occasions, such as generate HTML documentation from XML source files. That's what lead me to develop this standalone Ant mode for Emacs.
To install this Ant mode, copy el/ant.el file in a location where Emacs will find it and add the following lines in your .emacs configuration file:
(load "ant") (ant-mode)
When you restart Emacs, an Ant menu appears, with the following entries:
The result of the build is printed in the compilation buffer and you may thus click on an error message to open the responsible file in an Emacs buffer.
It may be useful to bind keys to Ant calls. For instance, my .emacs file has the following lines:
(global-set-key [f2] 'ant-build) (global-set-key [f3] 'ant-rebuild)
This software is under Apache Software License. You may find a copy of this license (and of the licenses of other software used in this program) in the LICENSE file of SAT installation directory.
The documentation uses my new DTD and has been updated.
New tasks:
Bug fix and enhancements:
The task Marge has been optimized (using StringBuffer). The task is about 30 times faster !
New Valid task to validate XML files.
New Link task to check links in an HTML file.
Test tasks attributes. File attributes (such as file and dir) are checked.
Bug correction (file path are now relative to the project file location instead of the current directory).
It is now possible to set and get Ant properties from within a Beanshell script using setAntProperty() and getAntProperty() commands.
Tasks are no more executable outside Ant (this was much more simple within Ant and painful to maintain).
The code was ported to the last versions of Beanshell (version 1.1a18 works, but 1.2b1 doesn't due to a bug).
Added unit tests (in the prj/test directory).
The JAXP API is now used in the XTask. Permits to instanciate the XML parser in a generic way (independent from the parser implementation, provided that it implements the JAXP API). This class also search correctly for files (in the directory of the build.xml file).
The tasks have been modified (after intensive usage to generate my web site) to be more efficient/user friendly. Changes are the following:
There should not be any syntax modification in future versions, that should only stabilize the code (attributes have to be tested to produce informative error messages).
First distributed version of SAT. The tasks Bsh, XTask, Merge, Insert and Split have been implemented.
Enjoy !