NAME

Java::Build::GenericBuild - a high level driver to control Java builds

SYNOPSIS

There are two (or more) code files needed to effectively use this module. First, create a subclass of this class:

package Java::Build::MyBuild;
use Carp;

# Do the following in a BEGIN block before the use base statement:
BEGIN { $ENV{CLASSPATH} .= ":/path/to/sun's/lib/tools.jar"; }
use base 'Java::Build::GenericBuild';
# use any other Java::Build modules you need

my @args = (
    { BUILD_SUCCESS => sub { croak "You must supply a BUILD_SUCCESS"  } },
    { CONFIG_LOC    => sub { croak "You must supply a CONFIG_LOC"     } },
    { MAIN_DIR      => \&_form_main_dir                                 },
    # ...
); # Include all the attributes that matter to your build here, and
   # what to do if the caller omits them.
   # If they are required, die in the subroutine, otherwise provide a
   # subroutine reference which will fill in the default

sub new {
    my $class = shift;
    my $self  = shift;
    $self->{ATTRIBUTES} = \@attrs;
    process_attrs($self);

    return bless $self, $class;
}

# Include common targets callers can share here.  Put unique targets
# in the calling scripts (see below).
sub init           { my $self = shift; ... }
sub cvs_refresh    { my $self = shift; ... }
sub compile        { ... }
# ...
sub _form_main_dir { my $self = shift; $self->{MAIN_DIR} = '/usr/src'; }

In some script:

#!/usr/bin/perl
use strict; use warnings;

use Java::Build::MyBuild;

my $project = Java::Build::MyBuild->new(
    BUILD_SUCCESS => '/where/this/module/can/store/build/state.info',
    CONFIG_LOC    => '/some/path/to/my.conf',
    NAME          => 'MyApplication',
    SRC_DIR       => '/where/my/java/files/live',
    SUBPROJECTS   => [
        { NAME => "util"                        },
        { NAME => "app", USING => \&compile_app },
    ],
);
$project->targets(qw( init cvs_refresh unique compile ));
$project->GO(@ARGV);

package Java::Build::MyBuild; # re-enter the build package to add targets
sub unique {...} # a routine that MyBuild doesn't provide

DESCRIPTION

This module is designed to be the top level controller of a build. You do not need to use this to enjoy most of the benefits of Java::Build. Yet, we have found this scheme very useful. Here's how we do it.

First, we make a hash which has all the data about our project. In that we describe the paths, files, names, and subprojects in our project.

Second, we pass that hash to the constructor of a subclass of this class which calls this module's process_attrs routine to fill in missing default values according to the rules in @args. See below for details.

Third, we call targets to set the order of the build. We could put these directly into the hash passed to the constructor, but we actually separate the constructor call from the other steps in two files. This allows us to describe the project in one place, then perform a variety of builds in another place.

Finally, we call GO which begins running the targets in order. The user can request which targets they want to run, see GO below for details of how dependencies are enforced.

targets

This is get/set accessor for the TARGETS field. It is often convenient to call this after construction, especially when construction and use are in different files for the purpose of separting the build description from the target list.

GO

This is the operative method of this module. It uses the TARGETS attribute to run the build. To use this approach, you list the subroutines to call in order, by name, under the TARGETS key of your build hash (you can put them there in your constructor call, or call targets to do it later). Then, pass a list of user requested targets to this routine. The rest happens automagically.

If you don't pass any parameters, GO will perform a complete build from scratch, calling each TARGETS member in order from first to last.

If you pass one parameter, the build will run from the last successful task up to and including the requested task.

If you pass in more than one parameter, GO will order these, compare them to the last successful build step, and produce a final list of required targets which must be executed in order to satisfy the user request.

This assumes that the TARGETS must be done in order in a successful build. Each TARGETS member requires all previous TARGETS to be successfully accomplished, before it will run. That can happen during this build, or in a previous build. The success of previous builds is recorded in BUILD_SUCCESS, which must be in your project hash. You can even lie by manually editing the BUILD_SUCCESS file (if that makes your build explode in flames, don't come running to me). That file must have one line of the form:

last_successful_target=name

In all cases, the first item in TARGETS runs. This is usually an init step. You might use it to obtains a build lock, open a log, etc. If it should always be done at the outset, put it in your first target. This include setting up signal handlers to clean-up if the program dies.

process_attrs

For each element in its ATTRIBUTES array reference, process_attrs looks in the caller's hash. If the element is already there, nothing happens. If the element is missing, it calls the corresponding code reference with the caller's hash. The elements are processed in order, so later routines can rely on the earlier ones to fill in values they need. Any required attributes should be first in the ATTRIBUTES list and have an appropriate fatal subroutine.