Rev 4184 | Rev 5709 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
######################################################################### Copyright (C) 2008 ERG Limited, All rights reserved## Module name : jats_sandbox.pl# Module type : JATS Utility# Compiler(s) : Perl# Environment(s): JATS## Description : A script to build a collection of packages in the# same sandbox. This script will:## Determine the packages in the sandbox# Determine the build order of the packages# Build the packages in the correct order# Make the packages in the correct order## The script will allow for:# The creation of a sandbox# The addition of packages to the sandbox# Removal of packages from the sandbox### Command syntax (basic)# jats sandbox <command> (options | actions)@## Commands include:# create - Create a sandbox# delete - Delete a sandbox# info - Show sandbox info## build - Build all packages in the sandbox# make - make all packages in the sandbox##......................................................................#require 5.008_002;use strict;use warnings;use JatsError;use JatsSystem;use FileUtils;use JatsBuildFiles;use JatsVersionUtils;use ArrayHashUtils;use Pod::Usage; # required for help supportuse Getopt::Long qw(:config require_order); # Stop on non-optionmy $VERSION = "1.0.0"; # Update this## Options#my $opt_debug = $ENV{'GBE_DEBUG'}; # Allow global debugmy $opt_verbose = $ENV{'GBE_VERBOSE'}; # Allow global verbosemy $opt_help = 0; # Help levelmy $opt_exact = 0; # Exact (escrow) buildmy $opt_toPackage; # Control recursionmy $opt_fromPackage; # Control recursionmy @opt_justPackage; # Control recursionmy @opt_ignorePackage; # Control recursion## Globals - Provided by the JATS environment#my $USER = $ENV{'USER'};my $UNIX = $ENV{'GBE_UNIX'};my $HOME = $ENV{'HOME'};my $GBE_SANDBOX = $ENV{'GBE_SANDBOX'};my $GBE_DPKG_SBOX= $ENV{'GBE_DPKG_SBOX'};my $GBE_MACHTYPE = $ENV{'GBE_MACHTYPE'};my $GBE_DPKG_LOCAL = $ENV{'GBE_DPKG_LOCAL'};my $GBE_DPKG_CACHE = $ENV{'GBE_DPKG_CACHE'};my $GBE_DPKG = $ENV{'GBE_DPKG'};my $GBE_DPLY = $ENV{'GBE_DPLY'};my $GBE_DPKG_STORE = $ENV{'GBE_DPKG_STORE'};my $GBE_BUILDFILTER = $ENV{'GBE_BUILDFILTER'};## Globals#my @stopped = (); # Stopped entriesmy @build_order = (); # Build Ordered list of entriesmy %extern_deps; # Hash of external dependenciesmy %packages; # Hash of packages#-------------------------------------------------------------------------------# Function : Mainline Entry Point## Description :## Inputs :### Process help and manual options#my $result = getOptionsFromArray ( \@ARGV );pod2usage(-verbose => 0, -message => "Version: $VERSION") if ($opt_help == 1 || ! $result);pod2usage(-verbose => 1) if ($opt_help == 2 );pod2usage(-verbose => 2) if ($opt_help > 2 );## Configure the error reporting process now that we have the user options#ErrorConfig( 'name' => 'SANDBOX','verbose' => $opt_verbose );## Reconfigure the options parser to allow subcommands to parse options#Getopt::Long::Configure('permute');## Determine Sandbox type. Exact or local#$opt_exact = (-f $GBE_SANDBOX . '/sandbox_dpkg_archive/.exact' )if ( $GBE_SANDBOX );## Parse the user command and decide what to do## Remove user command from the command line. This will leave command options# in @ARGV so that they can be parsed by the subcommand.#my $cmd = shift @ARGV || "";help(1) if ( $cmd =~ m/^help$/ || $cmd eq "" );buildcmd($cmd, @ARGV ) if ( $cmd =~ m/(^all$)|(^build$)/ );cache(@ARGV) if ( $cmd =~ m/^cache$/ );clean($cmd, @ARGV) if ( $cmd =~ m/(^clobber$)|(^clean$)/ );cmd('make', $cmd, @ARGV ) if ( $cmd =~ m/(^make$)/ );cmd('cmd', @ARGV) if ( $cmd =~ m/^cmd$/ );create_sandbox() if ( $cmd =~ m/^create$/ );delete_sandbox() if ( $cmd =~ m/^delete$/ );info(@ARGV) if ( $cmd =~ m/^info$/ );populate(@ARGV) if ( $cmd =~ m/^populate$/ );buildfilter(@ARGV) if ( $cmd =~ m/^buildfilter$/ );Error ("Unknown sandbox command: $cmd");exit 1;#-------------------------------------------------------------------------------## Give the user a clue#sub help{my ($level) = @_;$level = $opt_help unless ( $level );pod2usage(-verbose => 0, -message => "Version: ". $VERSION) if ($level == 1 );pod2usage(-verbose => $level -1 );}#-------------------------------------------------------------------------------# Function : create_sandbox## Description : create a sandbox in the current current directory## Inputs : None##sub create_sandbox{GetOptions ("help:+" => \$opt_help,"manual:3" => \$opt_help,"exact" => \$opt_exact,) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Create Sandbox") if ($opt_help || $#ARGV >= 0 );Error ("Cannot create a sandbox within a sandbox","Sandbox base is: $GBE_SANDBOX" ) if ( $GBE_SANDBOX );mkdir ('sandbox_dpkg_archive') || Error ("Cannot create the directory: sandbox_dpkg_archive") ;TouchFile( 'sandbox_dpkg_archive/.exact', 'Sandbox marker file')if ($opt_exact);Message ('Sandbox created' . ($opt_exact ? ' - With exact version number processing' : ''));exit 0;}#-------------------------------------------------------------------------------# Function : delete_sandbox## Description : Delete a sandbox# Its up to the user the delete the components in the sandbox## Inputs : None## Returns :#sub delete_sandbox{GetOptions ("help:+" => \$opt_help,"manual:3" => \$opt_help,) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Delete Sandbox") if ($opt_help || $#ARGV >= 0 );unless ( $GBE_SANDBOX ){Warning("No sandbox found to delete");}else{Error ("Sandbox directory not completely removed")if RmDirTree( "$GBE_SANDBOX/sandbox_dpkg_archive" );Message("Sandbox information deleted","Sandbox components must be manually deleted");}exit 0;}#-------------------------------------------------------------------------------# Function : info## Description : Display Sandbox information## Inputs : Command line args# -v - Be more verbose## Returns : Will exit#sub info{my (@cmd_opts ) = @_;my $show = 0;Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts,'verbose:+' => \$show,) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Sandbox Information") if ($opt_help || $#cmd_opts >=0 );## Determine Sandbox information# Populate global variables#calc_sandbox_info(1);## Display information#Message ("Type : " . ($opt_exact ? 'Exact' : 'Development') );Message ("Base : $GBE_SANDBOX");Message ("Archive : $GBE_DPKG_SBOX");Message ("BuildFilter: $GBE_BUILDFILTER" . ( (-f $GBE_SANDBOX . '/buildfilter') ? ' - Local to sandbox' : ''));Message ("Build Order");foreach my $pname ( @stopped ){Message( " Level:" . "-" . " Name: " . $pname . ' (Stopped)');}foreach my $fe ( @build_order ){Message( " Level:" . $fe->{level} . " Name: " . $fe->{dname} . ($fe->{buildActive} ? '' : ' (Build Suppressed)'));Message( DisplayPath (" Path: $fe->{dir}" )) if $show;if ( $show ){foreach my $idep ( sort values %{$fe->{'ideps'}} ){Message (" I:$idep");}foreach my $edep ( sort keys %{$fe->{'edeps'}} ){my ($ppn,$ppv) = split( $; , $edep);Message (" E:$ppn $ppv");}}}## External dependencies flags# * - Package does not exist in dpkg_archive# + - Multiple versions of this package are usedMessage("External Dependencies");foreach my $de ( sort keys %extern_deps ){my @vlist = keys %{$extern_deps{$de}};my $flag = $#vlist ? '+' : '';foreach my $pve ( @vlist ){my ($pn,$pv) = split( $; , $pve );my $exists = check_package_existance($pn,$pv ) ? '' : '*';my $flags = sprintf ("%4.4s", $flag . $exists);Message ("${flags}${pn} ${pv}");if ( $show ){foreach my $pkg ( @{$extern_deps{$de}{$pve}} ){my $ppn = join ('.', split( $; , $pkg));Message (" U:$ppn");}}}}if ( $show > 2 || $opt_verbose > 2 ){DebugDumpData( "extern_deps", \%extern_deps);DebugDumpData( "build_order", \@build_order);DebugDumpData( "packages", \%packages);}exit (0);}#-------------------------------------------------------------------------------# Function : check_package_existance## Description : Determine if a given package-version exists## Inputs : $name - Package Name# $ver - Package Version## Returns : true - Package exists#sub check_package_existance{my ($name, $ver) = @_;## Look in each package archive directory#foreach my $dpkg ( $GBE_DPKG_SBOX,$GBE_DPKG_LOCAL,$GBE_DPKG_CACHE,$GBE_DPKG,$GBE_DPLY,$GBE_DPKG_STORE ){next unless ( $dpkg );if ( -d "$dpkg/$name/$ver" ){return 1;}}return 0;}#-------------------------------------------------------------------------------# Function : calc_sandbox_info## Description : Examine the sandbox and determine all the important# information## Operation will be modified by# $opt_toPackage# $opt_fromPackage# @opt_justPackage# @opt_ignorePackage## Inputs : info - True: Just for info# Keep supressed packages## Returns : Will exit if not in a sandbox# Populates global variables# @build_order - build ordered array of build entries#sub calc_sandbox_info{my ($info) = @_;## Start from the root of the sandbox#Error ("Command must be executed from within a Sandbox") unless ( $GBE_SANDBOX );chdir ($GBE_SANDBOX) || Error ("Cannot chdir to $GBE_SANDBOX");## Locate all packages within the sandbox# These will be top-level directories - one per package#my @dirlist;my @build_list;foreach my $pname ( glob("*") ){next if ( $pname =~ m~^\.~ );next if ( $pname =~ m~dpkg_archive$~ );next if ( $pname =~ m~^CVS$~ );next unless ( -d $pname );Verbose ("Package discovered: $pname");if ( -f "$pname/stop" || -f "$pname/stop.$GBE_MACHTYPE" ){push @stopped, $pname;Warning("Package contains stop file: $pname");next;}push @dirlist, $pname;## Locate the build files in each package# Scan the build files and extract dependancy information#my $bscanner = BuildFileScanner( $pname, 'build.pl','--LocateAll',$opt_exact ? '--ScanExactDependencies' : '--ScanDependencies' );$bscanner->scan();my @blist = $bscanner->getInfo();Warning ("Package does not have build files: $pname") unless ( @blist );Warning ("Package has multiple build files: $pname") if ( $#blist > 0 );push @build_list, @blist;}## Process each build file and extract# Name of the Package# Dependency list# Build up a hash of dependence information#my %depends;my %multi;foreach my $be ( @build_list ){Verbose( DisplayPath ("Build file: " . $be->{dir} . " Name: " . $be->{file} ));## Sandbox vs Exact processing# Set a suitable display name# Set a suitable tag#$be->{dname} = $opt_exact ? $be->{full} : $be->{mname};$be->{tag} = $opt_exact ? $be->{fullTag} : $be->{package};$be->{fname} = join ('_', $be->{name}, $be->{version});# DebugDumpData ("be", $be );## Catch multiple builds for the same package# Report later - when we have all#next unless ( $be->{dname} );push @{$multi{$be->{dname}}},$be->{dir};## Add into dependency struct#$depends{$be->{tag}} = $be;}foreach my $dname ( sort keys %multi ){ReportError ("Multiple builders for : $dname", @{$multi{$dname}} )if ( scalar @{$multi{$dname}} > 1 );}ErrorDoExit();#DebugDumpData ("depends", \%depends );## Determine the build order#@build_order = ();my $more = 1;my $level = 0;## Remove any dependencies to 'external' packages# These will not be met internally and can be regarded as constant## Split 'depends' into internal (ideps) and external (edeps)# edeps : External Dependencies# Key: Name;Version# Value: 'tag' - index into packages# ideps : Internal Dependencies# Key: 'tag' - Index into packages# Value: 'dname' - Display Name#foreach my $key ( keys %depends ){foreach my $build ( keys( %{$depends{$key}{depends}} )){unless (exists $depends{$build}){$depends{$key}{'edeps'}{$depends{$key}{depends}{$build}} = $build;delete ($depends{$key}{depends}{$build}) ;Verbose2( "Not in set: $build");}else{$depends{$key}{'ideps'}{$build} = $depends{$build}{dname};}}}#DebugDumpData ("depends", \%depends );## Determine package build order# Scan the list of packages in the build set and determine# those with no dependencies. These can be built.# Remove those packages as dependents from all packages# Repeat.#my %found = map { $_ => 1 } @opt_ignorePackage;my %notFound = map { $_ => 1 } @opt_justPackage;my $scan_start = 0;my $scan_stop = 0;my $scan_active = ($opt_fromPackage) ? 0 : 1;$scan_active = 0 if ( !$opt_fromPackage && !$opt_fromPackage && !@opt_ignorePackage && @opt_justPackage );while ( $more ){$more = 0;$level++;my @build;## Locate packages with no dependencies#foreach my $key ( keys %depends ){next if ( keys( %{$depends{$key}{depends}} ) );push @build, $key;}foreach my $build ( @build ){$more = 1;my $fe = $depends{$build};my $scan_add = $scan_active ? 1 : 0;if ( $opt_fromPackage && (($fe->{mname} eq $opt_fromPackage) || ($fe->{name} eq $opt_fromPackage) || ($fe->{fname} eq $opt_fromPackage))){$scan_add = 1;$scan_active = 1;$scan_start = 1;}if ( $opt_toPackage && (($fe->{mname} eq $opt_toPackage) || ($fe->{name} eq $opt_toPackage) || ($fe->{fname} eq $opt_toPackage))){$scan_add = 0;$scan_active = 0;$scan_stop = 1;}if ( @opt_justPackage ){foreach my $pname ( @opt_justPackage ){if ( (($fe->{mname} eq $pname) || ($fe->{name} eq $pname) || ($fe->{fname} eq $pname))){$scan_add = 1;delete $notFound{$pname};}}}if ( @opt_ignorePackage ){foreach my $pname ( @opt_ignorePackage ){if ( (($fe->{mname} eq $pname) || ($fe->{name} eq $pname) || ($fe->{fname} eq $pname))){$scan_add = 0;delete $found{$pname};}}}$fe->{level} = $level;$fe->{buildActive} = $scan_add;$packages{$build} = $fe;push (@build_order, $fe) if ( $scan_add || $info );delete $depends{$build};delete $fe->{depends}; # remove now its not needed}foreach my $key ( keys %depends ){foreach my $build ( @build ){delete $depends{$key}{depends}{$build};}}}## Detect bad user specifications#ReportError ("Specified FromPackage not found: $opt_fromPackage") if ( $opt_fromPackage && !$scan_start );ReportError ("Specified ToPackage not found: $opt_toPackage") if ( $opt_toPackage && !$scan_stop );ReportError ("Specified ExactPackages not found: ", keys( %notFound) ) if ( %notFound );ReportError ("Specified IgnorePackages not found: ", keys( %found) ) if ( %found );ErrorDoExit();## Calculate the external dependencies# Only process packages that are a part of the build## extern_deps structure# Hash key: 'tag' - Index into packages# Value: Hash of:# Key : Name;Version# Value: Array of: 'tags' (Index into packages)# of packages that use the external# component.{Verbose ("Calculate external dependencies");%extern_deps = ();foreach my $key ( keys %packages ){next unless ( $packages{$key}{buildActive} );next unless ( $packages{$key}{'edeps'} );foreach ( keys %{$packages{$key}{'edeps'}} ){push @{$extern_deps{$packages{$key}{'edeps'}{$_}} {$_} }, $key;}}}## Just to be sure to be sure#if ( keys %depends ){#DebugDumpData ("depends", \%depends );Error( "Internal algorithm error: Bad dependancy walk","Possible circular dependency");}# DebugDumpData("Packages", \%packages);# DebugDumpData ("Order", \@build_order);# DebugDumpData("External Depends", \%extern_deps );}#-------------------------------------------------------------------------------# Function : cmd## Description : Execute a command in all the sandboxes# Locate the base of the sandbox# Locate all packages in the sandbox# Locate all build files in each sandbox# Determine build order# Issue commands for each sandbox in order## Inputs : Arguments passed to jats build## Returns : Will exit#sub cmd{my ($hcmd, @cmd_opts ) = @_;my $opt_keepgoing;Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts,'keepgoing!' => \$opt_keepgoing,) || Error ("Invalid command line" );SubCommandHelp( $opt_help, $hcmd) if ($opt_help );## Determine Sandbox information# Populate global variables#calc_sandbox_info();foreach my $fe ( @build_order ){my $dir = $fe->{dir};Message( "Level:" . $fe->{level} . " Name: " . $fe->{dname} ,DisplayPath (" Path: $fe->{dir}" ));my $result = JatsCmd( "-cd=$dir", @cmd_opts);if ( $result ) {if ( $opt_keepgoing ) {Warning ("Cmd failure");} else {Error ("Cmd failure");}}}exit 0;}#-------------------------------------------------------------------------------# Function : buildcmd## Description : Build the entire sandbox# The all and the build are similar.# Its not really useful to do a build without a make# so we don't try## Inputs : Arguments passed to jats make### Returns : Will exit#sub buildcmd{my ($cmd, @cmd_opts) = @_;my @build_opts;my @make_opts;## Extract and options#Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts ) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Command $cmd") if ($opt_help );## Insert default options#push @build_opts, '-noforce' if ( $cmd eq 'all' );push @build_opts, '-force' if ( $cmd ne 'all' );## Attempt to split the options into build and make options# Only handle the often used options to build.#foreach ( @cmd_opts ){if ( m/^-cache/ || m/^-package/ || m/^-forcebuildpkg/ || m/-expert/) {push @build_opts, $_;} else {push @make_opts, $_;}}push @make_opts, 'all' unless ( @make_opts );## Determine Sandbox information# Populate global variables#calc_sandbox_info();foreach my $fe ( @build_order ){my $dir = $fe->{dir};Message( "Level:" . $fe->{level} . " Name: " . $fe->{dname} ,DisplayPath (" Path: $fe->{dir}" ));JatsCmd( "-cd=$dir", 'build', @build_opts) && Error ("Build Cmd failure") if ( $result );JatsCmd( "-cd=$dir", 'make', @make_opts) && Error ("Make Cmd failure") if ( $result );}exit 0;}#-------------------------------------------------------------------------------# Function : clean## Description : Execute a command in all the sandboxes# Locate the base of the sandbox# Locate all packages in the sandbox# Locate all build files in each sandbox# Determine build order# Issue commands for each sandbox in order## Inputs : Arguments passed to jats build## Returns : Will exit#sub clean{my ($mode, @cmd_opts ) = @_;## Extract and options#Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts ) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Clean") if ($opt_help );## Determine Sandbox information# Populate global variables#calc_sandbox_info();my @cmd = $mode eq 'clobber' ? ('clobber') : ('make', 'clean' );## Clobber and clean need to be done in the reverse order#foreach my $fe ( reverse @build_order ){my $dir = $fe->{dir};Message( "Level:" . $fe->{level} . " Name: " . $fe->{dname} ,DisplayPath (" Path: $fe->{dir}" ));my $result = JatsCmd( "-cd=$dir", @cmd, @cmd_opts);Error ("Cmd failure") if ( $result );}exit 0;}#-------------------------------------------------------------------------------# Function : cache## Description : Cache external packages into the sandbox## Inputs : @opts - User options## Returns : Nothing#sub cache{my (@cmd_opts) = @_;getOptionsFromArray ( \@cmd_opts ) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Cache") if ($opt_help || $#cmd_opts >= 0 );## Determine Sandbox information# Populate global variables#Message("Cache External Dependencies");calc_sandbox_info();## Walk the list of external dependencies and cache each one#foreach my $de ( sort keys %extern_deps ){my @vlist = keys %{$extern_deps{$de}};foreach my $pve ( @vlist ){my ($pn,$pv) = split( $; , $pve );Message ("Cache ${pn} ${pv}");JatsTool ('cache_dpkg', "${pn}/${pv}" );}}exit 0;}#-------------------------------------------------------------------------------# Function : populate## Description : Populate the sandbox with package versions### Inputs : commands - Array of command line arguments# Mode-0:## pkg_name pkg_version - Import files for named package# options:# -recurse - Import dependent packages too# -missing - Import dependencies not in dpkg_archive# -test - Show what would be done# -extractfiles - Extract file, no view##### Returns : Does not return#use JatsRmApi;use DBI;## Data Base Interface#my $RM_DB;my $PopLevel = 0;my %PopPackage;my @StrayPackages;my @PopBase;sub populate{my (@cmd_opts ) = @_;my $opt_missing = 0;my $opt_recurse = 0;my $opt_test = 0;my $opt_show = 0;my $opt_extractfiles;my @opt_extract = qw(-extract);my @opt_fnames;my @opt_exclude;my $opt_all;Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts,"all" => \$opt_all,"missing" => \$opt_missing,"test" => \$opt_test,"show" => \$opt_show,"recurse:100" => \$opt_recurse,'excludePackage:s' => sub{ opts_add2List( \@opt_exclude, @_ )},) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Populate Sandbox") if ($opt_help );## Sanity tests#Error ("Populate: -missing and -all options are mutually exclusive")if ( $opt_missing && $opt_all );## Extract options for the jats extract utility#foreach ( @cmd_opts ){if ( m~^-~ ) {push ( @opt_extract, $_);} else {push ( @opt_fnames, $_);}}## Allow exactly zero or two 'bare' arguments# Create an array of package-versions to be processed.#if ( $#opt_fnames >= 0 ){Error ("Populate: Must specify both a package name and version")if ( $#opt_fnames != 1 );push @PopBase, join( $;, @opt_fnames );}elsif ( $opt_missing || $opt_all ){## User has not provided a package name to extract# Assume that the user will want all or missing dependencies## Determine packages that are not present#calc_sandbox_info();## Scan for missing dependencies#foreach my $de ( sort keys %extern_deps ){my @vlist = keys %{$extern_deps{$de}};foreach my $pve ( @vlist ){my ($pn,$pv) = split( $; , $pve );unless ($opt_missing && check_package_existance( $pn, $pv )){push @PopBase, join( $;, $pn , $pv );}}}}else{Error ("No command or option specified. See help for command usage");}## Process the list of package-versions# These are top level packages. Get details from Release Manager##DebugDumpData("Data", \@PopBase );$PopLevel = 0;foreach my $entry ( @PopBase ){my ($pname, $pver ) = split( $; , $entry);my $pv_id = getPkgDetailsByName($pname, $pver);Error ("populate: $pname, $pver not found in Release Manager" )unless ( $pv_id );getPkgDetailsByPV_ID($pv_id);}## If recursing then process packages that have yet to# be processed. At the start there will be the initial user specified# packages on the list. Place a marker at the end so that we can# determine how far we are recursing down the dependency tree.#$opt_recurse = ($opt_all ? 100 : $opt_recurse);if ( $opt_recurse ){my $marker = join($; , '_NEXT_LEVEL_', 0, 0 );push @StrayPackages, $marker;$PopLevel++;while ( $#StrayPackages >= 0 ){my ($name, $ver, $pv_id) = split($;, shift @StrayPackages);## Marker.# Increment the level of recursion# Detect end conditions#if ( $name eq '_NEXT_LEVEL_' ){last unless ($#StrayPackages >= 0 );$PopLevel++;last if ( $PopLevel > $opt_recurse );push @StrayPackages, $marker;next;}next if ( exists $PopPackage{$name}{$ver}{done} );getPkgDetailsByPV_ID ( $pv_id );#print "Stray: $pv_id, $name, $ver\n";}}#DebugDumpData("Data", \%PopPackage );## Determine packages that need to be extracted# Sort alphabetically - case insensitive#foreach my $pname ( sort {lc($a) cmp lc($b)} keys %PopPackage ){pkgscan:foreach my $pver ( sort keys %{$PopPackage{$pname}} ){## Create a nice view name for the extraction# Will also be used to test for package existence#my $vname = "$pname $pver";$vname =~ s~ ~_~g;$vname =~ s~__~~g;if ( -d "$GBE_SANDBOX/$vname" ){Warning("Package already in sandbox: $pname, $pver");next;}## If scanning for missing packages, then examine archives# for the packages existence. Don't do this on level-0 packages# These have been user specified.#if ( $opt_missing && $PopPackage{$pname}{$pver}{level} ){my $found = check_package_existance( $pname, $pver );if ( $found ){Verbose ("Package found in archive - skipped: $pname, $pver");next;}}## Has the user specifically excluded this package# Allow three forms# packageName# packageName_Version# packageName.projectName#my $excluded;foreach my $ename ( @opt_exclude ){if ( $ename eq $pname ) {$excluded = 1;} elsif ($ename eq $pname .'_' . $pver ) {$excluded = 1;} else {if ( $pver =~ m~(\.[a-z]{2,4})$~ ){$excluded = ($ename eq $pname . $1 );}}if ( $excluded ){Message ("Package excluded by user - skipped: $pname, $pver");next pkgscan;}}## Generate commands to extract the package#my $vcstag = $PopPackage{$pname}{$pver}{vcstag};my @cmd = qw(jats_vcsrelease);push @cmd, "-view=$vname", "-label=$vcstag", @opt_extract;if ( $opt_show ){Message ("$pname $pver");}elsif ( $opt_test ){Message "jats " . QuoteCommand (@cmd );}else{Message "Extracting: $pname $pver";my $rv = JatsCmd (@cmd);Error ("Package version not extracted")if ( $rv );}}}## This command does not return#exit (0);}#-------------------------------------------------------------------------------# Function : buildfilter## Description : Manipulate the sandbox build filter## Inputs : Optional filter names# +NAME - will add filter# -NAME will remove it# NAME will set it# No args will just display the build filter## Returns : Does not return#sub buildfilter{my (@cmd_opts ) = @_;my @filter_list;my $first_arg = 1;my $modified;Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts ) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Buildfilter") if ($opt_help );## Set the initial filter list# This will have been parsed by JATS before we get here#@filter_list = split( /[,\s]+/, join(',', $GBE_BUILDFILTER));## Extract options for the jats extract utility#foreach ( @cmd_opts ){if (m~^\+(.*)~){UniquePush( \@filter_list, $1);}elsif (m~^\-(.*)~){ArrayDelete( \@filter_list, $1);}else{@filter_list = () if ($first_arg);UniquePush( \@filter_list, $_);}$first_arg = 0;$modified = 1;}## Display the results to the user#Message('BuildFilter:', @filter_list);## Write out a new file#if ($modified){FileCreate($GBE_DPKG_SBOX . '/buildfilter', @filter_list);}## This command does not return#exit (0);}#-------------------------------------------------------------------------------# Function : getPkgDetailsByName## Description : Determine the PVID for a given package name and version## Inputs : $pname - Package name# $pver - Package Version## Returns :#sub getPkgDetailsByName{my ($pname, $pver) = @_;my $pv_id;my (@row);connectRM(\$RM_DB) unless ($RM_DB);# First get details for a given package versionmy $m_sqlstr = "SELECT pv.PV_ID, pkg.PKG_NAME, pv.PKG_VERSION" ." FROM RELEASE_MANAGER.PACKAGE_VERSIONS pv, RELEASE_MANAGER.PACKAGES pkg" ." WHERE pkg.PKG_NAME = \'$pname\' AND pv.PKG_VERSION = \'$pver\' AND pv.PKG_ID = pkg.PKG_ID";my $sth = $RM_DB->prepare($m_sqlstr);if ( defined($sth) ){if ( $sth->execute( ) ){if ( $sth->rows ){while ( @row = $sth->fetchrow_array ){$pv_id = $row[0];my $name = $row[1];my $ver = $row[2];Verbose( "getPkgDetailsByName :PV_ID= $pv_id");}}$sth->finish();}}else{Error("Prepare failure" );}return $pv_id;}#-------------------------------------------------------------------------------# Function : getPkgDetailsByPV_ID## Description : Populate the Packages structure given a PV_ID# Called for each package in the SBOM## Inputs : PV_ID - Package Unique Identifier## Returns : Populates Package#sub getPkgDetailsByPV_ID{my ($PV_ID) = @_;my $foundDetails = 0;my (@row);connectRM(\$RM_DB) unless ($RM_DB);# First get details from pv_idmy $m_sqlstr = "SELECT pv.PV_ID, pkg.PKG_NAME, pv.PKG_VERSION, release_manager.PK_RMAPI.return_vcs_tag($PV_ID)" ." FROM RELEASE_MANAGER.PACKAGE_VERSIONS pv, RELEASE_MANAGER.PACKAGES pkg " ." WHERE pv.PV_ID = \'$PV_ID\' AND pv.PKG_ID = pkg.PKG_ID";my $sth = $RM_DB->prepare($m_sqlstr);if ( defined($sth) ){if ( $sth->execute( ) ){if ( $sth->rows ){while ( @row = $sth->fetchrow_array ){my $pv_id = $row[0];my $name = $row[1];my $ver = $row[2];my $vcstag = $row[3] || '';$vcstag =~ tr~\\/~/~;Verbose ("getPkgDetailsByPV_ID: $PV_ID, $name, $ver, $vcstag");$PopPackage{$name}{$ver}{pvid} = $PV_ID;$PopPackage{$name}{$ver}{done} = 1;$PopPackage{$name}{$ver}{vcstag} = $vcstag;$PopPackage{$name}{$ver}{level} = $PopLevel;getDependsByPV_ID( $pv_id, $name, $ver );}}else{Warning ("No Package details for: PVID: $PV_ID");}$sth->finish();}else{Error("getPkgDetailsByPV_ID: Execute failure", $m_sqlstr );}}else{Error("Prepare failure" );}}#-------------------------------------------------------------------------------# Function : getDependsByPV_ID## Description : Extract the dependancies for a given package version## Inputs : $pvid## Returns :#sub getDependsByPV_ID{my ($pv_id, $pname, $pver) = @_;connectRM(\$RM_DB) unless ($RM_DB);## Now extract the package dependencies#my $m_sqlstr = "SELECT pkg.PKG_NAME, pv.PKG_VERSION, pd.DPV_ID" ." FROM RELEASE_MANAGER.PACKAGE_DEPENDENCIES pd, RELEASE_MANAGER.PACKAGE_VERSIONS pv, RELEASE_MANAGER.PACKAGES pkg" ." WHERE pd.PV_ID = \'$pv_id\' AND pd.DPV_ID = pv.PV_ID AND pv.PKG_ID = pkg.PKG_ID";my $sth = $RM_DB->prepare($m_sqlstr);if ( defined($sth) ){if ( $sth->execute( ) ){if ( $sth->rows ){while ( my @row = $sth->fetchrow_array ){my $name = $row[0];my $ver = $row[1];Verbose2( " Depends: $name, $ver");unless ( exists $PopPackage{$name} && exists $PopPackage{$name}{$ver} && exists $PopPackage{$name}{$ver}{done} ){push @StrayPackages, join($;, $name, $ver, $row[2] );}}}$sth->finish();}}else{Error("GetDepends:Prepare failure" );}}#-------------------------------------------------------------------------------# Function : getOptionsFromArray## Description : Like getOptions, but handles an array# Provided as the version of Perl used does not have one## Inputs : pArray - Ref to array# .... - GetOptions arguments## Returns :#sub getOptionsFromArray{my ($pArray, %args) = @_;## Common arguments#my %commonOptions = ('help|h:+' => \$opt_help,'manual:3' => \$opt_help,'verbose:+' => \$opt_verbose,'topackage:s' => \$opt_toPackage,'frompackage:s' => \$opt_fromPackage,'justpackage:s' => sub{ opts_add2List( \@opt_justPackage, @_ )},'ignorepackage:s' => sub{ opts_add2List( \@opt_ignorePackage, @_ )},);## Merge in the user options#@commonOptions{keys %args} = values %args;local ( @ARGV );@ARGV = @$pArray;my $rv = GetOptions ( %commonOptions );@$pArray = @ARGV;ErrorConfig('verbose' => $opt_verbose );return $rv;}#-------------------------------------------------------------------------------# Function : opts_add2List## Description : Option processing helper# Add comma separated options to an array# User can then add items one at a time, or several at once## Inputs : aref - Ref to an array to extent# arg2 - Option name# arg3 - Option value## Returns :#sub opts_add2List{my( $ref, $name, $value) = @_;if ( $value ){foreach ( split(/\s*,\s*/,$value) ){push @{$ref}, $_;}}}#-------------------------------------------------------------------------------# Function : SubCommandHelp## Description : Provide help on a subcommand## Inputs : $help_level - Help Level 1,2,3# $topic - Topic Name## Returns : This function does not return#sub SubCommandHelp{my ($help_level, $topic) = @_;my @sections;## Spell out the section we want to display## Note:# Due to bug in pod2usage can't use 'head1' by itself# Each one needs a subsection.#push @sections, qw( NAME SYNOPSIS ) ;push @sections, qw( ARGUMENTS OPTIONS ) if ( $help_level > 1 );push @sections, qw( DESCRIPTION EXAMPLES ) if ( $help_level > 2 );## Extract section from the POD#pod2usage({-verbose => 99,-noperldoc => 1,-sections => $topic . '/' . join('|', @sections) } );}#-------------------------------------------------------------------------------# Documentation# NOTE## Each subcommand MUST have# head1 section as used by the subcommand# This should be empty, as the contents will NOT be displayed# head2 sections called# NAME SYNOPSIS ARGUMENTS OPTIONS DESCRIPTION EXAMPLES##=head1 xxxxxx#=head2 NAME#=head2 SYNOPSIS#=head2 ARGUMENTS#=head2 OPTIONS#=head2 DESCRIPTION#=head2 EXAMPLES#=pod=head1 NAMEjats_sandbox - Build in a Development Sandbox=head1 SYNOPSISjats sandbox [options] command [command options]Options:-help[=n] - Display help with specified detail-help -help - Detailed help message-man - Full documentationOptions for recursion control:-toPackage=name - Stop building after package-fromPackage=name - Start building from package-justPackage=name[,name] - Build named packages-ignorePackage=name[,name] - Do not build named packagesCommands:help - Same as -helpcreate - Create a sandbox in the current directorypopulate - Populate the sandbox with packagesdelete - Delete the sandboxinfo [[-v]-v] - Sandbox information. -v: Be more verbosebuildfilter - Modify and display sandbox buildfiltercmd - Do commands in all sandbox componentsall - Do 'build', if required, then a make in all componentsbuild - Force 'build and make' in all sandbox componentsmake - Do 'make' in all sandbox componentsclean - Do 'make clean' in all sandbox componentsclobber - Do 'build clobber' is all sandbox componentscache - Cache external dependent packagesUse the commandjats sandbox 'command' -hfor command specific help=head1 OPTIONS=over 8=item B<-help[=n]>Print a brief help message and exits.There are three levels of help=over 8=item 1Brief synopsis=item 2Synopsis and option summary=item 3Detailed help in man format=back 8=item B<-help -help>Print a detailed help message with an explanation for each option.=item B<-man>Prints the manual page and exits. This is the same a -help=3=item B<-toPackage=name>This option is available in all commands that process multiple packages.Package processing will stop at the named package.The package name can be specicied in one of three forms:=over 8=item 1Just the package name. ie: MyPackage=item 2The package name and the project suffix. ie: MyPackage.prj=item 3The package name and version, joined with an underscore: ie: MyPackage_1.0.0000.prj=back 8=item B<-fromPackage=name>This option is available in all commands that process multiple packages.Package processing will start at the named package.The package name can be specified in one of the three forms descibed under the '-toPackage' option.=item B<-justPackage=name[,name]>This option is available in all commands that process multiple packages. Thenamed packages will be processed in the correct build order. Packages that arenot named will be skipped, unless the package is being processed due tobeing in the 'fromPackage' to 'toPackage' range.Multiple packages can be named either by separating names with a comma, orwith multiple options.The package names can be specified as a mix of the three forms descibed under the '-toPackage' option.=item B<-ignorePackage=name[,name]>This option is available in all commands that process multiple packages. Thenamed packages will not be processed.Multiple packages can be named either by separating names with a comma, orwith multiple options.The exclusion of a package takes precedence over its inclusion.The package names can be specified as a mix of the three forms descibed under the '-toPackage' option.=back=head1 DESCRIPTIONThis program is the primary tool for the maintenance of Development Sandboxes.More documentation will follow.=head2 SANDBOX DIRECTORYThe sandbox directory is marked as being a sandbox through the use of the'sandbox create' command. This will create a suitable structure within thecurrent directory.Several JATS commands operate differently within a sandbox. The 'extract' and'release' commands will create static views within the sandbox and not thenormal directory. The 'sandbox' sub commands can only be used within a sandbox.The sandbox directory contains sub directories, each should contain a singlepackage. Sub directories may be created with the 'jats extract' command or with the'jats sandbox populate' command.Note: Symbolic links are not supported. They cannot work as he sandbox mechanismrequires that all the packages be contained within a sub directory tree sothat the root of the sandbox can be located by a simple scan of the directorytree.If a package subdirectory contains a file called 'stop' or 'stop.<GBE_MACHTYPE>', then that package will not be considered as a part of thebuild-set. A 'stop' file will prevent consideration all build platforms. The 'stop.<GBE_MACHTYPE>' will only prevent consideration if being built on a GBE_MACHTYPEtype of computer.If the sandbox contains a file called 'buildfilter', then the contents of thefile will be read and used a buildfilter. The file is processed by reading eachline and:=over 4=item *Removing white space at both ends of the line=item *Removing empty lines=item *Lines that start with a # are comments and are removed=item *Remaining lines are joined together to form a buildfilter=back=head1 Create Sandbox=head2 NAMECreate Sandbox=head2 SYNOPSISjats sandbox create [command options]Command Options-help[=n] - Command specific help, [n=1,2,3]-verbose[=n] - Verbose operation-exact - Create sandbox to reproduce exact versions=head2 OPTIONSThe 'create' command takes the following options:=over 8=item -exactWhen this option is specified the sandbox is marked for exact processing ofpackage versions. In this mode the version numbers of the packages in thesandbox are significant. This is ideal for recreating a package-version.The default is for in-exact processing, in which the version numbers of packageswithin the sandbox are not significant. The is ideal for development.=back=head2 DESCRIPTIONThe 'create' command will create a sandbox in the users current directory. It isnot possible to create a sandbox within a sandbox.A sandbox can be created in a directory that contains files and subdirectories.The create command simply places a known directory in the current directory.This directory is used by the sandboxing process. It may be manually deleted, ordeleted with the 'delete' command.=head1 Populate Sandbox=head2 NAMEPopulate a Sandbox=head2 SYNOPSISjats sandbox populate [command options] [packageName packageVersion]Command Options-help[=n] - Command specific help, [n=1,2,3]-toPackage=name - Stop building after package-fromPackage=name - Start building from package-justPackage=name[,name] - Build named packages-ignorePackage=name[,name] - Do not build named packages-excludePackage=name[,name]- Do not extract named package-recurse[=n] - Locate dependencies within packages-all - Populate with all dependencies-missing - Locate missing packages-show - Show packages that would be extracted-test - Do not extract packages-<Other> - Pass options to jats extract=head2 ARGUMENTSThe 'populate' command can take a package name and version as arguments. It willthen populate the sandbox with this package. See 'DESCRIPTION' for details.=head2 OPTIONSThe 'populate' command takes the following options:=over 4=item -excludePackage=name[,name]This option prevents one, or more, packages from populating the sandbox.Packages specified with this option will not be extracted from version controland added to the sandbox.Packages can be itentified in three ways:=over 4=item 1. Package NameAll package versions matching the named package will be excluded.=item 2. Package Name and VersionOnly the specified version of the named package will be excluded. Theuser specifies the package name and version as a single string separated withan underscore. ie: core_devl_2.100.5000.cr=item 3. Package Name and SuffixAll packages matching the named package and project will be excluded. Theuser specifies the package name and project as a single string separated witha dot. ie: core_devl.cr=back=item -recurse[=N]This option will modify the operation of the command such that dependenciesof named packages can also be extracted into the sandbox.The default operation is to only extract named packages. If the option isspecified then all dependent packages are processed. An optional numeric argumentcan be specified to limit the depth of the recursion.=item -allThis option will populate the sandbox will all dependencies of packages that arecurrently in the sandbox.The global options that control recursion will affect the packages that areprocessed.This option cannot be used with the '-missing' option.=item -missingThis option will modify the operation of the dependency recursion scanning suchthat dependent packages that exist in a package archive will not be extracted.Use of this option allows a sandbox to be populated with packages that arerequired by packages in the sandbox, but are not available in a package archive.The global options that control recursion will affect the packages that areprocessed.This option cannot be used with the '-all' option.=item -showThis option will prevent the command from performing the extraction. It willsimply display the names of the packages that would be extracted.=item -testThis option will prevent the command from performing the extraction. It willsimply display the JATS commands that can be used to perform the extraction.=item -<Other>Options not understood by the 'populate' sub command will be passed throughthe package extraction program. Useful options include:=over 4=item *-extractfiles=item *-branch=<branch name>=back=back=head2 DESCRIPTIONThe 'populate' command can be used to assist in populating the sandbox. It hastwo modes of operation.=over 4=item 1Named PackageIf the user specifies both a package name and a package version then the commandwill populate the sandbox with that package and optionally its dependencies.=item 2Determine missing dependenciesIf the user does not specify a package name and version, but does specifythe '-missing' option, then the command will examine the current sandbox anddetermine missing dependent packages. It will then populate the sandbox withthese packages and optionally there dependencies.=back=head2 EXAMPLES=over 4=item *jats sandbox populate package1 version1This command will populate the sandbox with version1 of package1, if it does notalready exist in the sandbox.=item *jats sandbox populate package1 version1 -recurse -missingThis command will populate the sandbox with version1 of package1, if it does notalready exist in the sandbox, together will all the packages dependencies thatare not available in a package archive.=item *jats sandbox populate -recurse -missingThis command will examine the current sandbox and populate the sandbox withpackages that are required to build the packages in the sandbox and thedependencies of these packages, provide the dependent package is not in apackage archive.=item *jats sandbox populateThis command will examine the current sandbox and populate the sandbox withpackages that are required to build the packages in the sandbox. It will notexamine the dependents of these packages.=back=head1 Delete Sandbox=head2 NAMEDelete a a sandbox=head2 SYNOPSISjats sandbox [options] deleteOptions:-help[=n] - Help message, [n=1,2,3]-man - Full documentation [-help=3]-verbose[=n] - Verbose command operation=head2 DESCRIPTIONThe 'delete' command will delete the sandbox's marker directory. The command maybe executed anywhere within the sandbox.Once the sandbox has been deleted, the user must remove the components within thesandbox.=head1 Sandbox Information=head2 NAMEDisplay Sandbox Information=head2 SYNOPSISjats sandbox info [command options]Command Options-help[=n] - Command specific help, [n=1,2,3]-verbose[=n] - Display more information-toPackage=name - Stop building after package-fromPackage=name - Start building from package-justPackage=name[,name] - Build named packages-ignorePackage=name[,name] - Do not build named packages=head2 OPTIONS=over=item B<-verbose[=n]>This options will increase the verbosity of the information being displayed.Values 1 and 2 are described in the detailed 'DESCRIPTION'. Other values arereserved for diagnostic use.=back=head2 DESCRIPTIONThe 'info' command will display information about the build order and thedependencies of packages that it finds within the sandbox.The command works within various levels of verbosity:=over 8=item *No VerbosityThe basic command will display the build order and the externaldependencies. External dependencies may be prefixed with one of thefollowing indicators:=over 8=item '+' Multiple versions of this package are being used by sandboxed components.=item '*' The package cannot be found in any of the package archives.=back=item *Verbosity of 1This level of verbosity will display the build order and detailed informationon the dependencies. The dependencies will be prefixed with:=over 8=item E Dependent Package is external to the sandbox=item I Dependent Package is internal to the sandbox=backExternal dependencies may be prefixed with one of the indicators described forno-verbosity. Additionally the internal consumer of the external package is alsoshown. These are prefixed with a 'U'.=item *Verbosity of 2Reserved for future use=item *Verbosity over 2This should be considered a debug option. Undocumented internal information willbe displayed.=back=head1 Buildfilter=head2 NAMEDisplay and Modify Sandbox buildfilter=head2 SYNOPSISjats sandbox buildfilter [command options] [TARGETS]+Command Options-help[=n] - Command specific help, [n=1,2,3]-man - Same as -help=3Target Names-TARGET - Remove target from the current buildfilter+TARGET - Add target to current buildfilterTARGET - If first target, then reset buildfilterand add target, otherwise add target.=head2 OPTIONSThe 'buildfilter' command takes the following options:=over 8=item -TARGETIf a target name starts with a '-' and is not an option, then that target will beremoved from the current buildfilter.If the named target is not a part of the current buildfilter then nothing will happen.=item +TARGETIf a target name starts with a '+' then that target will be added to the current buildfilter.If the named target is already a part of the current buildfilter then nothing will happen.=item TARGETIf a target name does not start with either a '-' or a '+' then the target will be added to thecurrent buildfilter.If this is the first named target then the build filter will be set to this one target.=back=head2 DESCRIPTIONThe 'buildfilter' command will display and optionally modify the build filter used withinthe sandbox.=head2 EXAMPLESThe commandjats sandbox buildfilterwill simply display the current buildfilter.The commandjats sandbox buildfilter +COBRA +PPC_603Ewill append the build targets COBRA and PPC_603E to the current buildfilter.The commandjats sandbox buildfilter -COBRAwill remove the build target COBRA from the current buildfilter.The commandjats sandbox buildfilter COBRA +PPC_603Eor jats sandbox buildfilter COBRA PPC_603Ewill set the buildfilter to be COBRA and PPC_603E=head1 Command all=head2 NAMEBuild packages in the sandbox=head2 SYNOPSISjats sandbox all [command options] [arguments]Command Options-help[=n] - Command specific help, [n=1,2,3]-toPackage=name - Stop building after package-fromPackage=name - Start building from package-justPackage=name[,name] - Build named packages-ignorePackage=name[,name] - Do not build named packages=head2 ARGUMENTSArguments are passed to the 'make' phase of the process.=head2 OPTIONSThe are command specific options.=head2 DESCRIPTIONThe 'all' command will perform build, if the build files are out of date,followed by a make in each of the packages within the sandbox, in the correctbuild order.Any arguments are passed to the 'make' phase of the process.This command may be used to:=over 8=item *Pickup any build file changes.=item *Resume a failed build.=back=head1 Command build=head2 NAMEBuild packages in the sandbox=head2 SYNOPSISjats sandbox build [command options] [arguments]Command Options-help[=n] - Command specific help, [n=1,2,3]-toPackage=name - Stop building after package-fromPackage=name - Start building from package-justPackage=name[,name] - Build named packages-ignorePackage=name[,name] - Do not build named packages=head2 ARGUMENTSArguments are passed to the 'make' phase of the process.=head2 OPTIONSThe are no command specific options.=head2 DESCRIPTIONThe 'build' command will force a build followed by a make in each of the packageswithin the sandbox, in the correct build order.Any arguments are passed to the 'make' phase of the process.In practice, the 'sandbox all' command is quicker.=head1 Clean=head2 NAMEClean all sandbox components=head2 SYNOPSISjats sandbox clean|clobber [command options]Command Options-help[=n] - Command specific help, [n=1,2,3]-toPackage=name - Stop building after package-fromPackage=name - Start building from package-justPackage=name[,name] - Build named packages-ignorePackage=name[,name] - Do not build named packages=head2 ARGUMENTSNone=head2 OPTIONSNo command specific options=head2 DESCRIPTIONThe 'clean' command will perform a 'jats make clean' in all components in thesandbox.The 'clobber' command will perform a 'jats clobber' in all components in thesandbox.=head1 make=head2 NAMEMake packages in the sandbox=head2 SYNOPSISjats sandbox make [command options] [arguments]Command Options-help[=n] - Command specific help, [n=1,2,3]-toPackage=name - Stop building after package-fromPackage=name - Start building from package-justPackage=name[,name] - Build named packages-ignorePackage=name[,name] - Do not build named packages=head2 ARGUMENTSArguments are passed to the 'make' phase of the process.=head2 OPTIONSThe are no command specific options.=head2 DESCRIPTIONThe 'make' command will perform a 'make' operation in each of the packageswithin the sandbox, in the correct build order.Any arguments are passed to the 'make'.=head1 cmd=head2 NAMEProcess each package with a specified command.=head2 SYNOPSISjats sandbox cmd [command options] [arguments]Command Options-help[=n] - Command specific help, [n=1,2,3]-[no]keepgoing - Ignore errors-toPackage=name - Stop building after package-fromPackage=name - Start building from package-justPackage=name[,name] - Build named packages-ignorePackage=name[,name] - Do not build named packages=head2 ARGUMENTSArguments are passed to a JATS command.=head2 OPTIONS=over=item B<-[no]keepgoing>This options controls the behaviour of the command when an error is encountered.The default opertation is to terminate the command on the package with theerror. This can be modified so that errors are ignored.=back=head2 DESCRIPTIONThe 'cmd' command will pass all of its arguments to JATS in the build directoryof each of the packages within the sandbox, in the package build order.=head2 EXAMPLESThe following command will update all the Subversion-based packages in the sandbox.jats sandbox cmd eprog svn updateNote the use of 'eprog' in the command string. This tells JATS to run the external(to JATS) program. Without this the command would run the JATS-internal commandcalled 'svn' - with different results.The following command will update the dependencies in the build.pl files to matchthose of a nominated release. This will only affect the package versionsexternal to the sandbox, although all version information in the build.plfiles will be updated.jats sandbox cmd upddep -rtagid=12345=head1 Cache=head2 NAMECache dependent packagesjats sandbox [options] cache [command options]Options:-help[=n] - Help message, [n=1,2,3]-man - Full documentation [-help=3]-verbose[=n] - Verbose command operationCommand Options-help[=n] - Command specific help, [n=1,2,3]=head2 ARGUMENTSThe are no command specific arguments.=head2 OPTIONSThe are no command specific options.=head2 DESCRIPTIONThe 'cache' command will cache all external dependent packages into the usersdpkg_archive_cache as defined through the EnvVar GBE_DPKG_CACHE. The result issimilar to the command 'jats sandbox build -cache', without the overhead ofbuilding the sandbox components.This command allows the simple creation of a small development environment thatis not tied to the larger Development Environment. It may then be used in adisconnected mode to perform development.=cut