Subversion Repositories DevTools

Rev

Rev 285 | Rev 295 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#! perl
########################################################################
# Copyright (C) 1998-2004 ERG Limited, All rights reserved
#
# Module name   : jats.sh
# Module type   : Makefile system
# Compiler(s)   : n/a
# Environment(s): jats
#
# Description:
#       This is a wrapper script to simplify access to the jats
#       build system
#
# Usage:        jats build | make | install | help | ...
#
# Package: PACKAGE_TAG
# Version: VERSION_TAG
#......................................................................#

require 5.008_002;
use strict;
use warnings;

use Cwd;
use File::Copy;
use Getopt::Long qw(:config require_order);     # Stop on non-option
use Pod::Usage;                                 # For help support
use FindBin;                                    # Determine the current directory
use lib "$FindBin::Bin/LIB";                    # Allow JATS libraries
use Config;
use Sys::Hostname;                              # For hostname

use JatsError;                                  # Common error reporting
use DescPkg;                                    # Package Descriptor processing
use JatsSystem;                                 # System call interface
use JatsBuildFiles;                             # Parse Build Files

my $GBE_VERSION = "VERSION_TAG";                # Will be re-written when released
my $GBE_NOT_RELEASED = "RELEASE_TAG";           # Will be re-written when released
my $opr_done;
my $opt_here = 0;
my $BUILD_FILE      = "build.pl";
my @BUILD_FILE_ALT  = qw(auto.pl build_test.pl);
my $BUILD_FILE_SET;
my $RESULT = 0;
my $PSPLIT = ':';                               # Win/Unix path splitter
my $TIMER = time;
my $opt_time = 0;
my $opt_help = 0;
my $opt_locate = 0;
my $opt_locate_file;
my $opt_locate_package;
my $opt_dir;
my $opt_java;
my $opt_export_vars = 1;
my $result;

#
#   Grab a copy of ALL the environment variable that will be used
#   This will simplify the script a great deal
#

our $GBE_JATS_VERSION   = $ENV{'GBE_JATS_VERSION'}   || '';
our $GBE_JATS_SANE      = $ENV{'GBE_JATS_SANE'}      || 0;
our $GBE_CORE           = $ENV{'GBE_CORE'}           || '';
our $GBE_BIN            = $ENV{'GBE_BIN'}            || '';
our $GBE_CONFIG         = $ENV{'GBE_CONFIG'}         || '';
our $GBE_DPLY           = $ENV{'GBE_DPLY'}           || '';
our $GBE_DPKG           = $ENV{'GBE_DPKG'}           || '';
our $GBE_DPKG_STORE     = $ENV{'GBE_DPKG_STORE'}     || '';
our $GBE_DPKG_LOCAL     = $ENV{'GBE_DPKG_LOCAL'}     || '';
our $GBE_DPKG_CACHE     = $ENV{'GBE_DPKG_CACHE'}     || '';
our $GBE_DPKG_SBOX      = $ENV{'GBE_DPKG_SBOX'}      || '';
our $GBE_SANDBOX        = $ENV{'GBE_SANDBOX'}        || '';
our $GBE_PERL           = $ENV{'GBE_PERL'}           || '';
our $GBE_TOOLS          = $ENV{'GBE_TOOLS'}          || '';
our $GBE_MACHTYPE       = $ENV{'GBE_MACHTYPE'}       || '';
our $GBE_HOSTMACH       = $ENV{'GBE_HOSTMACH'}       || $GBE_MACHTYPE;
our $GBE_PLATFORM       = $ENV{'GBE_PLATFORM'}       || '';
our $GBE_BUILDFILTER    = $ENV{'GBE_BUILDFILTER'}    || '';
our $GBE_DEBUG          = $ENV{'GBE_DEBUG'}          || 0;
our $GBE_VERBOSE        = $ENV{'GBE_VERBOSE'}        || 0;
our $GBE_DRV            = $ENV{'GBE_DRV'}            || '';
our $GBE_HOSTNAME       = $ENV{'GBE_HOSTNAME'}       || hostname || '';
our $USER               = $ENV{'USER'}               || '';
our $PERL5LIB           = $ENV{'PERL5LIB'}           || '';
our $JAVA_HOME          = $ENV{'JAVA_HOME'}          || '';
our $GBE_ABT            = $ENV{'GBE_ABT'}            || '';
our $GBE_UNIX           = 1;
our $CWD;

#-------------------------------------------------------------------------------
#   Clean up some environment variables
#       GBE_BUILDFILTER - may be space or comma separated list
#       GBE_PLATFORM - may be space or comma separated list
#       GBE_ABT - may be space or comma separated list
#       GBE_HOSTNAME - No whitespace
#
$GBE_BUILDFILTER = join (' ', split( /[,\s]+/, $GBE_BUILDFILTER));
$GBE_PLATFORM    = join (' ', split( /[,\s]+/, $GBE_PLATFORM));
$GBE_ABT         = join (' ', split( /[,\s]+/, $GBE_ABT));
$GBE_HOSTNAME    =~ s~\s+~~g;

#-------------------------------------------------------------------------------
#   Parse the user options
#   GetOptions has been configured so that it will stop parsing on the first
#   non-options. This allows the command syntax to the script to have options
#   that are directed to this script before the command keyword, and command
#   options to the subcommand to be after the keyword.
#
$result = GetOptions (
            "help|h:+"          => \$opt_help,
            "manual:3"          => \$opt_help,
            "verbose|v:+"       => \$GBE_VERBOSE,
            "debug:+"           => \$GBE_DEBUG,
            "cd|changedir=s"    => \$opt_dir,
            "locate"            => \$opt_locate,
            "locatefile=s"      => \$opt_locate_file,
            "locatepkg=s"       => \$opt_locate_package,
            "here"              => \$opt_here,
            "time"              => \$opt_time,
            "b|buildfile=s"     => \&opts_buildfile,
            "java=s"            => \$opt_java,
            "version=s",        => \&opts_version,
            "platform:s",       => sub{ opts_extend( \$GBE_PLATFORM, @_ )},
            "buildfilter:s"     => sub{ opts_extend( \$GBE_BUILDFILTER, @_ )},
            "abt:s"             => sub{ opts_extend( \$GBE_ABT, @_ )},
            "exportvars!"       => \$opt_export_vars,
            );

#
#   Configure the error reporting process now that we have the user options
#
ErrorConfig( 'name'    => 'JATS',
             'debug'   => $GBE_DEBUG,
             'verbose' => $GBE_VERBOSE );
#
#   Post process some of the options
#
Error ("Options not parsed. Use -help for help") unless $result;
Verbose3 ('ARGS', @ARGV);

#
#   Option Helper Routine
#   Save alternate buildfile. Flag that it has been set
#
sub opts_buildfile
{
    my ($junk, $bf) = @_;
    $BUILD_FILE = $bf;
    $BUILD_FILE_SET = 1;
    Verbose ("Use alternate buildfile: $BUILD_FILE");
    return;
}

#
#   Options Helper Routine
#   Collect a space/comma delimited list and replace/append to string
#   Allows the string to be reset
#   Use of a +" will append to existing value
#
#       arg1 - Ref to string to store data
#       arg2 - Option name
#       arg3 - Option value
#
sub opts_extend
{
    my( $ref, $name, $value) = @_;
    if ( $value )
    {
        $value =~ s/,/ /g;
        $value = ${$ref} . ' ' . $value
            if ( $value =~ s/\+/ /g );
        $value =~ s/^\s+//;
        $value =~ s/\s+/ /;
    }
    ${$ref} = $value;
    return;
}

#
#   Options Helper routine
#   Collect --version=version, then stop processing of the command line
#   This will simulate a '--'
#
sub opts_version
{
    my ($junk, $vn) = @_;
    $GBE_JATS_VERSION = $vn;
    die("!FINISH\n");
}

################################################################################
#
#   Ensure that essential environment variables are present and that they
#   do not contain spaces.
#
ReportError('Set env-var GBE_PERL (typically "/usr/bin/perl")')   unless ( $GBE_PERL );
ReportError('Set env-var GBE_DPKG (typically "/devl/dpkg_archive")') unless ( $GBE_DPKG );
ReportError('Set env-var GBE_CORE (typically "/devl/dpkg_archive/PACKAGE_TAG/VERSION_TAG")')  unless ( $GBE_CORE );
ReportError('Hostname cannot be determined') unless ( $GBE_HOSTNAME );

################################################################################
#
#   Warn if using a version of perl that is newer than expected
#   The 'require' does a lower limit test
#
if ( ! $GBE_JATS_SANE )
{
    Warning ("Perl Version may not be compatible",
             "Jats has been tested with: 5.8.8",
             "This version is: $]",
            ) if ( $] > 5.010000 );

    Warning ("The PDK, as used by some deployed packages does not work with this version of perl.",
             "ERG only has a licence for PDK V5 and this only works with Perl 5.6 and 5.8",
             "This version is: $]",
            ) if ( $] > 5.009000 );
}

#
#   Warn if this is not an active state release
#
#   Would be nice, but I can't figure out how to do it - yet
#   The following does not work on all platforms
#
#unless ( $ActivePerl::VERSION )
#{
#    Warning ("Perl does not appear to be an ActiveState Release", "Jats may not function as expected");
#}
#else
#{
#    Verbose ("ActiveState Version: $ActivePerl::VERSION");
#}

################################################################################
#
#   Warn if this version of JATS is not correctly versioned
#   Users should be using correctly versioned copies of JATS. These come
#   from dpkg_archive.
#
Warning ("*** Unofficial version of JATS ***")
    if ( ! $GBE_JATS_SANE && $GBE_NOT_RELEASED );

################################################################################
#   If running with cygwin then warn if the CYGWIN environment variable
#   contains "tty". tty mode causes some strange behaviour such as:
#       1) Non flushing of perl scripts under some conditions (eg:create_dpkg)
#
if ( $ENV{'CYGWIN'} && $ENV{'CYGWIN'} =~ m/tty/ )
{
    Warning ("The CYGWIN variable contains \"tty\".",
             "This can cause strange behaviour with interactive scripts");
}

#
#   Remove CDPATH - this breaks things when cygwin is running
#   even if we are not running under cygwin perl
#
delete $ENV{'CDPATH'};


################################################################################
#   Sanitize GBE_CORE and others for internal Perl use
#   It may contain \ to be added to PATH but within perl is needs /
#
$PERL5LIB =~ tr~\\/~/~s;

TestDirectoryConfig( 'GBE_CORE' );
TestDirectoryConfig( 'GBE_BIN' );
TestDirectoryConfig( 'GBE_PERL' );
TestDirectoryConfig( 'GBE_DPLY' );
TestDirectoryConfig( 'GBE_DPKG' );
TestDirectoryConfig( 'GBE_DPKG_CACHE' );
TestDirectoryConfig( 'GBE_DPKG_LOCAL' );
TestDirectoryConfig( 'GBE_DPKG_STORE' );
TestDirectoryConfig( 'GBE_DPKG_SBOX' );
TestDirectoryConfig( 'GBE_SANDBOX' );

