Fireant Builder

Using Ant programmatically

Introduction

This project provide a framework to use Apache Ant programmatically and directly with Java code, by-passing build.xml. It is a wrapper on top of Ant, not replacing Ant. There are a few advantages that make this a more productive environment :
  • Use Java constructs (control statements, expressions, regex, I/O... etc) directly without having to go through taskdef and XML. Many operations are much straight forward when coding in Java directly.
  • Since the builder is pure Java code, it can be developed and debug with all the readily available Java tools (IDE, editor, debugger, ... etc.). With a proper Java IDE, you get syntax and reference checks for free. You can also step through the build process with a Java debugger and refactor just like normal java code.

Wrappers are provided for some frequently used tasks and data types. Tasks that are not wrapped can also be instantiated and called directly. However, some fairly good understanding of the tasks may be required in order to setup and call them properly.

See the sample builder here , the fireant and the blacksun.fireant project in the source distribution for examples.

Releases notes

How it works

It is indeed quite straight forward. Instead of writing a build.xml, you write a builder class in Java (eg. Builder.java). The builder class should extends the FireantBuilder class. The builder class simply defines each target of the project as a nested class derived from FireantTarget. The target can instantiate Ant tasks in its constructor, or it can call Ant tasks and use Java code directly in the execute() method. The target can also invoke other target(s) (in the same or in another builder) by the addDependency() calls in its constructor. Or it can invoke target(s) directly in the execute() method using, for example, the build(Class target) method (See blacksun.fireant project fireant.blacksun.Builder.java class for examples).

The FireantBuilder.build() methods use reflection to look for all the FireantTarget classes defined in the builder and add the targets to a FireantProject. The FireantTarget is instantiated through the FireantTarget(FireantProject) constructor if one is found, otherwise it is invoked through the default constructor. The build() methods then invoke the Project.executeTarget(target) method to build the target, just like a normal Ant project.

Building the builders

While the most convinent way to compile and invoke a fireant builder is using the Eclipse IDE. blacksun-fireant.jar now (since v0.8.0) include a main class, the BuildBuilder class that can be used to invoke javac to build the fireant builders from command line. First setup the JAVA_HOME environment variable properly and make sure java and javac executables are on the executable search path, then run:

    java -jar blacksun-fireant.jar [-options] <source-dir>

    Options:
        --help                  Help.
        --debug                 Print debug info.
        --anthome <dir>         Ant home directory.
        --lib <path>            Extra classpath.
        --javac <filepath>      Path to the javac executable.
        -d <dir>                Binary output directory.
        -e <regex>              Include files that match given jakarta-oro regex.
        -E <regex>              Exclude files that match given jakarta-oro regex.

This compile the all the java files in the source directory into the given output directory. If successful, you can invoke the compiled builders from command line:

    java -classpath <classpath> <builder-class> <target>

See the fireant.sh script in the black-sun blacksun.fireant project for example.

Eclipse fireant builder action

An Eclipse fragment as an addon to the black-sun builder plugin provide workbench actions that invoke one of the FireantBuilder.build(target) method of the builder. For small projects, the builder can simply be part of the target project. However, the preferred setup is to implement the builders for a number of other (related) projects in a separate builder project. In such case, the classpath for the builder can be configured independently. Eclipse can also generate a launch configuration with correct classpath to run the builders. See the blacksun.fireant project for example.

The builder can be invoked in many ways, from the workbench or from command line:

Invoke Builder from PackageView

When invoking the fireant action in PackageView over a java source file, the action would lookup all FireantTarget classes defined in the source file and popup a dialog with the targets. Simple select a target to invoke the builder to build that target. If the action is invoked from the NavigatorView, you have to enter the target name explicitly in the popup dialog. If class members are shown in the PackageView, the fireant action can also be invoked directly over the target class.

Invoke Builder from OutlineView

When invoking the fireant action in OutlineView of a java source file over a FireantTarget class, the action invoke the target defined by the selected class.

Invoke Builder from Java Source Editor

The fireant action can be invoked from the java editor context menu while editing a FireantBuilder source file. The action would look for the target class name at the cursor location and build that target.

Invoke Builder through Eclipse Launch Configuration

The builder can be invoked just like other java program with the Eclipse Launch Configuration eg. created by Run As/Java Application menu item, provided the builder has the main() method (see the sample builder for example). Eclipse would automatically configure the correct classpath when the launch configuration is created.

Invoke Builder as an Project/Incremental Builder

The builder can be configured as a project builder or as an incremental builder like the javabuilder or the BlacksunBuilder. Simply add the builder specification to the .project file, for example:
    <buildCommand>
            <name>sf.blacksun.eclipse.builder.FireantProjectBuilder</name>
            <arguments>
                    <dictionary>
                            <key>project</key>
                            <value>blacksun.fireant</value>
                    </dictionary>
                    <dictionary>
                            <key>builder</key>
                            <value>blacksun.FeatureBuilder</value>
                    </dictionary>
                    <dictionary>
                            <key>target</key>
                            <value>blacksun.FeatureBuilder$Print</value>
                    </dictionary>
                    <!-- optional -->
                    <dictionary>
                            <key>workdir</key>
                            <value></value>
                    </dictionary>
                    <dictionary>
                            <key>arguments</key>
                            <value>-verbose -debug</value>
                    </dictionary>
                    <dictionary>
                            <key>vmargs</key>
                            <value>-mx128M</value>
                    </dictionary>
                    <dictionary>
                            <key>norefresh</key>
                            <value>false</value>
                    </dictionary>
            </arguments>
    </buildCommand>
where the project argument specify the project that contains the fireant builder (optional, default=the project that trigger the build), the builder argument specify the fully qualified builder class name and the target argument specify the fully qualified target class name. If the target is an inner class of the builder, the builder option is optional.

There is also an IncrementalFireantProjectBuilder, which is same as the FireantProjectBuilder except that it is invoked with information about the resource delta when it is an incremental build. The delta information are passed as arguments to the FireantBuilder:

  • --incremental - If specified, there are delta information. Otherwise builder should perform a full build.
  • --added <file>@ - Full absolute path of files added. This option is repeated multiple times if there are multiple files added. CLIUtil should always return a List<String> for the array options.
  • --removed <file>@ - Full absolute path of files removed.
  • --changed <file>@ - Full absolute path of files that are modified.

Make sure the IncrementalFireantBuilder.main() accept the above command line options. Example, for CLIUtil, add the following to the option spec.:

    "incremental | added@ | removed@ | changed@"

To use the incremental builder from .project file, simply specify sf.blacksun.eclipse.builder.IncrementalFireantProjectBuilder instead of the sf.blacksun.eclipse.builder.FireantProjectBuilder in the .project file.

IMPORTANT: When invoking fireant builder from workbench, blacksun builder plugin direct output of each task to the Eclipse Console View. Make sure to turn off Automatic remove terminated launch when creating new launch option in Window/Preferences/Run/Debug/Launching preference page. Otherwise, you would only be able to view output of the most recent task.

Blacksun builder plugin would usually delete all terminated fireant launch configurations on Eclipse shutdown. However, in case Eclipse crashed (for example), old launch configurations may not be deleted automatically and need to be deleted manually from the launch menu. As of Eclispe 3.1 deleting too many configurations at a time works very slowly, delete about 20 at a time seems to works fine.

Invoke Builder from Command Line

The builder can be invoked from the command line, just like other java classes, when provided with the proper classpath.
    java -classpath <classpath> <Builder-class> <target>
The classpath for the builder can be obtained inside Eclipse through launch configuration or directly dump from the classloader. See the sample Builder Usage target for details.