Apache Ant Task Design Guidelines
This document covers how to write ant tasks to a standard required to be
incorporated into the Ant distribution. You may find it useful when
writing tasks for personal use as the issues it addresses are still
there in such a case.
Don't break existing builds
Even if you find some really hideous problem with ant, one that is easy
to fix, if your fix breaks an existing build file then we have problems.
Making sure that every build file out there still works, is one of the
goals of all changes. As an example of this, Ant1.5 passes the single
dollar sign "$" through in strings; Ant1.4 and before would strip it. To
get this fix in we first had to write the test suite to expose current
behaviour, then change something so that singe $ was passed through, but
double "$$" got mapped to "$" for backwards compatibility.
Use built in helper classes
Ant includes helper tasks to simplify much of your work. Be warned that
these helper classes will look very different in ant2.0 from these 1.x
versions. However it is still better to use them than roll your own, for
development, maintenance and code size reasons.
Execute
Execute will spawn off separate programs under all the platforms which
ant supports, dealing with Java version issues as well as platform
issues. Always use this task to invoke other programs.
Java, ExecuteJava
These classes can be used to spawn Java programs in a separate VM (they
use execute) or in the same VM -with or without a different classloader.
When deriving tasks from this, it often benefits users to permit the
classpath to be specified, and for forking to be an optional attribute.
Project and related classes
Project, FileUtils, JavaEnvUtils all have helper functions
to do things like touch a file, to
copy a file and the like. Use these instead of trying to code them
yourself -or trying to use tasks which may be less stable and fiddlier
to use.
Obey the Sun/Java style guidelines
The Ant codebase aims to have a single unified coding standard, and that
standard is the
Sun Java coding guidelines
It's not that they are better than any alternatives, but they are a
standard and they are what is consistently used in the rest of the
tasks. Code will not be incorporated into the database until it complies
with these.
If you are writing a task for your personal or organisational use, you
are free to use whatever style you like. But using the Sun Java style
will help you to become comfortable with the rest of the Ant source,
which may be important.
One important rule is 'no tabs'. Use four spaces instead. Not two,
not eight, four. Even if your editor is configured to have a tab of four
spaces, lots of others aren't -spaces have more consistency across
editors and platforms. Some IDEs (JEdit) can highlight tabs, to stop you
accidentally inserting them
There is an ant build file check.xml in the main ant directory with runs
checkstyle over
ant's source code.
Attributes and elements
Use the Ant introspection based mapping of attributes into Java datatypes,
rather than implementing all your attributes as setFoo(String) and doing
the mapping to Int, bool or file yourself. This saves work on your part,
lets Java callers use you in a typesafe manner, and will let the Xdocs
documentation generator work out what the parameters are.
The ant1.x tasks are very inconsistent regarding naming of attributes
-some tasks use source, others src.
Here is a list of preferred attribute names.
failonerror
|
boolean to control whether failure to execute should throw a
BuildException or just print an error.
Parameter validation failures should always throw an error, regardless
of this flag
|
destdir
|
destination directory for output
|
destfile
|
destination file for output
|
srcdir
|
source directory
|
srcfile
|
source file
|
Yes, this is a very short list. Try and be vaguely consistent with the core
tasks, at the very least.
Support classpaths
Try and make it possible for people to supply a classpath to your task,
if you need external libraries, rather than make them add everything to
the ANT_HOME\lib directory. This lets people keep the external libraries
in their ant-based project, rather than force all users to make changes
to their ant system configuration.
Design for controlled re-use
Keep member variables private. If read access by subclasses is required.
add accessor methods rather than change the accessiblity of the member.
This enables subclasses to access the contents, yet
still be decoupled from the actual implementation.
The other common re-use mechanism in ant is for one task to create and
configure another. This is fairly simple.
Do your own Dependency Checking
Make has the edge over Ant in its integrated dependency checking: the
command line apps make invokes dont need to do their own work. Ant tasks
do have to do their own dependency work, but if this can be done then
it can be done well. A good dependency aware task can work out the dependencies
without explicit dependency information in the build file, and be smart
enough to work out the real dependencies, perhaps through a bit of file parsing.
The depends task is the best example of this. Some of the zip/jar
tasks are pretty good too, as they can update the archive when needed.
Most tasks just compare source and destination timestamps and work from there.
Tasks which don't do any dependency checking do not help users as much as
they can, because their needless work can trickle through the entire build, test
and deploy process.
Support Java 1.2 through Java 1.4
Ant1.5 and lower was designed to support Java1.1. Ant1.6 and higher
is designed to support Java1.2: to build on it, to run on it. Sometimes
functionality of tasks have to degrade in that environment
- this is usually due to library limitations;
such behaviour change must always be noted in the documentation.
What is problematic is code which is dependent on Java1.3 features
-java.lang.reflect.Proxy, or Java1.4 features - java.io.nio for example.
Be also aware of extra
methods in older classes - like StringBuffer#append(StringBuffer).
These can not be used directly by any code and still be able to compile
and run on a Java 1.2 system.
If a new method in an existing class
is to be used, it must be used via reflection and the
NoSuchMethodException handled somehow.
What if code simply does not work on Java1.2? It can happen. It will
probably be OK to have the task as an optional task, with compilation
restricted to Java1.3 or later through build.xml modifications.
Better still, use reflection to link to the classes at run time.
Java 1.4 adds a new optional change to the language itself, the
assert keyword, which is only enabled if the compiler is told
to compile 1.4 version source. Clearly with the 1.2 compatibility requirement,
Ant tasks can not use this keyword. They also need to move away from
using the JUnit assert() method and call assertTrue()
instead.
Java 1.5 will (perhaps) add a new keyword - enum, one should avoid
this for future compatibility.
Refactor
If the changes made to a task are making it too unwieldy, split it up
into a cleaner design, refactor the code and submit not just feature
creep but cleaner tasks. A common design pattern which tends to occur in
the ant process is the adoption of the adapter pattern, in which a base
class (say Javac or Rmi) starts off simple, then gets convoluted with
support for multiple back ends -javac, jikes, jvc. A refactoring to
split the programmable front end from the classes which provide the back
end cleans up the design and makes it much easier to add new back ends.
But to carry this off one needs to keep the interface and behaviour of
the front end identical, and to be sure that no subclasses have been
accessing data members directly -because these data members may not
exist in the refactored design. Which is why having private data members
is so important.
Test
Look in ant/src/testcases and you will find JUnit tests for the
shipping ant tasks, to see how it is done and what is expected of a new
task. Most of them are rudimentary, and no doubt you could do better for
your task -feel free to do so!
A well written set of test cases will break the Ant task while it is in
development, until the code is actually complete. And every bug which
surfaces later should have a test case added to demonstrate the problem,
and to fix it.
The test cases are a great way of testing your task during development.
A simple call to 'build run-test' in the ant source tree will run all ant
tests, to verify that your changes don't break anything.
To test a single task, use the one shot ant run-single-test
-Dtestcase=${testname}
where ${testname}
is the name of your test class.
The test cases are also used by the committers to verify that changes
and patches do what they say. If you've got test cases it increases your
credibility significantly. To be precise, we hate submissions without
test cases, as it means we have to write them ourselves. This is
something that only gets done if we need the task or it is perceived as
utterly essential to many users.
Remember also that Ant 1.x is designed to compile and run on Java1.2, so
you should test on Java 1.2 as well as any later version which you use.
You can download an old SDK from Sun for this purpose.
Finally, run a full build test
before and after you start
developing your project, to make sure you havent broken anything else by
accident.
Document
Without documentation, the task can't be used. So remember to provide a
succint and clear html (soon, xml) page describing the task in a similar
style to that of existing tasks. It should include a list of attributes
and elements, and at least one working example of the task. Many users
cut and paste the examples into their build files as a starting point,
so make the examples practical and test them too.
You can use the xdocs stuff in proposal/xdocs to autogenerate your
documentation page from the javadocs of the source; this makes life
easier and will make the transition to a full xdoclet generated
documentation build process trivial.
Licensing and Copyright
Any code submitted to the Apache project must be compatible with the
Apache Software License, and the act of submission must be viewed as an
implicit transfer of ownership of the submitted code to the Apache
Software Foundation.
This is important.
The fairly laissez-faire license of Apache is not compabitible with
either the GPL or the Lesser GPL of the Free Software Foundation -the
Gnu project. These licenses have stricter terms, "copyleft", which are
not in the Apache Software Foundation license.
This permits people and organisations to build
commercial and closed source applications atop the Apache libraries and
source -but not use the Apache, Ant or Jakarta Project names without
permission.
Because the Gnu GPL license immediately extends to cover any larger
application (or library, in the case of GLPL) into which it is
incorporated, the Ant team can not incorporate any task based upon GPL
or LGPL source into the Ant codebase. You are free to submit it, but it
will be politely and firmly rejected.
Once ant-2 adds better dynamic task incorporation, it may be possible to
provide a framework for indirectly supporting [L]GPL code, but still no tasks
directly subject to the Gnu licenses can be included in the Ant
CVS tree.
If you link to a GPL or LGPL library, by import
or
reflection, your task must be licensed under the same terms. So tasks
linking to (L)GPL code can't go into the Apache managed codebase.
Tasks calling such code can use the 'exec' or 'java' tasks to run the
programs, as you are just executing them at this point, not linking to
them.
Even if we cannot include your task into the Apache codebase, we can
still point to where you host it -just submit a diff to
xdocs/external.html pointing to your task.
If your task links directly to proprietary code, we have a differnt
problem: it is really hard to build the tasks. Please use reflection.
Dont re-invent the wheel
We've all done it: written and submitted a task only to discover it
was already implemented in a small corner of another task, or it has
been submitted by someone else and not committed. You can avoid this
by being aware of what is in the latest CVS tree -keep getting the daily
source updates, look at manual changes and subscribe to the dev
mailing list.
If you are thinking of writing a task, posting a note on your thoughts
to the list can be informative -you well get other peoples insight and
maybe some half written task to do the basics, all without writing a
line of code.
Submitting to Ant
The process for submitting an Ant task is documented on the
jakarta web site.
The basic mechanism is to mail it to the dev mailing list.
It helps to be on this list, as you will see other submissions, and
any debate about your own submission.
You may create your patch file using either of the following approaches.
The committers recommend you to take the first approach.
-
Approach 1 - The Ant Way
Use Ant to generate a patch file to Ant:
ant -f patch.xml
This will create a file named patch.tar.gz that will contain a unified
diff of files that have been modified and also include files that have
been added. Review the file for completeness and correctness. This approach
is recommended because it standardizes the way in which patch files are
constructed. It also eliminates the chance of you missing to submit new files
that constitute part of the patch.
Approach 2 - The Manual Way
Patches to existing files should be generated with
cvs diff -u filename
and save the output to a file. If you want to get
the changes made to multiple files in a directory , just use cvs
diff -u
. Then, Tar and GZip the patch file as well as any new files
that you have added.
The patches should be sent as an attachment to a message titled [PATCH]
and distinctive one-line summary in the subject of the patch. The
filename/task and the change usually suffices. It's important to include
the changes as an attachment, as too many mailers reformat the text
pasted in, which breaks the patch.
Then you wait for one of the committers to commit the patch, if it is
felt appropriate to do so. Bug fixes go in quickly, other changes
often spark a bit of discussion before a (perhaps revised) commit is
made.
New submissions should be proceeded with [SUBMIT]. The mailer-daemon
will reject any messages over 100KB, so any large update should be
zipped up. If your submission is bigger than that, why not break it up
into separate tasks.
We also like submissions to be added to
bugzilla, so that they
dont get lost. Please submit them by first filing the report with a
meaningful name, then adding files as attachments. Use CVS diff files
please!
If you hear nothing after a couple of weeks, remind the mailing list.
Sometimes really good submissions get lost in the noise of other issues.
This is particularly the case just prior to a new point release of
the product. At that time anything other than bug fixes will tend
to be neglected.
Checklists
These are the things you should verify before submitting patches and new
tasks. Things don't have to be perfect, it may take a couple of
iterations before a patch or submission is committed, and these items
can be addressed in the process. But by the time the code is committed,
everything including the documentation and some test cases will have
been done, so by getting them out the way up front can save time.
The committers look more favourably on patches and submissions with test
cases, while documentation helps sell the reason for a task.
Checklist before submitting a patch
- Added code complies with style guidelines
- Code compiles and runs on Java1.2
- New member variables are private, and provide public accessor methods
if access is actually needed.
- Existing test cases succeed.
- New test cases written and succeed.
- Documentation page extended as appropriate.
- Example task declarations in the documentation tested.
- Diff files generated using cvs diff -u
- Message to dev contains [PATCH], task name and patch reason in
subject.
- Message body contains a rationale for the patch.
- Message attachment contains the patch file(s).
Checklist before submitting a new task
- Java file begins with Apache copyright and license statement.
- Task does not depend on GPL or LGPL code.
- Source code complies with style guidelines
- Code compiles and runs on Java1.2
- Member variables are private, and provide public accessor methods
if access is actually needed.
- Maybe Task has failonerror attribute to control failure behaviour
- New test cases written and succeed
- Documentation page written
- Example task declarations in the documentation tested.
- Patch files generated using cvs diff -u
- patch files include a patch to defaults.properties to register the
tasks
- patch files include a patch to coretasklist.html or
optionaltasklist.html to link to the new task page
- Message to dev contains [SUBMIT] and task name in subject
- Message body contains a rationale for the task
- Message attachments contain the required files -source, documentation,
test and patches zipped up to escape the HTML filter.
Copyright © 2001-2003 Apache Software Foundation. All rights
Reserved.