################################################################################
#   Setup the Machine Type
#
#   GBE_MACHTYPE is used to determine the BIN directory from which JATS will
#   pull local binary executables from. The directory must exist.
#
#   We need GBE_MACHTYPE to be correctly defined by the user
#   This is machine specific and should be done in jats.sh/jats.bat
#
unless ( $GBE_MACHTYPE )
{
    $GBE_MACHTYPE = 'win32' if ( $^O eq "cygwin" );
    $GBE_MACHTYPE = 'win32' if ( $^O eq "MSWin32" );
    $GBE_MACHTYPE = 'win32' if ( $^O eq "win95" );
    Verbose ("Setting GBE_MACHTYPE: $GBE_MACHTYPE") ;
}

ReportError ('Set env-var GBE_MACHTYPE (typically "win32")') unless ( $GBE_MACHTYPE );
ErrorDoExit();

if ( $GBE_MACHTYPE eq 'win32' )
{
    $PSPLIT = ';';
    $GBE_UNIX = 0;
}

################################################################################
#   Windows: If the user is running JATS from within the development view
#   then they may not provide a drive letter in the full name of GBE_CORE
#   For correct operation GBE_CORE needs to be able to run programs and
#   scripts from any other drive
#
#   If GBE_CORE does not have a driver letter - then add one
#   Note: Use the CWD before any CD operations
#
if ( $GBE_MACHTYPE eq 'win32'  && $GBE_CORE !~ m/^\w\:/ )
{
        my $cwd = getcwd();
        $GBE_CORE = substr( $cwd, 0, 2 ) . '/' . $GBE_CORE;
        $GBE_CORE =~ s~//~/~g;
        Verbose2 ("Setting GBE_CORE drive: $GBE_CORE");
}

################################################################################
#   Attempt to run JATS from a local cache
#   This mechanism allows JATS to be a link to a desired version on a network
#   drive, but to have the version transferred locally if required
#
#   The transfer/check is only done on the first invocation of jats. If jats uses
#   jats to perform functions, then it will not be performed.
#
#   If we want to run an alternate version of JATS, then don't attempt to
#   cache the initial version of JATS.
#
if ( $ENV{GBE_CACHE_JATS} && ! $GBE_JATS_SANE && ! $GBE_JATS_VERSION)
{
    my $state = "Not Cached: Not running from dpkg_archive";
    #
    #   Must have the DPKG_ARCHIVE cache
    #   Must be running from DPKG_ARCHIVE - prevent messing with local copies
    #
    if ( $GBE_DPKG_CACHE )
    {
        #
        #   JATS must be running from an archive, otherwise we assume that its
        #   a local copy. Detected by:
        #       GBE_NOT_RELEASED being set
        #       Lack of descpkg file
        #
        if ( ! $GBE_NOT_RELEASED  && -f "$GBE_CORE/descpkg" )
        {
            my ($archive, $package) = LocateJatsVersion( $GBE_VERSION );
            if ( $archive )
            {
                Verbose ("Update cached version of JATS: $GBE_VERSION");
                #
                #   Attempt to cache the package
                #   Need enough JATS environment to run the cache_dpkg utility
                #
                {
                    local %ENV = %ENV;

                    $ENV{GBE_TOOLS} = "$GBE_CORE/TOOLS";
                    $ENV{PERL5LIB} = "$GBE_CORE/TOOLS/LIB" ;
                    $ENV{GBE_BIN}  = "$GBE_CORE/BIN.$GBE_MACHTYPE";
                    $ENV{GBE_VERBOSE}  = $GBE_VERBOSE;

                    etool ( 'cache_dpkg.pl', $package );
                }

                my $tgt = "$GBE_DPKG_CACHE/$package";
                if ( -d $tgt )
                {
                    Verbose ("Using cached version of JATS: $GBE_VERSION");
                    $GBE_CORE = $tgt;
                    $state = "Cached";
                }
                else
                {
                    $state = "Not Cached: Copy Failure";
                }
            }
            else
            {
                $state = "Not Cached: Not found in archives";
            }
        }
    }
    else
    {
        $state = "Not Cached: No GBE_DPKG_CACHE";
    }

    #
    #   Flag JATS having been cached on the machine
    #
    $ENV{GBE_CACHE_JATS} = $state;
}


################################################################################
#   Establish a relationship between GBE_BIN, GBE_TOOLS and GBE_CONFIG
#   unless the user has already provided one.
#
#   Only NEED GBE_CORE - the others will be derived
#
unless ( $GBE_BIN )
{
    $GBE_BIN = "$GBE_CORE/BIN.$GBE_MACHTYPE";
    Verbose2 ("Setting GBE_BIN: $GBE_BIN");
}
ReportError ('GBE_MACHTYPE is not valid.',
             'There must be a corresponding BIN directory in GBE_CORE',
             "GBE_BIN     : $GBE_BIN",
             "GBE_MACHTYPE: $GBE_MACHTYPE"
             ) unless ( -d $GBE_BIN );

ReportError ('Machine specific binary directory does not appear to setup',
             'After JATS is installed the PostInstall script must be run',
             "GBE_BIN     : $GBE_BIN"
             ) unless ( -x $GBE_BIN . '/sh' || -x $GBE_BIN . '/sh.exe'  );

unless ( $GBE_TOOLS )
{
    $GBE_TOOLS = "$GBE_CORE/TOOLS";
    Verbose2 ("Setting GBE_TOOLS: $GBE_TOOLS");
}

unless ( $GBE_CONFIG )
{
    $GBE_CONFIG = "$GBE_CORE/CFG";
    Verbose2 ("Setting GBE_CONFIG: $GBE_CONFIG");
}

#
#   Extend the PERL5 with our own Module Repository
#
$PERL5LIB = join( $PSPLIT, split( $PSPLIT, $PERL5LIB), "$GBE_CORE/TOOLS/LIB" );


################################################################################
#   Sanity Test
#   The ABT environment requires several parameters to specify the location
#   of the Release/Deployment Managers
#
#   GBE_DM_LOCATION will be set GBE_RM_LOCATION, if not set
#
#   Only perform the test:
#       - On the first invocation of JATS, not nested invocations
#       - Based on the EnvVar, not the user option. This will allow a user
#         to invoke ABT without the need for these values to be present
#
if ( ! $GBE_JATS_SANE && $ENV{GBE_RM_LOCATION} && ! $ENV{GBE_DM_LOCATION} )
{
    $ENV{GBE_DM_LOCATION} = $ENV{GBE_RM_LOCATION};
}

if ( ! $GBE_JATS_SANE && $ENV{GBE_ABT} )
{
    foreach my $var ( qw(GBE_DM_LOCATION GBE_DM_USERNAME GBE_DM_PASSWORD GBE_RM_URL))
    {
        Warning("Deployment Manager EnvVar may need to be defined: $var") unless ( $ENV{$var} );
    }

    foreach my $var ( qw(GBE_RM_LOCATION GBE_RM_USERNAME GBE_RM_PASSWORD GBE_DM_URL))
    {
        ReportError("Release Manager EnvVar needs to be defined: $var") unless ( $ENV{$var} );
    }
}

################################################################################
#
#   Change to the required root directory
#       The user may specify a start directory( -c )
#
change_dir ( $opt_dir );


################################################################################
#   Locate the root of the build - if required
#   This is used by the autobuild tools to:
#       1) Locate the build.pl file for JATS based builds
#          The name of the target package can be provided to resolve
#          cases of multiple build files.
#
#       2) Locate the <ProjectName>depends.xml file for ANT based builds
#          The name of the buildfile will be specified
#          There must be only one file of this name in the tree
#
#   Note: If only one build file is found, then it will be used
#         It will not be sanity tested. This is intentional
#         for backward compatability.
#
if ( $opt_locate || $opt_locate_file || $opt_locate_package )
{
    #
    #   Allow the user to specify the name of the build file
    #   This will be used in ANT builds as the name changes
    #   but it can be predetermined
    #
    $opt_locate_file = $BUILD_FILE unless ( $opt_locate_file );

    #
    #   Create a Build File Scanner object
    #   This will be used to locate a suitable build file
    #
    Verbose ("Autolocate the build file: $opt_locate_file");

    my $bscanner = BuildFileScanner ( '.', $opt_locate_file );
    my $count = $bscanner->locate();

    Error ("Autolocate. Build file not found: $opt_locate_file" )
        if ( $count <= 0 );

    #
    #   If multiple build files have been found
    #   If $opt_locate_package is set then we can attempt to resolve the package
    #
    #   Scan the buildfiles and determine the names of the packages that will
    #   be built. This can be used to generate nice error messages
    if ( $count > 1 )
    {
        $bscanner->scan();
        $count = $bscanner->match( $opt_locate_package );
#DebugDumpData ("Bscan", \$bscanner );

        my $errmess;
        unless ( $opt_locate_package ) {
            $errmess = "Use -locatepkg=pkg to resolve required package";

        } elsif ( $count <= 0 ) {
            $errmess = "None found that build package: $opt_locate_package";

        } elsif ( $count > 1 ) {
            $errmess = "Multiple build files build the required package: $opt_locate_package";
        }

        #
        #   Pretty error display
        #   Display build directory and the package name (mangled)
        #
        if ( $errmess )
        {
            Error ("Autolocate. Multiple build files found.",
                   $errmess,
                   "Build files found in:", $bscanner->formatData() );
        }
    }

    #
    #   Extract the required build file directory
    #
    my $dir = $bscanner->getMatchDir() || '';
    Verbose ("Autolocate. Found $count build files: $dir");

    #
    #   Select the one true build directory
    #
    change_dir ( $dir );

    #
    #   Kill location scan as we have already located the build file
    #
    $opt_here = 1;
}

################################################################################
#   Version redirection
#   JATS allows the version of JATS to be used to be specified
#   This mechanism operates on the assumption that the required version is
#   present in dpkg_archive. If a specific version is required then the bulk
#   of this script is bypassed and the arguments passed directly to the required
#   target
#
if ( $GBE_JATS_VERSION && $GBE_JATS_VERSION eq $GBE_VERSION )
{
    Message("Using current JATS version: $GBE_JATS_VERSION" );
    $GBE_JATS_VERSION = undef;
}

