In looking at the build.xml files for components created so far, I'd like
to suggest some common practices based on what seems to work well.  If we
started converging on these practices, then we can codify them in
guidelines for new Commons committers so they don't have to struggle to
figure out how we do things.

Note that these guidelines would certainly be useful within
jakarta-commons-sandbox as well as jakarta-commons -- but we've explicitly
said that the sandbox has few or no rules, so the "official" guidelines
should apply to jakarta-commons only.

To help us towards the goal of similar implementations across components,
I've got the following suggestions for us to consider and comment on:


(1) Reliance on Ant Installation

Cactus and Beanutils expect you to have installed your favorite version of
Ant, including setting ANT_HOME and adding $ANT_HOME/bin to your PATH.  In
addition, you will need to install any optional tasks (and JAR files they
depend on) in $ANT_HOME/lib.  This approach means we don't need to create
"build.bat" and "build.sh" scripts -- we can just execute "ant" directly
to run our builds.


(2) Ant Library Dependencies

Ant supports the ability to configure the set of JAR files it uses in the
$ANT_HOME/lib directory.  At least for the present, can we encourage
people to configure like this?
- Ant 1.3 or later (affects what commands you can use in build.xml files).
- Install the corresponding optional.jar file.
- Replace the JAXP/1.0 parser (jaxp.jar and parser.jar) with the
  JAXP/1.1 stuff (jaxp.jar, crimson.jar, and xalan.jar).  This makes
  things like the <style> task work, and will become the norm in
  Ant 1.4.
- Other JARs needed here should be documented in the instructions for
  the Commons component that needs them (although see my separate rant
  about stylebook :-).


(3) CLASSPATH Assumptions

To the maximum extent feasible, build.xml scripts should make zero
assumptions about the contents of the developer's CLASSPATH (or
assumptions about what has been installed in the $JAVA_HOME/jre/lib/ext
directory on Java2 systems).  A good way to test this is to explicitly
unset your CLASSPATH, and make sure all the targets in your build.xml
script work correctly.

Note that this requires mechanisms for declaring external dependencies --
see below for suggestions.


(4) Source Directory Structure

The following directory layout will be typical for a Commons component:

jakarta-commons/foo/             For component "foo"
  build/                         BUILD SCRIPTS
    build.xml                    Standard build.xml script (with others
                                 if appropriate)
    build.properties.sample      Example external dependencies file (see
                                 below for details)
  conf/                          CONFIGURATION FILE SOURCES
    MANIFEST.MF                  Manifest file for JAR(s)
  dist/                          (OUTPUT) BINARY DISTRIBUTION
    conf/                        Static configuration files
    docs/                        Documentation other than JavaDocs
    javadoc/                     JavaDoc API docs
    lib/                         JAR file(s) containing the component
    src/                         Copy of CVS source repository (?)
  docs/ (or xdocs/)              DOCUMENTATION OTHER THAN JAVADOCS
  src/                           SOURCE CODE MODULES
    java/                        Java sources (or "share") - may be >1
    test/                        Unit test sources (same package
                                 hierarchy as "java")
  target/                        (OUTPUT) BUILD DESTINATION
    classes/                     Compiled classes for component itself
    conf/                        Configuration files (possibly filtered)
    tests/                       Compiled classes for unit tests
  web/                           WEB APPLICATION SOURCES (IF RELEVANT)

A couple of notes related to this:

- Larger components (like Cactus) will probably have more specialized
  directory structures, but this should suffice for relatively simple
  components.

- The "clean" target will generally delete "dist" and "target".

- The "compile" target will generally compile to "target/classes" and
  leave the output classes and resources unJAR'd.  It will normally depend
  on a "static" target to copy configuration files.

- The "compile.tests" target will generally compile to "target/tests".

- The "javadoc" target will generally output to "dist/javadoc".

- The "ant dist" target will generally depend on "compile" and "javadoc",
  and will copy things from "target" to "dist" (including JARing the
  component itself, for most components).

- If your Commons component has a dependency on another Commons
  component, you would typically have the following properties in
  your build.xml script (see below for more on declaring external
  dependencies):

    <property name="foo.home"      value="../foo/dist">
    <property name="foo.jar"       value="${foo.home}/lib/foo.jar"/>


(5) Base Directory Declaration in "build.xml" file

Although the "build.xml" file itself is placed in the
"build" subdirectory, you should use a 'basedir=".."' element in your
<project> declaration, so that relative paths get resolved against the
component's primary directory.  This avoids the need to prefix all of
these paths with "../" due to the location of the build.xml file.


(6) Documenting Available Targets

It is typical to see Ant targets like "usage" or "tasks" that simply use
the <echo> command to create a usage message.  Normally, only those
targets intended for direct use by the developer are documented here.

You can accomplish the same goal with less work by using the
"description" attribute of the <target> element, like this:

  <target name="static" depends="..."
   descriptoin="Copy static files to build directory">
    ...
  </target>

Now, when the user types "ant -projecthelp" they get a nice list of all
the targets that have a description (in alphabetical order), followed by a
list of the other targets (which are presumably used internally and not
intended for human consumption).


(7) Conventional Target Names

Although Ant doesn't care about target names, it is easier on humans if
the same names are generally used for the same tasks.  The following are
suggested for common use cases:
- init - Initialize properties and evaluate conditionals
- prepare - Create output directories in the "target" destination
- static - Copy static files (perhaps with filtering) to the
  "target" destination
- compile - Compile Java classes to the "target" destination
- compile.tests - Compile the (optional) unit tests
- javadoc - Create JavaDoc API documentation to the "target" destination
- dist - Create a binary distribution to the "dist" destination
- clean - Delete the "target" and "dist" destinations
- all - Execute clean,compile (or whatever is appropriate)
- test - Execute all unit tests
- test.xxx - Execute unit tests for module xxx of the package


(8) User Customizable Properties

Many users will wish to customize properties that have default values set
in the build.xml script.  In fact, you might wish to customize at three
different levels (local to this component, common to all the
jakarta-commons components, common to all builds that use this
convention).  In order to enable this, include the following settings in
your build.xml file:

  <property file="build/build.properties"/>
  <property file="../build.properties"/>
  <property file="${user.home}/build.properties"/>

In addition, your build.xml file should provide reasonable defaults for
each individual property you rely on.

Note that the "build.properties" file itself is not checked in to CVS (and
should be listed in the ".cvsignore" file to avoid confusing
messages).  To help users understand what properties might be usefully set
in one of these files, you should include a
"build/build.properties.sample" file that contains documentation and
examples for these values.


(9) Declaring External Dependencies

One of the common use cases for using build.properties files is to declare
the location of external packages that your build process depends on.  At
least two approaches are in common use:

- Declare the pathname to the binary distribution directory of the
  package you depend on (by convention, for package "foo" the property
  would be named "foo.home").

- Declare the pathname to the JAR file you actually need (by convention,
  for package "foo" the property would be named "foo.jar").  Note that
  this approach is problematic when you need more than one file from the
  distribution.

To accomodate both approaches, build files should support both use
cases.  For example, if you depend on JUnit you might include the
following properties in your build.xml file:

  <property name="junit.home" value="/usr/local/junit3.5"/>
  <property name="junit.jar"  value="${junit.home}/junit.jar"/>

so that a particular developer can override either property (in a
build.properties file) and still get the desired results.



Craig McClanahan

Reply via email to