In this example, we create a simple Java “executable”
(JAR file with a main method) that depends
on a simple “library”. The library contains some
automatically generated code which is generated by a code
generator that we also supply. Our code generator defines a
simple ant task to do the generation. To tie this all together,
we have several build items, which we will examine from the top
down in dependency order. This example can be found in the
doc/example/java directory. When building
Java code with ant, abuild makes use
of a custom logger. This logger suppresses the output of empty
ant targets, but does so in a manner
that works well with nested targets. If you wish to use the
standard ant logger, you can invoke
abuild with the --no-abuild-logger
command-line option.
We'll start off with the executable build
item in the executable directory. If you
look at its Abuild.conf file, you will see
that it depends on library:
java/executable/Abuild.conf
this: executable platform-types: java parent-dir: .. deps: library
From executable's
Abuild-ant.properties file, you can see that
this build item is generating the file
example-executable.jar, and that it is
generating a wrapper script:
java/executable/Abuild-ant.properties
abuild.jar-name = example-executable.jar abuild.main-class = com.example.executable.Executable abuild.wrapper-name = example
The src directory under
executable contains a
java directory and a
resources directory. The
src/java directory contains the java code,
which in this case consists of the single source file
Executable.java located in directory
structure based on its package, as always. The
resources directory contains the file
file.text which is to be included in the JAR
file. Our example executable reads the contents of this file
from the JAR file as an example of how to make use of the
resources directory.
Next, we'll turn our attention to the library
build item. Our library build item creates a
JAR file called example-library.jar as you
can see from looking at the value of
abuild.jar-name in its
Abuild-ant.properties file:
java/library/Abuild-ant.properties
abuild.jar-name = example-library.jar # Generate a Negator class using code-generator abuild.hook-build-items = code-generator code-generator.classname = com.example.library.generated.Negator
In addition, this build item makes use of hooks in the
code-generator build item. Specifically,
we tell that build item to generate a class called
Negator in the
com.example.library.generated package.
Our library build item contains a single
source file in src/java. This source file
uses the generated Negator class.
For most users, this is all you really need to know: you will
create your own code and resources and build them by setting
properties. Sometimes the properties will be standard abuild
properties, and sometimes there will be additional properties as
required by build items that provide build-related services to
your build item. As we continue, we will explore the
code-generator build item to see what's
going on behind the scenes.
The code-generator build item is in the
code-generator directory. This build item
creates a JAR file called
CodeGenerator.jar. It also sets the
abuild property abuild.include-ant-runtime
so that the ant runtime library will
be included in the compile-time classpath:
java/code-generator/Abuild-ant.properties
abuild.jar-name = CodeGenerator.jar abuild.include-ant-runtime = yes
This build item also has a file called
ant-hooks.xml. This file contains the build
hooks that are provided by this build item. The
ant-hooks.xml file references the property
code-generator.classpath, which is declared in
the Abuild.interface file. Here are those
files:
java/code-generator/Abuild.interface
# Provide a variable that names our code generator so we can use it in # taskdef. There's no reason for most build items to provide this # information, but it is needed for cases in which there is a reason # to refer to a specific output product individually. Since this jar # just creates an ant task, we don't need to add it to the classpath. declare code-generator.classpath filename code-generator.classpath = $(ABUILD_OUTPUT_DIR)/dist/CodeGenerator.jar
java/code-generator/ant-hooks.xml
<?xml version="1.0"?>
<!-- This ant fragment provides the "codegen" task, provided by -->
<!-- this build item, to generate a class named by the user of -->
<!-- the build item. -->
<project name="code-generator-hooks">
<!-- Create the targets required to glue our codegen target -->
<!-- into abuild. We want our codegen task to be called as a -->
<!-- code generator as a "generate" hook. We also provide a -->
<!-- properties-help hook. -->
<!-- Provide the "-generate" target and have it invoke the -->
<!-- "codegen" target. -->
<target name="-generate" depends="codegen"/>
<!-- Provide properties-help information. -->
<target name="-properties-help">
<echo>properties-help for code-generator:
Set the following property:
* code-generator.classname -- the fully qualified classname to
be generated
</echo>
</target>
<!-- Now provide our actual task and target. -->
<taskdef name="codegen"
classname="com.example.codeGenerator.ExampleTask"
classpath="${code-generator.classpath}"/>
<!-- Our code generator will write to the generated-jsrc -->
<!-- directory named in the abuild.private.dir.generated-jsrc -->
<!-- property as provided by abuild. -->
<target name="codegen">
<fail message="property code-generator.classname must be defined"
unless="code-generator.classname"/>
<codegen sourcedir="${abuild.private.dir.generated-jsrc}"
classname="${code-generator.classname}"/>
</target>
</project>
This particular build item provides two hooks: a generate hook, and a properties-help hook. These are implemented by defining targets called -generate and -properties-help. It is proper for all build items that provide hooks to provide a properties-help hook if the hooks expect any additional properties to be set. In this case, we are generating code, so we need to know the name of the class we are to generate. Our -properties-help target echoes this information to ant's output stream. The generate hook is run by abuild before it compiles any code, so the javac step will be able to access the generated source files along with the hand-coded ones. Our -generate target is an empty target that depends on the codegen target which does the actual work.
Notice that there is nothing else required in an
ant-hooks.xml file other than creation of
appropriately named targets. Abuild's ant call will automatically
load and invoke these targets at the right time as required.
The code-generator build item's
Abuild.interface does something that most
build items don't have to do: it declares a custom variable that
holds the name of the JAR file that it generates, and it does not
add this JAR file to the classpath:
java/code-generator/Abuild.interface
# Provide a variable that names our code generator so we can use it in # taskdef. There's no reason for most build items to provide this # information, but it is needed for cases in which there is a reason # to refer to a specific output product individually. Since this jar # just creates an ant task, we don't need to add it to the classpath. declare code-generator.classpath filename code-generator.classpath = $(ABUILD_OUTPUT_DIR)/dist/CodeGenerator.jar
The reason for this is that
code-generator's users don't need to call
anything from its JAR file in their code, but its
ant-hooks.xml file, which will be invoked
with basedir set to the calling build item's
output directory, needs to know the location of the JAR file.
The code-generator's
src/java directory contains the source code
to the ant task, but the ant task's operation is clear just by
looking at the ant-hooks.xml file. Notice
that it writes its generated code in to the
${abuild.private.dir.generated-jsrc}
directory, which is what all code generators should do. (For
additional information about writing targets, see Section 27.3, “Guidelines for Ant Target Authors”.) Our
generate hook does not have to be concerned
about any other details. Abuild will call it automatically at
the correct point in the build process.
Next, look at the other-executable build
item. This build item actually contains no source code. All it
does is define the properties required to generate a wrapper
script, but this time, it uses a different main class in the
executable JAR file. This build item serves primarily as an
example of using the wrapper target, which is a post-package
hook, from a build item that doesn't actually package anything.
Here are its Abuild.conf and
Abuild-ant.properties files:
java/other-executable/Abuild.conf
this: other-executable platform-types: java parent-dir: .. deps: executable
java/other-executable/Abuild-ant.properties
abuild.main-class = com.example.executable.Other abuild.wrapper-name = other