if ( $GBE_JATS_VERSION )
{
    #
    #   Report any errors detected
    #
    ErrorDoExit();
    Message("Using JATS version: $GBE_JATS_VERSION");

    #
    #   If we have a cache available, then attempt to transfer the required
    #   version into the cache. If we don't have a cache, then don't even try.
    #
    if ( $GBE_DPKG_CACHE )
    {
        #
        #   Attempt to locate the desired version in one of the well known
        #   package archives. This will give us its package name.
        #
        my ($archive, $package) = LocateJatsVersion ($GBE_JATS_VERSION);
        Error("Cannot find JATS version: $GBE_JATS_VERSION") unless $archive;

        #
        #   Do NOT propagate GBE_JATS_VERSION in the environment
        #   This will only cause problem in recursion
        #
        delete $ENV{GBE_JATS_VERSION};

        #
        #   Attempt to cache the package
        #   Need enough JATS environment to run the utility
        #
        {
            local %ENV = %ENV;

            $ENV{PERL5LIB} = $PERL5LIB;
            $ENV{GBE_BIN}  = $GBE_BIN;
            $ENV{GBE_VERBOSE}  = $GBE_VERBOSE;
            $ENV{GBE_TOOLS}  = $GBE_TOOLS;

            etool ( 'cache_dpkg.pl', $package );
        }
    }

    #
    #   Locate the version of JATS to be run (again)
    #   It should be in the cache, but it may not be
    #
    my ($archive, $package) = LocateJatsVersion ($GBE_JATS_VERSION);
    Error("Cannot find JATS version: $GBE_JATS_VERSION") unless $archive;
    
    $GBE_CORE = "$archive/$package";
    Verbose2 ("Using alternate version: $GBE_CORE");

    #
    #   Run the specified version of JATS
    #   Force the required version of GBE_CORE and invoke the wrapper script
    #
    $ENV{GBE_CORE} = $GBE_CORE;

    Verbose("Use ARGV: @ARGV");
    $RESULT = System ($GBE_PERL, "$GBE_CORE/TOOLS/jats.pl", @ARGV );
    do_exit();
}

################################################################################
#   Determine the current directory
#   Note: Don't use `pwd`. This sucks for so many reasons including
#         the fact that it may not be available until this wrapper
#         script has done it job.
#
$CWD = getcwd();
Verbose ("Current Working Directory: $CWD");
Warning ("Current working directory contains spaces") if ( $CWD =~ m/\s/ );


################################################################################
#   Setup drive
#   This is only required under windows
#   Purpose is unclear, but under windows it is required, so lets create one
#   if its not present
#
unless ( $GBE_UNIX )
{
    unless ( $GBE_DRV )
    {
        ($GBE_DRV = $CWD ) =~ s~[^:]*$~~;
        $GBE_DRV = "c:" if ( $GBE_DRV !~ m/:/ );
        Verbose2( "Setting GBE_DRV: $GBE_DRV" );
    }
}

################################################################################
#   Sanity test the main archive variable
#   This MUST address one archive
#

Error ("GBE_DPKG must not be a path list: $GBE_DPKG")
    if ( $GBE_DPKG =~ /$PSPLIT/ );
Error ("GBE_DPKG is not a directory : $GBE_DPKG")
    unless( -d $GBE_DPKG );

################################################################################
#   Sanity test the cache
#
Error ("GBE_DPKG_CACHE is not a directory : $GBE_DPKG_CACHE")
    if ( $GBE_DPKG_CACHE && ! -d $GBE_DPKG_CACHE );

################################################################################
#   Sanity test the global store
#
Error ("GBE_DPKG_STORE is not a directory : $GBE_DPKG_STORE")
    if ( $GBE_DPKG_STORE && ! -d $GBE_DPKG_STORE );

Error ("GBE_DPLY is not a directory : $GBE_DPLY")
    if ( $GBE_DPLY && ! -d $GBE_DPLY );
    
########################################################################
#
#       Locate a local_dpkg_archive directory, by searching from the CWD
#       to the root of the file system
#
unless ( $GBE_DPKG_LOCAL )
{
    ($GBE_DPKG_LOCAL) = scan_for_dir ('local_dpkg_archive');
}
else
{
    Error ("GBE_DPKG_LOCAL is not a directory : $GBE_DPKG_LOCAL")
        unless( -d $GBE_DPKG_LOCAL );
}

########################################################################
#
#       Locate a sandbox_dpkg_archive directory by searching from the CWD
#       to the root of the file system
#
#       sandbox_dpkg_archive is a dpkg_archive for packages that are being
#       co-developed. Package Versions within the sandbox are ignored
#

#
#   Ensure that the user does not set $GBE_SANDBOX or $GBE_DPKG_SBOX
#   $GBE_JATS_SANE will be set for multiple invocations, thus it is cleared
#   for the first one (unless the user messes with it).
#
unless ( $GBE_JATS_SANE )
{
    Error ("GBE_SANDBOX must not be set by the user") if ( $GBE_SANDBOX );
    Error ("GBE_DPKG_SBOX must not be set by the user") if ( $GBE_DPKG_SBOX );

    #
    #   The ABT does not use the sandbox
    #   It will never be found and will be ignored
    #
    unless ( $GBE_ABT )
    {
        ($GBE_DPKG_SBOX,$GBE_SANDBOX)  = scan_for_dir ('sandbox_dpkg_archive');
    }
}

########################################################################
#
#   Ensure that the user has been set
#   Under windows USER may not be set, but USERNAME may be
#
$USER = $ENV{ 'USERNAME' } || '' unless ( $USER );
$USER = $ENV{ 'LOGNAME' }  || '' unless ( $USER );

################################################################################
#   Sanitize the PATH variable
#   Some of the Win32 binary tools used by JATS cannot handle lower case Path
#   Force an upper case PATH
#
my $PATH = '';
$PATH .= delete( $ENV{ 'Path' } ) if (exists $ENV{ 'Path' });
$PATH .= delete( $ENV{ 'path' } ) if (exists $ENV{ 'path' });
$PATH .= delete( $ENV{ 'PATH' } ) if (exists $ENV{ 'PATH' });
$ENV{'PATH'} = $PATH;

#
#   Windows now defines ComSpec. This is only a problem when programs are
#   started up without the use of cmd.exe - like the JATS internal sh.exe
#   Correct by deleting the environment variable and forcing it to be uppercase
#
my $COMSPEC = '';
$COMSPEC = delete( $ENV{'COMSPEC'} ) if ( exists $ENV{'COMSPEC'} );

################################################################################
#   There is some really ugly interaction between Cygwin, ActiveState Perl 5.8.2
#   and xmake. Even if none of the cygwin bits are used within JATS the fact that
#   Cygwin is in the path causes problems.
#
#   Problems seen:
#       1) "jats build"     xmake fails with a segment fault. Possibly when
#                           displaying an error message
#       2) Warnings and messages generated from within Perl don't always appear
#          In particular. The output from a Message() within a PLATFORM/xxx file
#          does not get displayed.
#
#   Solution:
#       Remove cygwin from the PATH
#
$PATH =~ s~c:\\cygwin[^;]*;~~ig;

################################################################################
#   Ensure that the PATH contains the PERL executable
#   Need to true path to the PERL executable so that the user has access to some
#   of the perl utility programs such as pod2html
#
$PATH = $Config{binexp} . $PSPLIT . $PATH;

################################################################################
#   There is more ugliness if GBE_BIN is not in the users path
#   I suspect that it something within xmake (under win32) and that it needs
#   to be able to find sh within the path - even though its fully pathed
#
#   Add GBE_BIN to the start of the path to assist in searching
#   Also ensure that we pickup our version of utilities instead of random
#   versions.
#
my $GBE_BIN_ADD = $GBE_BIN;
$GBE_BIN_ADD =~ s~/~\\~g unless ( $GBE_UNIX );
$PATH = $GBE_BIN_ADD . $PSPLIT . $PATH;

################################################################################
#   Clean PATH
#       Remove duplicates
#       Remove empty elements
#       Clean path endings
#       Place non-existent paths at the end. They will be seen, but not scanned
#
{
    my @new_path;
    my @non_exist;
    my %seen;
    foreach ( split $PSPLIT, $PATH )
    {
        s~[/\\]+$~~;                                # Remove trailing / or \
        my $name = ( $GBE_UNIX ) ? $_ : lc ($_);    # Windows is case insensitive
        next unless ( $_ );                         # Remove empty elements
        next if ( /^\.+$/ );                        # Remove . and ..
        next if ( exists $seen{$name} );            # Remove duplicates
        if ( -d $_ ) {
            push @new_path, $_;                     # Exists
        } else {
            push @non_exist, $_;                    # Place non existent paths at the end
        }
        $seen{$name} = 1;
    }
    $PATH = join( $PSPLIT, @new_path, @non_exist );
}

################################################################################
#   Windows: Ensure that cmd.exe is in the users PATH
#            If cmd.exe cannot be found then the 'system' command will not work
#
unless ( $GBE_UNIX )
{
    my $cmd_found;
    foreach ( split $PSPLIT, $PATH )
    {
        my $file = $_ . "/cmd.exe";
        Verbose2 ("Look for: $file");
        if ( -x $file  )
        {
            $cmd_found = 1;
            Verbose ("Found: $file");
            last;
        }
    }

    Warning( "Users PATH does not contain \"cmd.exe\"",
             "System commands may not work" ) unless $cmd_found;
}

################################################################################
#   Sanitize the Microsoft Visual Studio environment variables LIB and INCLUDE.
#   If these have a trailing \ then the generated makefiles
#   will fail. This is impossible to detect and fix within make so do it here
#
#   Note: JATS no longer allows these environment variables through to the
#         makefiles.
#
unless ( $GBE_UNIX )
{
    for my $var qw ( LIB INCLUDE )
    {
        my $evar = $ENV{$var};
        if ( $evar )
        {
            $evar =~ s~\\;~;~g;         # Remove trailing \ from components \; -> ;
            $evar =~ s~\\$~~;           # Remove trailing \
            $evar =~ s~;$~~;            # Remove trailing ;
            $evar =~ s~;;~;~g;          # Remove empty items ;;
            $ENV{$var} = $evar;
        }
    }
}

################################################################################
#   Setup the default JAVA_HOME
#   User should specify 1.4, 1.5,1.6 ....
#
$JAVA_HOME = get_java_home ($opt_java)
    if ( $opt_java );


################################################################################
#   Update the environment variables used by JATS, unless requested otherise.
#
#   If JATS is being used to startup build daemons, then we don't want to
#   export many of the calculated values into the environment. In particular
#   GBE_CORE, GBE_BIN, GBE_CONFIG and GBE_TOOLS must not be exported as it will
#   prevent the daemon from picking up the 'current' version of JATS
#
#

if ( $opt_export_vars )
{
    $ENV{'PATH'}              = $PATH;
    $ENV{'GBE_VERSION'}       = $GBE_VERSION;
    $ENV{'GBE_CORE'}          = $GBE_CORE;
    $ENV{'GBE_BIN'}           = $GBE_BIN;
    $ENV{'GBE_CONFIG'}        = $GBE_CONFIG;
    $ENV{'GBE_DPLY'}          = $GBE_DPLY;
    $ENV{'GBE_DPKG'}          = $GBE_DPKG;
    $ENV{'JATS_HOME'}         = $GBE_DPKG;
    $ENV{'GBE_DPKG_CACHE'}    = $GBE_DPKG_CACHE;
    $ENV{'GBE_DPKG_STORE'}    = $GBE_DPKG_STORE;
    $ENV{'GBE_DPKG_LOCAL'}    = $GBE_DPKG_LOCAL;
    $ENV{'GBE_SANDBOX'}       = $GBE_SANDBOX;
    $ENV{'GBE_DPKG_SBOX'}     = $GBE_DPKG_SBOX;
    $ENV{'GBE_PERL'}          = $GBE_PERL;
    $ENV{'GBE_TOOLS'}         = $GBE_TOOLS;
    $ENV{'GBE_MACHTYPE'}      = $GBE_MACHTYPE;
    $ENV{'GBE_HOSTMACH'}      = $GBE_HOSTMACH;
    $ENV{'GBE_PLATFORM'}      = $GBE_PLATFORM;
    $ENV{'GBE_DRV'}           = $GBE_DRV;
    $ENV{'PERL5LIB'}          = $PERL5LIB;
    $ENV{'JAVA_HOME'}         = $JAVA_HOME if ($JAVA_HOME);
    $ENV{'GBE_JATS_SANE'}     = 1;
}
else
{
    #
    #   Delete, from the environment, values that are related to selecting
    #   the version of JATS being used
    #
    foreach ( qw( GBE_VERSION GBE_CORE GBE_BIN GBE_CONFIG GBE_TOOLS GBE_DRV PERL5LIB GBE_DPKG_SBOX GBE_SANDBOX GBE_PLATFORM GBE_JATS_SANE ) )
    {
        delete $ENV{$_};
    }
}

#
#   The following don't affect the operation of the build daemons selecting
#   the current version of JATS. Some need to be passed through anyway
#
$ENV{'GBE_BUILDFILTER'}   = $GBE_BUILDFILTER;
$ENV{'GBE_ABT'}           = $GBE_ABT if ($GBE_ABT);
$ENV{'GBE_DEBUG'}         = $GBE_DEBUG;
$ENV{'GBE_VERBOSE'}       = $GBE_VERBOSE;
$ENV{'GBE_UNIX'}          = $GBE_UNIX;
$ENV{'USER'}              = $USER;
$ENV{'COMSPEC'}           = $COMSPEC;
$ENV{'GBE_HOSTNAME'}      = $GBE_HOSTNAME;

#-------------------------------------------------------------------------------
# Function        : LocateJatsVersion
#
# Description     : Scan archives looking for a specified version of JATS
#                   Complicated by the name change from core_devl to jats
#                   that occurred circa version 2.71.7.
#                   The required version may be in either of the packages
#
# Inputs          : $version            - Version to locate
#
# Returns         : undef               - if not found
#                   Array of:
#                       Repository
#                       package/version
#
sub LocateJatsVersion
{
    my ($version) = @_;
    my $package;
    my $path;

    foreach my $archive qw ( GBE_DPKG_LOCAL GBE_DPKG_CACHE GBE_DPKG GBE_DPKG_STORE )
    {
        no strict 'refs';
        $path = ${$archive};
        use strict 'refs';
        next unless ( $path );

        foreach my $package qw( jats core_devl )
        {
            Verbose2( "ExamineDir: $path/$package/$version" );
            if ( -d "$path/$package/$version" )
            {
                return $path, "$package/$version";
            }
        }
    }
    return;
}


#-------------------------------------------------------------------------------
# Function        : TestDirectoryConfig
#
# Description     : Sanity test a user configurable directory or file
#                   Must not contain spaces
#                   Must not be a network drive ie: //computer
#
# Inputs          :
#
# Returns         :
#
sub TestDirectoryConfig
{
    my ($dir) = @_;

    no strict 'refs';
    my $val = ${$dir};

    if ( $val )
    {
        #
        #   Cleanup the path
        #
        $val =~ tr~\\/~/~s;
        $val =~ s~/+$~~;
        $val = '' if ( $val eq '-' || $val eq 'none' );
        ${$dir} = $val;

        ReportError("$dir path cannot contain spaces: $val") if ( $val =~ m/\s/ );
        ReportError("$dir path cannot be a computer name: $val") if ( $val =~ m~^//~  );
    }
    use strict 'refs';
    return;
}

#-------------------------------------------------------------------------------
# Function        : change_dir
#
# Description     : change directory to the specified directory
#
# Inputs          : $1      - Target directory
#
# Returns         :
#
sub change_dir
{
    my ($dir) = @_;

    if ( $dir && $dir !~ m/^\.$/ )
    {
        Verbose ("Changing to JATS build directory: $dir");
        chdir $dir || Error ("Bad change directory: $dir");
    }

    #
    #   Reset the CWD
    #   Note: Don't use `pwd`. This sucks for so many reasons including
    #         the fact that it may not be available until this wrapper
    #         script has done it job.
    #
    $CWD = getcwd();
    $CWD =~tr~\\/~/~s;
    Warning ("Current working directory contains spaces") if ( $CWD =~ m/\s/ );
    return;
}

#-------------------------------------------------------------------------------
# Function        : scan_for_dir
#
# Description     : Scan from the current directory up to the root
#                   of the current file system looking for a named
#                   directory
#
# Inputs          : $target                 - Directory to locate
#
# Returns         : full_path               - Path of dir
#                   full_dir                - Containng dir
#

sub scan_for_dir
{
    my ($target) = @_;
    Verbose2 ("Scan for $target");

    my $test_dir = $CWD;
    {
        do
        {
            #
            #   Stop at /home to prevent unix automounter spitting
            #   lots of error messages
            #
            last if ( $test_dir =~ m~/home$~ );
            Verbose2 ("Testing: $test_dir");

            my $test_path = $test_dir . '/' . $target;
            if ( -d $test_path )
            {
                Verbose ("Found $target: $test_path");
                return $test_path, $test_dir;
            }
            #
            #   Remove one directory
            #   Terminate the loop when no more can be removed
            #
        } while ( $test_dir =~ s~[/][^/]*$~~ );
    }
    return '','';
}

#-------------------------------------------------------------------------------
# Function        : get_java_home
#
# Description     : Convert user java option into a full path,or die
#
# Inputs          : User option
#
# Returns         : Full path
#
sub get_java_home
{
    my ($java) = @_;
    my $jv = "JAVA_HOME_$java";
    $jv =~ s~\.~_~g;

    unless ( exists($ENV{$jv}) )
    {
        Error ("Unknown JAVA version: $java",
               "Looking for EnvVar: $jv",
               "Example Usage: -java=1.5");
    }
    my $rv = $ENV{$jv};
    unless ( -d $rv )
    {
        Error ("Java home path not found: $jv",
               "Looking for: $rv" );
    }
    return $rv;
}

#-------------------------------------------------------------------------------
# Function        : get_version
#
# Description     : Return the version of JATS being run
#                   JATS should be run from a "package"
#                   The package should have a descpkg file
#                   Use this file
#
# Inputs          :
#
# Returns         : A string
#
sub get_version
{
    #
    #   The version number is embedded into this script by the release process
    #   The text [V]ERSION_TAG will be replaced by the real version number
    #   If this has not occurred then we know that the release is not official
    #   Need to be a little bit careful about the tag name
    #
    return ("Unreleased Version. Version tag has not been set")
        if ( $GBE_NOT_RELEASED );

    return "$GBE_VERSION [ Internal. Not an installed package ]"
        if ( ! -f "$GBE_CORE/descpkg" );

    my $rec;
    return $rec->{'VERSION_FULL'}
        if ($rec = ReadDescpkg ( "$GBE_CORE/descpkg" ) );

    return "ERROR";
}

#-------------------------------------------------------------------------------
# Function        : print_version
#
# Description     :
#
# Inputs          :
#
# Returns         :
#
sub print_version
{
    #
    #   Allow user to specify verboseness as an argument
    #
    foreach  ( @_ )
    {
        $GBE_VERBOSE++ if ( m/^-v/ );
    }

    Message get_version();
    Message "Internal: $GBE_VERSION" if ($GBE_VERBOSE);
    $opr_done = 1;
    return;
}


#-------------------------------------------------------------------------------
#
#   Give the user a clue
#
sub help
{
    my ($level) = @_;
    $level = $opt_help unless ( $level );

    pod2usage(-verbose => 0, -message => "Version: ". get_version())  if ($level == 1 );
    pod2usage(-verbose => $level - 1 );
}

#-------------------------------------------------------------------------------
#
#   Find a JATS build directory
#   Find a JATS build directory based on file $1
#
sub find_jats_dir
{
    #
    #   Autodetect suppressed ?
    #
    return if ( $opt_here );

    my (@FILES) = @_;
    push @FILES, @BUILD_FILE_ALT;
    
    my @SEARCH = qw( . .. ../.. ../../.. );

    #
    #   Locate the JATS build files
    #   Allow the user to be in a number of parent directories
    #
    for my $ROOTDIR (@SEARCH)
    {
        for my $SUBDIR ( '', '/jats', '/build', '/build/jats' )
        {
            my $DIR = $ROOTDIR . $SUBDIR;
            next unless -d $DIR;

            for my $FILE ( @FILES)
            {
                my $check_file = $DIR . '/' . $FILE;
                Verbose2 ("Check for: $check_file");
                if ( -f $check_file )
                {
                    change_dir ( $DIR );
                    Verbose2 ("Found: $check_file");
                    return;
                }
            }
        }
    }
    Error ("JATS directory not found. Cannot locate: @FILES");
}

#-------------------------------------------------------------------------------
#
#   Kick off the build process
#
sub build
{
    my $build_file = $BUILD_FILE;
    (my $name = $CWD) =~ s~^.*/~~ ;
    $opr_done = 1;

    find_jats_dir($build_file);
    Message ("=== Building $name ===");

    #
    #   Use an alternative buildfile - if it exists
    #   Do not use this file if the user has specified a buildfile
    #
    unless ( $BUILD_FILE_SET )
    {
        Verbose ("Search for alternate build file");
        foreach my $test_file ( @BUILD_FILE_ALT )
        {
            Verbose2 ("Search for alternate build file: $test_file");
            if ( -f $test_file )
            {
                $build_file = $test_file;
                Message ("=== USING ALTERNATE BUILDFILE: $build_file ===");
                last;
            }
        }
    }

    #
    #   Jats/make does not handle file systems with spaces in the path names
    #
    Error('$CWD path cannot contain spaces') if ( $CWD =~ m/\s/ );

    $RESULT = System ($GBE_PERL, $build_file, $CWD, "$GBE_TOOLS/buildlib.pl", "--project", @_ );

    Message ("=== Build $name NOT complete ===") if     ( $RESULT  );
    Message ("=== Build $name complete ===")     unless ( $RESULT );
    return $RESULT
}

#-------------------------------------------------------------------------------
# Function        : bmake_it
#
# Description     : Combo build and make operations
#
# Inputs          : ...     - COMMANDS or Make arguments.
#                             Key commands are BUILD and INSTALL
#
# Returns         : RESULT code
#
sub bmake_it
{
    my @make_args = @_;
    my $all = 1;
    my $msg;
    $opr_done = 1;

    if ( $make_args[0] && $make_args[0] eq 'NOTALL' )
    {
        $all = 0;
        shift @make_args;
    }
    elsif ( $make_args[0] && $make_args[0] eq 'BUILD' )
    {
        Verbose ("Makeit build") ;
        $msg = "Component not built";
        build();
        shift @make_args;
    }

    unless ( $RESULT )
    {
        push @make_args, '-default=all' if ( $all );
        Verbose ("Makeit make @make_args") ;

        find_jats_dir( "makefile.pl", "Makefile.gbe", $BUILD_FILE );
        Error ("No Makefile.gbe file found") unless ( -f 'Makefile.gbe' );

        etool ( 'jmake.pl', @make_args );
        $msg = "Component not made";
        @make_args = ();
    }


    Verbose ("Makeit Result: $RESULT") ;
    Error ( $msg ) if ( $RESULT );
    return  if ( $RESULT );
}

#-------------------------------------------------------------------------------
# Function        : dev_expand
#
# Description     : Expand the users arguments to the "debug/prod" command
#                   "debug/prod" builds and packages debug stuff
#
#                   Ignore make options.
#
# Inputs          : target
#                   Argument list
#
# Returns         : expanded argument list
#
sub dev_expand
{
    my @resultd;
    my @resultp;
    my @args;
    my @opts;

    #
    #   Remove options from the argument list
    #
    foreach ( @_ )
    {
        if ( m~=~ || m~^-~ ) {
            push @opts, $_;
        } else {
            push @args, $_;
        }
    }

    my $target = shift @args;
    my @cmd = $target;
    if ( $#args < 0 )
    {
        push @cmd, "package_$target";
    }
    else
    {
        foreach ( @args )
        {
            push @resultd, $_ . "_$target";
            push @resultp, $_ . "_package_$target";
            @cmd = ();
        }
    }

    return (@cmd, @resultd, @resultp, @opts);
}

#-------------------------------------------------------------------------------
# Function        : install_pkg
#
# Description     : Install the built package into local_dpkg_archive
#                   This will make it available for use, without populating the
#                   global archive
#
#                   This is done through an external utility to maintain
#                   consistent interface
#
sub install_pkg
{

    find_jats_dir($BUILD_FILE, "build.xml" );
    etool ( 'create_dpkg.pl', '-archive=local', '-quiet', '-override',  @_ );
}

#-------------------------------------------------------------------------------
# Function        : create_dpkg
#
# Description     : Install a package into the main dpkg_archive
#                   This is done through an external utility to maintain
#                   consistent interface
#
#                   This function is simply an easy way to access the utility

sub create_dpkg
{
    find_jats_dir($BUILD_FILE, "build.xml" );
    etool ( 'create_dpkg.pl',  @_ );
}

#-------------------------------------------------------------------------------
# Function        : etool
#
# Description     : Invoke an external tool program written in PERL
#
# Arguments       : $1  Name of the program to run
#                       With optional .pl suffix
#                   $2+ Program arguments
#
sub etool
{
    my $command = shift;
    my $cmd;
    my @etool_path = ( "$ENV{'GBE_TOOLS'}",
                       "$ENV{'GBE_TOOLS'}/DEPLOY",
                       "$ENV{'GBE_TOOLS'}/LOCAL" );
    if ( $command )
    {
        #
        #   Locate a potential tool
        #   These will have the user name or a .pl extension
        #
        ETOOL_SEARCH:
        foreach my $ext ( '', '.pl' )
        {
            foreach my $dir ( @etool_path )
            {
                $cmd = "$dir/$command$ext";
                last ETOOL_SEARCH if ( -f $cmd );
            }
            $cmd='';
        }

        Error ("Tool not found: $command", "Search path:", @etool_path) unless $cmd;
        $RESULT = System ( $GBE_PERL, $cmd, @_ );
    }
    else
    {
        #
        #   Display on the .pl commands
        #
        display_commands("Available Tools", \@etool_path, ['*.pl'] );
    }

    $opr_done = 1;
}

#-------------------------------------------------------------------------------
# Function        : ebin
#
# Description     : Invoke an external JATS provided binary
#                   within the JATS toolset
#
# Arguments       : $1  Name of the program to run
#                   $2+ Program arguments
#
sub ebin
{
    my $command = shift;
    my $cmd;
    my @ebin_path = ( "$GBE_BIN" );

    if ( $command )
    {
        #
        #   Locate a potential executable
        #   This
        #
        ETOOL_SEARCH:
        foreach my $ext ( '', '.exe', '.sh' )
        {
            foreach my $dir ( @ebin_path )
            {
                $cmd = "$dir/$command$ext";
                last ETOOL_SEARCH if ( -f $cmd );
            }
            $cmd='';
        }

        Error ("Program not found: $command", "Search path:", @ebin_path) unless $cmd;
        $RESULT = System ( $cmd, @_ );
    }
    else
    {
        #
        #   Display a list of programs
        #
        display_commands("Available Programs", \@ebin_path, ['*'] );
    }

    $opr_done = 1;
}

#-------------------------------------------------------------------------------
# Function        : eprog
#
# Description     : Invoke an external program
#                   Will detect local .pl files and execute them
#                   Will detect local .jar files and execute them
#                   Will detect local .bat files and execute them
#
# Arguments       : $1  Name of the program to run
#                   $2+ Program arguments
#
sub eprog
{
    Verbose ("eprog: @_");
    my $name = shift @_;
    Error ("eprog. No program specified") unless ( $name );

    $name .= ".pl"     if ( -f "${name}.pl" );
    $name .= ".jar"    if ( -f "${name}.jar" );
    $name .= ".bat"    if ( -f "${name}.bat" );

    #
    #   On Windows programs in the CWD will be found
    #   Mimic this behaviour on Unix
    #
    $name = "./$name" if ( $name !~ m~/~ && -f "./$name");

    if ( $name =~ m~\.pl$~ ) {
        $RESULT = System ( $GBE_PERL, $name, @_ );

    } elsif ( $name =~  m~\.jar$~ ) {
        $RESULT = System ( "$JAVA_HOME/bin/java", '-jar', $name, @_);

    } else {
        #
        #   Ensure .bat files are pathed with \, and not / characters
        #   The windows command interpreter does not like /
        #
        $name =~ s~/~\\~g if ( $name =~ m~\.bat$~ );

        $RESULT = System ( $name, @_ );
    }

    $opr_done = 1;
}

#-------------------------------------------------------------------------------
# Function        : display_commands
#
# Description     : Display a list of commands from a specified list of dirs
#                   Internal helper function
#
# Inputs          : $title      - Display header
#                   $ref_path   - Ref to an array that contains the search path
#                   $ref_ext    - Ref to an array of valid patterns
#
# Returns         : Nothing
#
sub display_commands
{
    my ( $title, $ref_path, $ref_ext ) = @_;

    #
    #   Display a list of commands
    #
    my %list;
    foreach ( @$ref_path )
    {
        foreach my $ext ( @$ref_ext )
        {
            foreach my $file (  glob( "$_/$ext") )
            {
                $file =~ s~.*/~~ unless $GBE_VERBOSE;
                $list{$file} = 1;
            }
        }
    }

    my $count = 0;
    my $limit = $GBE_VERBOSE ? 1 : 3;
    print "$title:\n";
    foreach ( sort keys %list )
    {
        printf "%-26s", $_;
        print "\n" if ( !( ++$count % $limit) );
    }
}

#-------------------------------------------------------------------------------
# Function        : run_ant
#
# Description     : Invoke ant
#                   If the current directory looks like an ERG build system, then
#                   create and maintain an auto.xml file. Otherwise simply invoke ant
#
# Inputs          : $1+ program arguments
#
sub run_ant
{
    my $JAVA_HOME = $ENV{JAVA_HOME} || Error ("JAVA_HOME is not defined in the environment");
    my $ANT_HOME  = $ENV{ANT_HOME}  || Error ("ANT_HOME is not defined in the environment" );

    #
    #   Detect an ERG formatted build
    #   This will have two files <projectname>.xml and <projectname>depends.xml
    #   Create the 'auto.xml' file only if its not present
    #
    my @ant_arg;
    my @flist;
    my $basename = '';
    my $scanner = '*depends.xml';

    #
    #   Use specified build file to resolve multiple names
    #   Strip any trailing depends.xml to make it user friendly
    #
    if ( $BUILD_FILE_SET )
    {
        $basename = $BUILD_FILE;
        $basename =~ s~\.xml$~~;
        $basename =~ s~depends$~~;
        $scanner = "${basename}depends.xml";
        Verbose ("Using buildfile: $basename");
    }

    my @buildlist = glob($scanner);
    foreach ( @buildlist )
    {
        if ( m/(.+)depends.xml/ )
        {
            my $pname = $1;
            push @flist, $pname if ( -f "$pname.xml" );
        }
    }

    if ( $#flist >= 0 )
    {
        Error ("Multiple depends.xml files found:", @flist,
               "Use -buildfile=name to resolve") if ( $#flist > 0 );

        my $depend = "$flist[0]depends.xml";
        @ant_arg = ('-f', "$flist[0].xml");

        Message ("Ant using projectfiles: $flist[0].xml");

        #
        #   Check for depends.xml newer than auto.xml.
        #
        my $auto_timestamp   = (stat('auto.xml'))[9] || 0;
        my $depend_timestamp = (stat($depend))[9];
        if ( $depend_timestamp > $auto_timestamp )
        {
            Message ("Creating: auto.xml");
            copy( $depend, 'auto.xml' );
            chmod 0777, 'auto.xml';
        }
    }
    elsif ( $BUILD_FILE_SET  )
    {
        Error ("Specified build file pair not found:", $basename, $basename . "depends.xml");
    }

    #
    #   The ant provided startup scripts don't return an exit code under
    #   windows. Invoke ant directly
    #
    launch_ant ( $JAVA_HOME, $ANT_HOME, @ant_arg, @_ );
    $opr_done = 1;
}

#-------------------------------------------------------------------------------
# Function        : run_abt
#
# Description     : Invoke auto build tool (older form)
#                   Options for the ABT
#                       --Java=x.x
#                   Invoke ANT for the ABT using a specified version of Java
#                   Do not play with the user environment.
#
# Inputs          : $1+ program arguments
#
sub run_abt
{
    my $ABT_JAVA = 'JAVA_HOME_1_6';         # Default version for the ABT
    my $JAVA_HOME = $ENV{$ABT_JAVA};
    my $ANT_HOME  = $ENV{ANT_HOME};
    my @abt_arg;
    my $buildfile = 'build.xml';

    ErrorConfig( 'name'    => 'JATS ABT' );

    #
    #   Use the user specified buildfile
    #
    $buildfile = $BUILD_FILE
        if ( $BUILD_FILE_SET );

    #
    #   Extract known options
    #
    foreach  ( @_ )
    {
        if ( m/-java=(.*)/ ) {
            $JAVA_HOME = get_java_home($1);

        } elsif ( m/^-buildfile=(.+)/ ) {
            $buildfile = $1;

        } else {
            push @abt_arg, $_;
        }
    }

    Error ("$ABT_JAVA is not defined in the environment") unless $JAVA_HOME;
    Error ("ANT_HOME is not defined in the environment" ) unless $ANT_HOME;
    Error ("Ant buildfile not found: $buildfile" ) unless (-f $buildfile);

    #
    #   Insert correct build file arguments
    #
    push @abt_arg, '-buildfile', $buildfile;

    #
    #   Add current directory as java library directory, but only if
    #   it contains JAR files.
    #
    push @abt_arg, '-lib', $CWD
        if ( glob ('*.jar') );
    
    #
    #   Use the ant-launcher to invoke ant directly
    #
    launch_ant ( $JAVA_HOME, $ANT_HOME, @abt_arg );
    $opr_done = 1;
}

#-------------------------------------------------------------------------------
# Function        : launch_ant
#
# Description     : Start ANT - with sanity checking
#
# Inputs          : JAVA_HOME
#                   ANT_HOME
#                   @user_args
#
# Returns         : Result Code
#
sub launch_ant
{
    my ($JAVA_HOME, $ANT_HOME, @user_args ) = @_;

    Error ("Directory not found: $JAVA_HOME") unless ( -d "$JAVA_HOME" );
    Error ("Program not found: $JAVA_HOME/bin/java") unless ( -e "$JAVA_HOME/bin/java" || -e "$JAVA_HOME/bin/java.exe" );
    Error ("Jar not found: $ANT_HOME/lib/ant-launcher.jar") unless ( -e "$ANT_HOME/lib/ant-launcher.jar" );
    Error ("Directory not found: $ANT_HOME") unless ( -d "$ANT_HOME" );
    Error ("Directory not found: $ANT_HOME/lib") unless ( -d "$ANT_HOME/lib" );

    #
    #   Use the ant-launcher to invoke ant directly
    #
    $RESULT = System ( "$JAVA_HOME/bin/java",
                       "-classpath","$ANT_HOME/lib/ant-launcher.jar",
                       "-Dant.home=$ANT_HOME",
                       "org.apache.tools.ant.launch.Launcher",
                       "-lib","$ANT_HOME/lib",
                       @user_args
                       );
    
   return $RESULT;
}


#-------------------------------------------------------------------------------
#
#   Cleanup the sandbox
#   Perform a "make clobber" then a "build clobber"
#
sub clobber
{
    Message ("=== Removing ======");
    find_jats_dir( $BUILD_FILE );

    #
    #   Run a "make clobber" to clean out "everything"
    #
    etool ( 'jmake.pl', 'clobber' )
        if ( -f "Makefile.gbe" );

    if ( -f $BUILD_FILE )
    {
        System ( "$GBE_PERL $BUILD_FILE $CWD $GBE_TOOLS/buildlib.pl clobber" );
    }
    else
    {
        Error ("Cannot clobber. No buildfile found");
    }

    Message ("=== Remove complete ===");
    $opr_done = 1;
}

#-------------------------------------------------------------------------------
#
#   Dumpout relevant parts of the environment
#
sub dump_vars
{
    $opr_done = 1;

    #
    #   Allow user to specify verboseness as an argument
    #
    foreach  ( @_ )
    {
        $GBE_VERBOSE++ if ( m/^-v/ )
    }

    #   Display an array separated list, one entry per line, with dir sanity check
    sub alist
    {
        my ($text, @var) = @_;
        my $sep = "=";
        for ( @var )
        {
            my $valid = ( -d $_ || -f $_ ) ? " " : "*";
            printf "%-17s%s%s%s\n", $text, $sep, $valid, $_;
            $text = "";
            $sep = " ";
        }
    }
    
    #   Display a ';' or ':' separated list, one entry per line
    sub dlist
    {
        my ($text, $var) = @_;
        alist( $text, split $PSPLIT, $var || " " );
    }

    # Simple print of name and variable
    sub pvar
    {
        my ($text, $data) = @_;
        printf "%-17s= %s\n", $text, $data;
    }

    # Print of name and variable of directory with sanity check
    sub dvar
    {
        my ($text, $data) = @_;
        my $valid = ( ! $data || -d $data || -f $data ) ? " " : "*";
        printf "%-17s=%s%s\n", $text, $valid, $data;
    }

    # Display a variable extracted from the environment
    sub penv
    {
        my ($var) = @_;
        pvar $var, $ENV{$var} || '';
    }

    # Display a variable extracted from the environment with dir sanity check
    sub denv
    {
        my ($var) = @_;
        dvar $var, $ENV{$var} || '';
    }

    #
    #   Complete environment dump
    #
    if ( $GBE_VERBOSE > 1 )
    {
        Message ("Complete environment dump");
        foreach my $var ( sort keys(%ENV) )
        {
            penv  $var;
        }
        return;
    }

    pvar    "Version"         , get_version();
    penv    "GBE_SCRIPT";
    pvar    "GBE_DEBUG"       , $GBE_DEBUG;
    pvar    "GBE_VERBOSE"     , $GBE_VERBOSE;
    pvar    "GBE_MACHTYPE"    , $GBE_MACHTYPE;
    pvar    "GBE_HOSTMACH"    , $GBE_HOSTMACH;
    pvar    "GBE_UNIX"        , $GBE_UNIX;
    pvar    "GBE_DRV"         , $GBE_DRV;
    dvar    "GBE_PERL"        , $GBE_PERL;
    dvar    "GBE_CORE"        , $GBE_CORE;
    dvar    "GBE_BIN"         , $GBE_BIN;
    dvar    "GBE_TOOLS"       , $GBE_TOOLS;
    dvar    "GBE_CONFIG"      , $GBE_CONFIG;
    penv    "GBE_CACHE_JATS";
    dlist   "GBE_SANDBOX"     , $GBE_SANDBOX;
    dlist   "GBE_DPKG_STORE"  , $GBE_DPKG_STORE;
    dlist   "GBE_DPKG"        , $GBE_DPKG;
    dlist   "GBE_DPKG_CACHE"  , $GBE_DPKG_CACHE;
    dlist   "GBE_DPKG_LOCAL"  , $GBE_DPKG_LOCAL;
    dlist   "GBE_DPKG_SBOX"   , $GBE_DPKG_SBOX;
    dlist   "GBE_DPLY"        , $GBE_DPLY;
    pvar    "GBE_PLATFORM"    , $GBE_PLATFORM;
    pvar    "GBE_BUILDFILTER" , $GBE_BUILDFILTER;
    pvar    "GBE_ABT"         , $GBE_ABT;

    denv    "GBE_VIEWBASE";
    pvar    "GBE_HOSTNAME"    , $GBE_HOSTNAME;
    dvar    "CWD"             , $CWD;
    pvar    "BUILD_FILE"      , $BUILD_FILE;

    penv    "COMSPEC";
    penv    "SHELL";
    pvar    "USER"            , $USER;
    dlist   "PATH"            , $PATH;

    #
    #   Display variables used by various "known" toolsets
    #   These do cause grief sometime, so only do it for verbose
    #
    if ( $GBE_VERBOSE )
    {
        print "\nPerl Environment\n";
        pvar  "VERSION"         ,$];
        penv  "PERL5SHELL";
        penv  "PERL5OPT";
        dlist "PERL5LIB"        ,$ENV{'PERL5LIB'};
        alist "INC"             ,@INC;

        print "\nJava Environment\n";
        foreach my $var ( sort grep ( {/^JAVA_HOME/ } keys %ENV) )
        {
            denv  $var;
        }
        denv  "ANT_HOME";
        denv  "JATS_HOME";
        dlist "CLASSPATH"       ,$ENV{'CLASSPATH'};
    }

    if ( $GBE_VERBOSE || $GBE_ABT )
    {
        print "\nRelease Manager Environment\n";
        penv    "GBE_RM_LOCATION";
        penv    "GBE_RM_USERNAME";
        penv    "GBE_RM_PASSWORD";
        penv    "GBE_RM_URL";
        penv    "GBE_DM_LOCATION";
        penv    "GBE_DM_USERNAME";
        penv    "GBE_DM_PASSWORD";
        penv    "GBE_DM_URL";
    }
    
    if ( $GBE_VERBOSE && ! $GBE_UNIX)
    {
        print "\nWindows Environment\n";
        denv  "TEMP";
        denv  "TMP";

        print "\nMicrosoft Studio Variables\n";
        denv  "PROGRAMFILES";
        denv  "WINDIR";
        denv  "MSVCDir";

        print "\nMicroTec Compiler Variables\n";
        denv "MRI_68K";
        denv "MRI_CF";
        denv "VISIONCLICK";

        print "\nPCLint Variables\n";
        denv "PCLINT";
        penv "LINTPACKAGE";
    }
}

#-------------------------------------------------------------------------------
# Function        : do_exit
#
# Description     : Common exit point so that time information can be displayed
#
# Inputs          : Optional exit code
#
# Returns         :
#
sub do_exit
{
    my ($ecode) = @_;

    $RESULT = $ecode if ( $ecode );

    #..
    #   Determine the runtime
    #
    if ( $opt_time )
    {
        my ($TIMEU, $TIMES) = times;
        $TIMER = time - $TIMER;
        my ($m, $s );

        $m = int($TIMER / 60);
        $s = $TIMER - ($m * 60);

        Message ( "Times: Real: $m:$s, User: $TIMEU, System: $TIMES");
    }
    #.
    #   Make sure that any important exit code is passed to the user
    #
    Verbose ("ExitCode: $RESULT");
    exit $RESULT;
}

########################################################################
#
#   Main body of the script
#
#   Process help and manual options
#       Done after all the setup to ensure that the PATH is correct
#       Done before bombout to give user a chance
#
help() if $opt_help;
ErrorDoExit();  # Exit if any error in setup
ErrorConfig( 'on_exit'    => \&do_exit );

#
#   Reset operational flags.
#   May have been used by setup
#
$opr_done = 0;
$RESULT = 0;

#
#   Process user commands
#
my $cmd = shift @ARGV || help(1);

help(1)                                 if ( $cmd =~ m/^help$/);
dump_vars(@ARGV)                        if ( $cmd =~ m/^var/ );
print_version(@ARGV)                    if ( $cmd =~ m/^ver/ );

clobber                                 if ( $cmd =~ m/^clobber$/ );
build    (@ARGV)                        if ( $cmd =~ m/^build$/ );

bmake_it ('NOTALL', @ARGV)              if ( $cmd =~ m/^[x]*make$/ );
bmake_it (@ARGV)                        if ( $cmd =~ m/^go$/ );
bmake_it (dev_expand('debug', @ARGV) )  if ( $cmd =~ m/^debug$/ );
bmake_it (dev_expand('prod', @ARGV) )   if ( $cmd =~ m/^prod$/ );
bmake_it ("clean", @ARGV)               if ( $cmd =~ m/^clean$/ );
bmake_it ("rebuild", @ARGV)             if ( $cmd =~ m/^rebuild$/ );
bmake_it ('BUILD', @ARGV)               if ( $cmd =~ m/^all$/ );
bmake_it ($cmd, @ARGV )                 if ( $cmd =~ m/^run_unit_tests/ );

install_pkg(@ARGV)                      if ( $cmd =~ m/^install$/ );
create_dpkg(@ARGV)                      if ( $cmd =~ m/^create_dpkg$/ );

ebin  (@ARGV)                           if ( $cmd =~ m/^ebin/ );
etool (@ARGV)                           if ( $cmd =~ m/^etool/ );
eprog (@ARGV)                           if ( $cmd =~ m/^eprog$/ );
run_ant (@ARGV)                         if ( $cmd =~ m/^ant$/ );
run_abt (@ARGV)                         if ( $cmd =~ m/^abt$/ );

etool ('cache_dpkg', @ARGV)                     if ( $cmd =~ m/^dpkg/ );
etool ('gen_msprojects', @ARGV)                 if ( $cmd =~ m/^gen_msproject/ );
etool ('jats_ccrelease.pl', @ARGV)              if ( $cmd =~ m/^cbuild/ );
etool ('jats_ccrelease.pl', @ARGV)              if ( $cmd =~ m/^release/ );
etool ('jats_ccrelease.pl', '-extract' ,@ARGV)  if ( $cmd =~ m/^extract/ );
etool ('jats_label', @ARGV)                     if ( $cmd =~ m/^label$/ );
etool ('jats_sandbox', @ARGV)                   if ( $cmd =~ m/^sandbox$/ );

etool ($cmd, @ARGV)                     unless ($opr_done); # Pass to etool if not known

do_exit();
#.

#-------------------------------------------------------------------------------
#   Documentation
#

=pod

=head1 NAME

jats - JATS utility interface tool

=head1 SYNOPSIS

 Usage: jats [opts] command [cmd-options]

 Where opts:
    -h, -h -h, -man=[n] - Help messages with increasing verbosity
    -cd dir             - Change to specified directory
    -b file             - Use alternate build file
    -verbose[=n]        - Verbose operation
    -debug[=n]          - Enable debug mode of JATS scripts
    -here               - Disable JATS auto locate of build.pl
    -locate             - Locate build.pl file and change to directory
    -locatepkg=pkg      - Locate build.pl. Resolve multiple files via pkg
    -locatefile=file    - Locate specified file and change to directory
    -platform=[name]    - Set GBE_PLATFORM to name   (=+ to append)
    -buildfilter=[xxx]  - Set GBE_BUILDFILTER to xxx (=+ to append)
    -abt=[xxx]          - Set GBE_ABT to xxx (=+ to append)
    -java=version       - Alters java version used (1.4, 1.5)
    -time               - Time build and compile times
    -version=xxx        - Use specified version of JATS
    -[no]exportvars     - Export sanitised JATS EnvVars (default)

 Where command is one of:
    build               - Rebuild the sandbox and makefiles
    make                - Make one or more components
    ant                 - Invoke an ant build
    abt [-java=xxx]     - Invoke the auto build tool
    install             - Install package into local_dpkg_archive
    help                - Display help message
    vars                - Display JATS related environment variables

    create_dpkg         - Install a package into main dpkg_archive
    label               - Labelling functions
    release             - Build a release from a clearcase label
    extract             - Extract a release from a clearcase label
    dpkg_cache          - Maintain a dpkg cache
    gen_msproject       - Generate MSVC project files
    etool name          - Run internal tool
    eprog name          - Run external tool
    ebin name           - Run JATS binary tool

 Shortcut commands. Composites of basic commands
    all [opt]           - Same as a "build" and "make all"
    go  [opt]           - Same as a "make all"
    debug [tgt]         - Same as "make debug package_debug"
    prod  [tgt]         - Same as "make prod package_prod"
    clean               - Same as "make clean"
    clobber             - Same as "build clobber"
    *                   - Unknown commands are treated as arguments to etool

 Where [cmd-options] are command specific.
 Try "jats command -help" for details

=head1 OPTIONS

=over 8

=item B<-help>

Print a brief help message and exits.

=item B<-help -help>

Print a detailed help message with an explanation for each option.

=item B<-man[=n]>

Prints the manual page and exits.

If a numeric manual level is provide then it will be used to control the
verbosity of help provided. This should be inthe range of 1 to 3.

=item B<-b file> B<-buildfile file>

This option modifies the operation of the "build" command. The command will
use the specified file instead of the normal build.pl file.

=item B<-cd dir> B<-changedir dir>

This option will change to specified directory before the command is invoked.

=item B<--verbose[=n]>

This option will increase the level of verbosity of the JATS command and command
invoked by the JATS shell.

If an argument is provided, then it will be used to set the level, otherwise the
existing level will be incremented. This option may be specified multiple times.

=item B<-debug[=n]>

This option will increase the level of debug output of the JATS command and command
invoked by the JATS shell.

If an argument is provided, then it will be used to set the level, otherwise the
existing level will be incremented. This option may be specified multiple times.

=item B<-here>

This option will disable the "autolocate" mechanism of the JATS shell script.

By default JATS will "hunt" for a suitable "build.pl" or "makefile.pl" before
executing a build or a make command. JATS will look in the following directories
for a  suitable file. Under some conditions this mechanism is not useful.

By default JATS will hunt in the following directories: "." "jats" "build/
jats" ".." "../.." and "../../..".

=item B<-locate>

This option will cause the JATS wrapper script to locate the build.pl file and
then change to that directory. This allows users to locate the build root of
a project and then issue other JATS commands.

If the B<-c> option is also specified then searching will start in the specified
directory.

If an alternate build file is specified with the B<-b> option then that filename
will be used in the location process.

This option implies a B<-here>. No further scanning will be done.

Exactly one build file must be located. If more than buildfile is located then
the locations of the build files is displayed and JATS will terminate.


=item B<-locatepkg=packagename>

This option is similar to the B<-locate> option, but it allows the required
build files to be located by ensuring that the required buildfile builds the
specified package.

There are two forms in which the B<packagename> can be specified. It can be
specified as a full package name and vesrion, or as a package name and the
project suffix. ie: jats-api.1.0.0000.cr or jats-api.cr

=item B<-locatefile=file>

This option is similar to the B<-locate> option, but it allows the name of the
file to be located to be specified.

=item B<-platform=[name]>

This option will set the JATS specific environment variable GBE_PLATFORM to
the specified name. The existing value of GBE_PLATFORM may be extended by
using "=+".

In practice the GBE_PLATFORM variable is of little use. The same effect can be
achieved directly with make targets or with GBE_BUILDFILTER.

=item B<-buildfilter=[xxx]>

This option will set the JATS specific environment variable GBE_BUILDFILTER to
the specified name. The existing value of GBE_BUILDFILTER may be extended by
using "=+".

This option may be used to limit the initial build, and subsequent "make" to a
limited set of platforms.

=item B<-abt=[xxx]>

This option will set the JATS specific environment variable GBE_ABT to
the specified name The existing value of GBE_ABT may be extended by
using "=+"..

This option is used to pass arguments directly to some tools within the
(ABT) Auto Build Tool framework, or to indicate that the build is being
performed within the ABT framework.

=item B<-java=version>

This option will override the default java version used by the ant builds and
passed to the underlying programs. The path to the Java SDK is determined from
the environment using the version string replacing the '.' with '_'

eg: -java=1.5, will examine the environment for JAVA_HOME_1_5 and will set
JAVA_HOME to that value. It will not setup the users PATH or classpath.

=item B<-time>

When this option is enabled the JATS script will report the runtime of the
underlying, invoked, command.

=item B<-version=xxx>

When this option is invoked JATS will attempt to use the specified version to
perform all processing. JATS will search the GBE_DPKG_LOCAL, GBE_DPKG_CACHE,
GBE_DPKG, GBE_DPKG_STORE in order to locate the specified version of the package.

The entire command line will be passed to the JATS wrapper script within that
version. This bypasses the jats.bat or jats.sh startup scripts.

JATS will attempt to transfer the target version to the local cache in an
attempt to improve performance.

=item B<-[no]exportvars>

The default operation is to export sanitised and calculated values to the
programs running under JATS. The use of NoExportVars will prevent JATS from
exporting modified EnvVars into the environment. This may be required by
processes, such as the build daemons that need to pick up the current version of
JATS on the fly and not have it fixed when the daemon is started.

=back

=head1 ARGUMENTS

=head2 Basic Commands

The following commands are invoked directly by the JATS script to wrap the build
tools in a controlled manner.

=over 8

=item B<build>

Build or rebuild the sandbox and makefiles.

This command must be used before the component can be "made" and when the
contents of the "buildpl" file change. It is a common mistake to use the "build"
command to much.

The script will hunt for a suitable build.pl file before running the build and
make subcommands.

The build process has a large number of options. Use "JATS build help" to
display the complete list.

=item B<make>

Make one or more components

This command will invoke a suitable "make" program on the makefile found in
the current directory. This makefile should have been generated by JATS.

The make process has a large number of options. Use "JATS make help" to
display the complete list.

=item B<ant>

This command will use ANT to build a component. JATS will determine the
build.xml file to use with the following algorithm.

    If the files called <Project>depends.xml and <Project>.xml exist then JATS
    will create an 'auto.xml' from the depends file, if one does not already
    exist or is older than the depends.xml file and then use the <Project>.xml
    file as the ant build file.

    Otherwise ant is invoked and it will expect a build.xml file.

If multiple <Project>depends.xml and <Project>.xml file pairs are found the
command will fail. This can be resolved through the use of the -buildfile=name
option.

Ant is invoked with the version of Java specified with the -Java=x.x option.

=item B<abt>

This command is used to invoke the Auto Build Tool. It will invoke ANT on the
build.xml file in the current directory in such  manner as to not affect the
environment of the programs running under ANT.

The remained of the command line is passed to the ABT, with the exception of
the options:

=over 8

=item * -java=x.x. This is used to control the version of Java used by the
ABT. The default is 1.5.

=item * -buildfile=name. This is used to provide a different build file to ant.
The default build file is 'build.xml'.

=back

=item B<install>

This command will install a generated "package" into the local_dpkg_archive
directory. This allows a group of packages to be tested before being published
into the global dpkg_archive.

NOTE. This command is no longer needed. The "build" process will place a
shortcut in the local_dpkg_archive to a components "pkg" directory. Other
components will utilize this shortcut directly and pickup the package without
the user needing to "install" the package - a step that can be forgotten.

=item B<help>

Display the basic help message.

=item B<vars [-v]>

This command will display all the JATS related environment variables in a
readable form.

Additional information will be displayed if an argument of "-v" is provided.

=back

=head2 Extended Commands

The following commands support the build environment. They are supplemental to
the build environment. They are implemented as extensions to the basic JATS
command scripts.

=over 8

=item B<create_dpkg>

This command will install a generated package into main dpkg_archive. This
command should not be used directly by a user as it does not ensure that package
has been created in a repeatable manner - use "jats release".

=item B<label>

This command provides a number of useful labelling mechanisms to assist in the
labeling of source code.

=item B<release>, B<cbuild>

This command allows a package to be built and released, given a Clearcase label.
This is the desired release mechanism.

The command has two main uses:

=over 8

=item 1

Build a package for release. The process will ensure that the build is
controlled and repeatable.

=item 2

Rebuild a package for test or debugging purposes.

=back

=item B<extract>

This is the same as "release -extract"


=item B<dpkg_cache>

This utility provides a number of commands to maintain the local cache of
dpkg_archive.

=item B<gen_msproject>

This utility will generate a set of Microsoft Studio (Version 6) project and
workspace files to encapsulate the JATS build. The resultant project allows VS
to be used as an editor, source browser, debugger and build tool. JATS is
still used to perform the build.

=item B<etool name>

This command allows any JATS extension program to be run. The programs will by
found in the JATS TOOLS directory.

=item B<eprog name>

This command allows any JATS extension program to be run.
If the program end in .pl, then it will be run within a perl interpreter.
If the program end in .jar, then it will be run under a java interpreter.

=item B<ebin name>

This command allows any JATS support program to be run. The programs will by
found in the JATS BIN directory for the current platform. This command allows
a user script to access many of the machine independent utilities in a simple
manner.

Example: "JATS ebin ls" - will provide a "real" ls on all platforms.

Example: "JATS ebin sh" - will start up the shell used by make.

=back

=head2 Command Shortcuts

The following commands are user shortcuts to the basic commands. The are basic
commands run in sequence.

=over 8

=item B<all [make_opts]>

This command is the same as running the command "build" and "make all".
It will build a component with a single command.

If "make_opts" is not present then "make all" is used.

If "make_opts" is present then they are passed to the "make" command. In this
manner is it possible to build a WIN32 component of package with a single
command.

=item B<go [make_opts]>

This command is similar to the "all" shortcut, but it does not "build" the
sandbox. It may be used where the sandbox has already been prepared.

=item B<debug [target]>

This command is the same as running "make debug package_debug". It will make and
package all the debug components of a sandbox.

If any additional arguments are provided to the B<dev> command then they will be
treated as make targets and the commands will be expanded to make and package
the specified debug versions of the names platforms. Make options cannot be
passed in this manner.

Example: "JATS dev GAK" will create and package the debug parts for the
GAK_MOS68K and GAK_MOSCF.

=item B<prod [target]>

This command is the same as running "make prod package_prod". It will make and
package all the debug components of a sandbox.

If any additional arguments are provided to the B<prod> command then they will be
treated as make targets and the commands will be expanded to make and package
the specified debug versions of the names platforms. Make options cannot be
passed in this manner.

Example: "JATS prod GAK" will create and package the production parts for the
GAK_MOS68K and GAK_MOSCF.

=item B<clean>

This is the same as "make clean".

=item B<clobber>

This is the same as "build clobber"

=item B<else>

Commands that are not known to the JATS wrapper script are passed to etool.

ie: "JATS xxxx" is is the same as "JATS etool xxxx".

=back

=head1 DESCRIPTION

JATS is a wrapper script. It provides:

=over 8

=item *

A controlled and satirized environment to all the build tools.

=item *

The same command interface on all supported machines: Windows, Solaris and
Linux.

=item *

Provides a framework for extending the build environment toolset.

=back

=head1 Installation of the startup script

The JATS wrapper script is invoked via a machine specific script. This is B<JATS.
BAT> under Windows and B<JATS.SH> under Unix platforms. The startup script
contains sufficient information to startup the main, and common, JATS
program. The script specifies the environment on the machine and should not
affect the target build environment. It also isolates the JATS version changes
from the user.

This script is intended to be user configured. It should be copied into the
users path and modified. It is intended that the script will not change from
version to version of JATS.

=head2  Environment variables

Environmant varibales that specify a path may be set to '-', or 'none' in order
to force an undefined path.

=over 8

=item GBE_MACHTYPE

This specifies the machine that the script is running on. This is fixed within
the startup script.

=item GBE_HOSTMACH

This is a copy of GBE_MACHTYPE.
Unlike GBE_MACHTYPE, this copy is not modified by makefiles.

=item GBE_PERL

This specifies the full path the to B<ActiveState> perl binary.

=item GBE_CORE

This specifies the path the to B<JATS> installation.

=item GBE_CACHE_JATS

When set to a non zero value will force JATS to transfer a working copy to the
local dpkg_archive. This will speed up the build process because the utilities
will be run from a local drive; not a network drive.

This will only operate if JATS is run from dpkg_archive.

=item GBE_DPKG_STORE (optional)

This is the global read-only archive store. It will only be used to source
packages after all other archive stores have been examined. The GBE_DPKG_STORE
is intended to provide a read-only or remote repository within a global
environment.

=item GBE_DPKG

This is the official archive. Some tools will publish packages directly to this
archive.

=item GBE_DPKG_CACHE (optional)

This the path to a local package archive cache. This is used to speed access to
main repository. The cache should be on the users local machine and not a network
drive.

=item GBE_DPKG_LOCAL (optional)

This the path to a group wide local package archive. This may be used to store
non-official packages that are under test or development.

=item GBE_DPKG_SBOX (optional)

This the path to a sandbox specific package archive. This may be used to store
non-official packages that are under test or development within the current sandbox.

The archive is located by searching from the current directory to the root of
the filesystem for a directory called 'sandbox_dpkg_archive'.

It is intended that a group of packages that are being developed in the same
sandbox will share the same sandbox_dpkg_archive.

Jats will ignore the version number when dealing with packages in GBE_DPKG_SBOX.
This is done to simplify the publishing and consuming of packages in the sandbox.

This should not be set by a user. It will be calculated by JATS and passed to
JATS tools and utilities.

=item GBE_SANDBOX (optional)

This the path to a sandbox base directory. It is intended that a group of
packages that are being developed in the same sandbox will share the same
sandbox_dpkg_archive.

This should not be set by a user. It will be calculated by JATS and passed to
JATS tools and utilities.

=item GBE_DPLY (optional)

This the path to the deployment archive.
This archive is NOT used when searching for packages, but it will be used when
publishing special deployment package. This is not the norm.

This variable may be set on a per-project basis.

=item GBE_PLATFORM (deprecated)

This specifies the names of platforms that will be built and made. This should
be empty. Use B<GBE_BUILDFILTER> to provide better control.

=item GBE_BUILDFILTER (desirable)

This is a filter string that specifies which platforms to create makefiles
for. This variable is used to prevent JATS from creating Solaris and Linux
targets on a Windows machine and visa-versa.

=item GBE_JATS_VERSION (optional)

Specifies the version of JATS that the user will use. This is the same as
specifying the -version=xx.xx.xx option on the command line, but because it is
in the environment the required version will be used by all invocations of JATS.

=item GBE_ABT (optional)

Used by the Auto Build Tool to indicate that the build is being performed by the
ABT. When set the build environment will be modified to suite the ABT. Some
operations may be relaxed.

=item GBE_VIEWBASE (optional)

Used by the 'release' utility to provide a user configurable base directory for
the creation of static views.

=item GBE_RM_LOCATION (optional)

Used by tools that interface to Release Manager: primarily the (ABT) Auto Build Tools.
Specifies the location of the Release Manager Database. This is a database url
of the form jdbc:subprotocol:subname as used by java.sql.DriverManager.getConnection()

Example: jdbc:oracle:thin:@auperaora03:1521:RELEASEM

=item GBE_RM_USERNAME (optional)

Used by tools that interface to Release Manager: primarily the (ABT) Auto Build Tools.
Specifies a USERNAME with access to the Release Manager Database.

=item GBE_RM_PASSWORD (optional)

Used by tools that interface to Release Manager: primarily the (ABT) Auto Build Tools.
Specifies a PASSWORD to be used in conjunction with GBE_RM_USERNAME to access
the Release Manager Database.

=item GBE_RM_URL (optional)

Used by tools that interface to Release Manager: primarily the (ABT) Auto Build Tools.
Specifies the base URL of RElease Manager

=item GBE_DM_LOCATION (optional)

Similar to GBE_RM_LOCATION, but is used to access the Deployment Manager Database.
If GBE_DM_LOCATION is not provided, then GBE_RM_LOCATION will be used.

=item GBE_DM_USERNAME (optional)

Similar to GBE_RM_USERNAME, but is used to access the Deployment Manager Database.

=item GBE_DM_PASSWORD (optional)

Similar to GBE_RM_PASSWORD, but is used to access the Deployment Manager Database.

=item GBE_DM_URL (optional)

Similar to GBE_DM_URL, but is used to access the Deployment Manager Database.

=item GBE_MAKE_TYPE (internal)

This EnvVar is set when a Makefile is being procssed by 'make'. The value
indicates the type of the build. It will be either P(Production), D(debug)
or C(Common).

=item GBE_MAKE_TARGET (internal)

This EnvVar is set when a Makefile is being procssed by 'make'. The value
is set current target platform name.

=item GBE_MAKE_CFG (internal)

This EnvVar is set when a Makefile is being procssed by 'make'. The value
is set to the path of the parsed makefile.pl data gathered when the makefile
was created.

=item GBE_MAKE_CMD (internal)

This EnvVar is set when a Makefile is being procssed by 'make'. The value
is set current make command being processed.

=item $GBE_HOSTNAME

This EnvVar is the name of the current host. It is available to be used within
scripts that need to stamp build files.

=back

=head1  dpkg_archive

B<dpkg_archive> is the common package repository. This is on a shared network drive
and as such affects the build speed. JATS supports three mechanisms to supplement
dpkg_archive, as described below. The archive search order is:

=over

=item GBE_DPKG_LOCAL    (User local store)

=item GBE_DPKG_CACHE    (Machine local cache)

=item GBE_DPKG          (Site repository)

=item GBE_DPKG_STORE    (Global repository)

=back

=head2  local dpkg_archive

JATS supports a package archive that is local to a project or suite of build
sandboxes. This is the archive used by the B<install> command.

The user does not have to do anything to access this archive. The archive is
located by the JATS wrapper script by scanning up the directory tree for a
directory called "local_dpkg_archive". If it is found then it is used as the
preferred source for locating archives. This allows a component to be tested
with a "local" or "fixed" version of another package, before an official
release is made.

A local archive may be shared by members of a team by setting the environment
variable B<GBE_DPKG_LOCAL> to the location of a shared drive.

=head2  dpkg_archive cache

JATS supports a package caching mechanism to reduce network utilization. If
the user specified B<GBE_DPKG_CACHE> exists then that archive is treated as a
cache archive. The various JATS tools work together to maintain the cache.

This cache must be setup by the user.

=head2  dpkg_archive store

JATS supports a global, read-only, package distribution mechanism. If
the user specified B<GBE_DPKG_STORE> exists then that archive is treated as a
global store. The store archive will be searched after all other archives.

A global store may be used by members of a global team, in a situation in
which the store is rsync'ed from a central repository.

=head1 EXAMPLES

=over 8

=item   JATS build

This will prime the sandbox ready for "make". The command will locate the
build.pl file and process it. This will locate all the external packages and
generate the required makefiles.

=item   JATS make

This will run the GNU make program over the makefiles generated by the "build"
command. This may be executed in a subdirectory in order to limit the targets
that are "made".

B<NOTE:> Do not run "make" without using the JATS wrapper. It will not perform
as expected as the environment will not have been set up correctly.

=back

=cut