Rev 6138 | Rev 6192 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
######################################################################### COPYRIGHT - VIX IP PTY LTD ("VIX"). 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 ToolsetFiles;use Pod::Usage; # required for help supportuse Getopt::Long qw(:config require_order); # Stop on non-optionuse Digest;my $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 recursionmy $opt_allSandbox; # Extend scope to entire sandboxmy $opt_processUsedBy; # Process dependants(consumers) not dependents(children)my $opt_keepgoing; # Ignore errorsmy $opt_reScan; # Rescan for buildfiles## 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_BUILDFILTER = $ENV{'GBE_BUILDFILTER'};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_DPKG_REPLICA = $ENV{'GBE_DPKG_REPLICA'};## Globals#my @stopped = (); # Stopped entriesmy @build_order = (); # Build Ordered list of entriesmy %extern_deps; # Hash of external dependenciesmy %packages; # Hash of packagesmy $currentPkgTag; # Tag of the current package - if any#-------------------------------------------------------------------------------# 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$/ );skipBuild(0, @ARGV) if ( $cmd =~ m/^skip$/ );skipBuild(1, @ARGV) if ( $cmd =~ m/^unskip$/ );fingerPrintPkg(@ARGV) if ( $cmd =~ m/^finger/i );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 : skipBuild## Description : Mark the building of a package for skipping## Inputs : Mode -: Skip, 1:Unskip# User commaand line## Returns : Nothing#sub skipBuild{my ($mode, @cmd_opts ) = @_;my $machine;Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts,'machine!' => \$machine,) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Skip Build") if ($opt_help );Error ("Command must be executed from within a Sandbox") unless ( $GBE_SANDBOX );## Determine Sandbox information# Populate global variables#calc_sandbox_info(1);## Display only#unless( @cmd_opts){foreach my $pkg (keys %packages){my $fe = $packages{$pkg};next unless $fe->{buildSkip};my $skipMsg = ($fe->{buildSkip}) ? ' (Build Skipped)' : ' (Build Suppressed)';if ($fe->{buildCurrent}) {$skipMsg .= ' (Current Package)';}Message( ' Name: ' . $fe->{dname} . $skipMsg );}exit 0;}foreach ( @cmd_opts ){## Locate the package#my $pe;if ($currentPkgTag && ($_ eq '.')){if (exists $packages{$currentPkgTag}){$pe = $packages{$currentPkgTag};}}unless ($pe) {foreach my $pkg (keys %packages){my $entry = $packages{$pkg};if ($entry->{dname} eq $_ || $entry->{fname} eq $_ ){$pe = $entry;last;}}}unless ($pe) {Warning("No package found matching: $_");next;}my $skipFile;if ($machine) {$skipFile = catdir($GBE_SANDBOX, 'sandbox_dpkg_archive', '_skip.' . $GBE_MACHTYPE . '.' . $pe->{dname});} else {$skipFile = catdir($GBE_SANDBOX, 'sandbox_dpkg_archive', '_skip.'. $pe->{dname});}Verbose("SkipFile: $skipFile");if ($mode){unlink $skipFile;}else{TouchFile($skipFile);}}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;my $showUsage = 0;my $showFingerPrint = 0;Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts,'verbose:+' => \$show,'usedby' => \$showUsage,'fingerprint' => \$showFingerPrint,) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Sandbox Information") if ($opt_help || $#cmd_opts >=0 );$showUsage = 1 if ($show >= 2);## Determine Sandbox information# Populate global variables#calc_sandbox_info();## Display information#Message ("Type : " . ($opt_exact ? 'Exact' : 'Development') );Message ("Scope : " . ($opt_allSandbox ? 'Entire Sandbox' : $packages{$currentPkgTag}{dname} ));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 ){displayHeader($fe, { indent => ' ', testFingerPrint => $showFingerPrint, showIcons => 1});if ( $show ){Message( DisplayPath (" Path: $fe->{dir}" ));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");}}if ($showUsage && exists($fe->{'usedBy'})){foreach my $edep ( sort {uc($a) cmp uc($b)} @{$fe->{'usedBy'}} ){Message (" U:$packages{$edep}{dname}");}}}## External dependencies flags# * - Package does not exist in dpkg_archive# + - Multiple versions of this package are usedMessage("External Dependencies");foreach my $de ( sort {uc($a) cmp uc($b)} keys %extern_deps ){my @vlist = keys %{$extern_deps{$de}};my $flag = $#vlist ? '+' : '';foreach my $pve ( sort {uc($a) cmp uc($b)} @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 || $showUsage ){foreach my $pkg ( sort {uc($a) cmp uc($b)} @{$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 : fingerPrintPkg## Description : Various finger print operations on the current package## Inputs : @ARGV - Arguments## Returns : WIll not return#sub fingerPrintPkg{my ( @cmd_opts ) = @_;my $opt_generate;my $opt_delete;Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts,'generate!' => \$opt_generate,'delete!' => \$opt_delete,) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Sandbox Finger Print") if ($opt_help );Error ("Command must be executed from within a Sandbox") unless ( $GBE_SANDBOX );## Determine Sandbox information# Populate global variables#calc_sandbox_info(1);## Determine the named packages, or use the current package#my @pkgList;if (@cmd_opts){@pkgList = @cmd_opts;}else{if ( $currentPkgTag ){if (exists $packages{$currentPkgTag}){push @pkgList, $packages{$currentPkgTag}{fname};}}}Error ("Command must be used within a package, or with named packages.")unless @pkgList;## Process all required packages#foreach my $pkgName ( @pkgList){## Locate the package#my $pe;foreach my $pkg (keys %packages){my $entry = $packages{$pkg};if ($entry->{dname} eq $pkgName || $entry->{fname} eq $pkgName ){$pe = $entry;last;}}unless ( $pe ) {Warning ("Cannot locate package: $pkgName");next;}## Recalculate finger print#my $tagFile = getPkgFingerPrintFile($pe);if ($opt_generate){my $ifaceDir = getpkgInterface($pe);if ($ifaceDir){Message ("Generate Fingerprint");FileCreate( $tagFile, genPkgFingerPrint($pe,'Generation') );}else{Warning("Package has not been built. Cannot generate fingerprint: $pkgName");}}elsif ($opt_delete){unlink $tagFile;Message ("Fingerprint file removed");}else{## Test the finger print on the current package#if ( -e $tagFile ){if ( TagFileMatch($tagFile, genPkgFingerPrint($pe, 'Test')) ){Message ("Fingerprint match");}else{Message("Fingerprint missmatch");}}else{Message ("Package does not have a fingerprint file");}}}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_REPLICA,$GBE_DPKG,$GBE_DPLY,$GBE_DPKG_STORE ){next unless ( $dpkg );if ( -d "$dpkg/$name/$ver" ){return 1;}}return 0;}#-------------------------------------------------------------------------------# Function : getpkgInterface## Description : Locate the packages interface directory## Inputs : $fe - Package Entry## Returns : Undef if not found# Path to the packages interface directory#sub getpkgInterface{my ($fe) = @_;## Determine the packages 'interface' directory#my $pSuffix = ($fe->{prj}) ? ( '.' . $fe->{prj}) : '';my $ifaceDir = catdir($GBE_SANDBOX, 'sandbox_dpkg_archive', $fe->{name}, 'sandbox' . $pSuffix . '.int');return unless -f $ifaceDir;$ifaceDir = TagFileRead($ifaceDir);$ifaceDir =~ s~\\~/~g;$ifaceDir =~ s~GBE_SANDBOX/~$GBE_SANDBOX/~;return $ifaceDir;}#-------------------------------------------------------------------------------# 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 : quiet undef - noisy# 1 - quiet## Returns : Will exit if not in a sandbox# Populates global variables# @build_order - build ordered array of build entries#sub calc_sandbox_info{my ($quiet) = @_;my $cacheFile = $GBE_DPKG_SBOX . '/location_cache';## Start from the root of the sandbox#Error ("Command must be executed from within a Sandbox") unless ( $GBE_SANDBOX );my $startDir = Getcwd();chdir ($GBE_SANDBOX) || Error ("Cannot chdir to $GBE_SANDBOX");## Retain subdir so we can figure out a starting package#$startDir = '' unless ($startDir =~ s~^$GBE_SANDBOX/~~);## Locate all packages within the sandbox# These will be top-level directories - one per package#my @build_list;my $currentRootFingerPrint = calc_rootFingerPrint();if (-f $cacheFile && !$opt_reScan){## Read in the location cache# Greatly speeds up the location process#open( my $lf, '<', $cacheFile) || Error ("Read error: $cacheFile. $!");## Check root directory signature# Has the user added or removed packages from the sandbox#my $rootFingerPrint = <$lf>;$rootFingerPrint =~ s~\s*$~~;if ($rootFingerPrint eq $currentRootFingerPrint ){while (<$lf>){my @locationEntry = split($;, $_);my $pname = $locationEntry[0];## Locate the build files in each package# Scan the build files and extract dependancy information#my $bscanner = BuildFileScanner( $pname, 'build.pl','--LocateAll','--LimitDepth=4','--ScanDependencies' );unless ($bscanner->setLocation($_)) {Verbose("Build file missing: Force full scan");@build_list = ();last;}$bscanner->scan();my @blist = $bscanner->getInfo();unless ($quiet) {Warning ("Package does not have build files: $pname") unless ( @blist );Warning ("Package has multiple build files: $pname") if ( $#blist > 0 );}push @build_list, @blist;}}close $lf;}unless (@build_list){Message ("Scanning sandbox for build files");FileCreate($cacheFile, $currentRootFingerPrint );my @locationData;foreach my $pname ( glob("*") ){next if ( $pname =~ m~^\.~ );next if ( $pname =~ m~dpkg_archive$~ );next if ( $pname eq 'CSV' );next if ( $pname eq 'lost+found' );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;}## Locate the build files in each package# Scan the build files and extract dependancy information#my $bscanner = BuildFileScanner( $pname, 'build.pl','--LocateAll','--LimitDepth=4','--ScanDependencies' );$bscanner->scan();my @blist = $bscanner->getInfo();unless ($quiet) {Warning ("Package does not have build files: $pname") unless ( @blist );Warning ("Package has multiple build files: $pname") if ( $#blist > 0 );}push @build_list, @blist;push @locationData, $bscanner->getLocation();}## Save (cache) location information#FileAppend($cacheFile, @locationData);}## 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 );## 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.### Determine the build order#@build_order = ();my $more = 1;my $level = 0;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 ( sort {lc($a) cmp lc($b)} 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};}}}## Test for a skip marker#my $skipFile = catdir($GBE_SANDBOX, 'sandbox_dpkg_archive', '_skip.'. $fe->{dname});my $skipMachFile = catdir($GBE_SANDBOX, 'sandbox_dpkg_archive', '_skip.' . $GBE_MACHTYPE . '.' . $fe->{dname});if ( -f $skipFile || -f $skipMachFile ){Warning("Package marked for skip: $fe->{dname}, $fe->{dir}") unless $quiet;$scan_add = 0;$fe->{buildSkip} = 1;}$fe->{level} = $level;$fe->{buildActive} = $scan_add;$packages{$build} = $fe;push (@build_order, $fe);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();## Just to be sure to be sure#if ( keys %depends ){#DebugDumpData ("depends", \%depends );Error( "Internal algorithm error: Bad dependancy walk","Possible circular dependency");}## Determine FULL internal dependency list for each package# Walk packages in build order so that we can leverage the calculations# already done.#foreach my $fe ( @build_order ){my @pkgBuildOrder;my %pkgSeen;my $key = $fe->{tag};if (exists $packages{$key}{'ideps'}){foreach ( keys %{$packages{$key}{'ideps'}} ){foreach my $ikey (@{$packages{$_}{'AllIdepsOrder'}}){push @pkgBuildOrder, $ikey unless $pkgSeen{$ikey};$pkgSeen{$ikey} = 1;}}}push @pkgBuildOrder, $key;$fe->{'AllIdepsOrder'} = \@pkgBuildOrder;## Is this package in the current directory#if ($startDir){my $matchBase = $startDir . '/';if ($matchBase =~ m~^$fe->{dir}/~){$fe->{buildCurrent} = 1;$currentPkgTag = $key;}}}## Now that we have a full dependency list for each package we can calculate a full# usage list. This is a complete list of all package that depend on a package both directly# and inderectly. Useful for regression testing#foreach my $fe ( @build_order ){foreach my $itag (@{$fe->{'AllIdepsOrder'}}){next if ($itag eq $fe->{tag});push @{$packages{$itag}{usedBy}}, $fe->{tag};}}## If the CWD is within a package then limit the build to that package and# its dependents, unless user specifies entire sandbox.#if ($currentPkgTag && ! $opt_allSandbox ){if (!$opt_processUsedBy ){## Reform the build_order to reflect the current sandbox# The @build_order is an array of package entries# The per-package build order is an array of keys#my @pkgOrder;foreach ( @{$packages{$currentPkgTag}{AllIdepsOrder}}){push @pkgOrder, $packages{$_} ;}# Reform the build order based on original level# Simply done to look pretty - and be consistient when compared with the entire sandbox@build_order = sort {$a->{level} <=> $b->{level}} @pkgOrder;}else{## Reform the build order to reflect the consumers of the current package# This does not include the current package. The assumption is that the package# has been built.## The @build_order is an array of package entries# The per-package build order is an array of keys#my @pkgOrder;foreach ( @{$packages{$currentPkgTag}{'usedBy'}}){push @pkgOrder, $packages{$_} ;}# Reform the build order based on original level# Simply done to look pretty - and be consistient when compared with the entire sandbox@build_order = sort {$a->{level} <=> $b->{level}} @pkgOrder;}}else{$opt_allSandbox = 1;}## 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 on packages in build");%extern_deps = ();foreach my $pe (@build_order){next unless ( $pe->{buildActive} );next unless ( $pe->{'edeps'} );foreach ( keys %{$pe->{'edeps'}} ){push @{$extern_deps{$pe->{'edeps'}{$_}} {$_} }, $pe->{tag};}}}# DebugDumpData("Packages", \%packages);# DebugDumpData ("Order", \@build_order);# DebugDumpData("External Depends", \%extern_deps );}#-------------------------------------------------------------------------------# Function : calc_rootFingerPrint## Description : Calculate a finger print of all the directories in the# sandbox root.## Used to determine if the user has added or remove a package# from the sandbox## Inputs : None## Returns : SHA1 hash#sub calc_rootFingerPrint{my $fpSha1 = Digest->new("SHA-1");foreach my $pname ( glob("*") ){next if ( $pname =~ m~^\.~ );next if ( $pname =~ m~dpkg_archive$~ );next if ( $pname =~ m~^CVS$~ );next unless ( -d $pname );$fpSha1->add($pname);## Include stop files in fingerprint too#$fpSha1->add("$pname/stop" ) if ( -f "$pname/stop" );$fpSha1->add("$pname/stop.$GBE_MACHTYPE" ) if ( -f "$pname/stop.$GBE_MACHTYPE" );}return $fpSha1->hexdigest;}#-------------------------------------------------------------------------------# 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 ) = @_;Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts ) || 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 $active = displayHeader($fe, { showPath => 1 });if ($active){my $dir = $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;my $opt_skip = 1;my $opt_clean = 0;## Extract and options#Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts,'skip!' => \$opt_skip,'clean!' => \$opt_clean,) || 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 $active = displayHeader($fe, { showPath => 1 });if ($active) {## Determine build success tag file# If the tag file exists, then see if any files in the files in the package source are more# recent than the tag file#my $mustBuild = 1;my $sigMatch = 0;my $tagFile = getPkgFingerPrintFile($fe);if ( -e $tagFile ) {if ( TagFileMatch($tagFile, genPkgFingerPrint($fe, 'Test')) ) {$sigMatch = 1;if ($opt_skip) {$mustBuild = 0;} else {$mustBuild = 2;}} else {}} else {$mustBuild = 2;$sigMatch = -1;}#Debug0("Skip: $opt_skip, sigMatch: $sigMatch, Build: $mustBuild, Opts: @make_opts",);if ($mustBuild){if (1){unlink $tagFile;my $dir = $fe->{dir};my $result;$result = JatsCmd( "-cd=$dir", 'build', @build_opts);if ($result){if ($opt_keepgoing){Warning( "Build Cmd failure - Keep going");next;}Error ("Build Cmd failure");}## Skip make if we have a prebuilt package#my $mustMake = 1;if ($mustMake){JatsCmd( "-cd=$dir", 'make', 'clean') if $opt_clean;$result = JatsCmd( "-cd=$dir", 'make', @make_opts);if ($result){if ($opt_keepgoing){Warning( "Make Cmd failure - Keep going");next;}Error ("Make Cmd failure");}}}FileCreate( $tagFile, genPkgFingerPrint($fe,'Generation') );}else{Message ("No file changes since last build. Skipping")}}}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 $active = displayHeader($fe, { showPath => 1 });if ($active){my $dir = $fe->{dir};my $result = JatsCmd( "-cd=$dir", @cmd, @cmd_opts);if ($result){if ($opt_keepgoing){Warning("Command Failure");}else{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");Error ("GBE_DPKG_CACHE not defined") unless $GBE_DPKG_CACHE;calc_sandbox_info();JatsTool ('cache_dpkg', "core_devl/jats2_current" );## 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, @_ )},'entireSandBox!' => \$opt_allSandbox,'users!' => \$opt_processUsedBy,'keepgoing!' => \$opt_keepgoing,'rescan!' => \$opt_reScan,);## 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 : displayHeader## Description : Display a build header## Inputs : $fe - Build entry# $indent - Indent text# $showPath - Boolean## Returns : True if this entry is to be fully process# False if its being skipped#sub displayHeader{my $fe = shift @_;my $args = pop @_ if (@_ > 0 and UNIVERSAL::isa($_[-1],'HASH'));# my ($fe, %args) = @_;my $indent = $args->{indent} || '';my $showPath = $args->{showPath};my ($status, $estatus) = addBuildInfo($fe, $args);my $msg1 = $indent . 'Level:' . $fe->{level} . " [$status]" . ' Name: ' . $fe->{dname} . $estatus;if ( $showPath) {my $msg1Len = length($msg1);if ($msg1Len < 80) {$msg1 .= ' ' . '=' x (79 - $msg1Len);}}Message( $msg1 , $showPath ? DisplayPath (" Path: $fe->{dir}" ) : undef);return $fe->{buildActive};}#-------------------------------------------------------------------------------# Function : addBuildInfo## Description : Add some build info status# It will have a .sig file if its been sucessfully built## Inputs : $fe - Package info## Returns : True - sig file present#sub addBuildInfo{my ($fe, $args) = @_;my $txt = '';my $statusB = ' ';my $statusF = ' ';my $statusS = ' ';unless ($fe->{buildActive}) {$txt .= ($fe->{buildSkip}) ? ' (Build Skipped)' : ' (Build Suppressed)';$statusS = ($fe->{buildSkip}) ? 'S' : 's';}if ($fe->{buildCurrent}){$statusB = '*';$txt .= ' (Current Package)';}my $sigFile = getPkgFingerPrintFile($fe);my $tagFile = $sigFile;if (-f $sigFile){$sigFile =~ s~ffp$~nob~;if (-f $sigFile) {$txt .= ' [NoBuild]';$statusB = '-';# } elsif (locatePreBuiltPackage($fe)) {# $txt .= ' [PreBuilt]';# $statusB = 'P';} else {$txt .= ' [Built]';$statusB = 'B';}if ($args->{testFingerPrint}){if ( TagFileMatch($tagFile, genPkgFingerPrint($fe, 'Test')) ){$txt .= ' [GoodFinger]';$statusF = 'G';} else {$txt .= ' [BadFinger]';$statusF = 'b';}}}return "$statusS$statusB$statusF", $txt ;}#-------------------------------------------------------------------------------# 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 : genPkgFingerPrint## Description : Generate a fingerprint over all files in the packages tree as# well as the package dependencies## Only used to detect changes to files in the subdir## This version does not actually generate a fingerprint over# the data file, rather the metadata of the file. This is much (much)# faster as there is no file i/o.## It does assume that the file metadata will change if the file is changed# Will also detect the addition / deletion of files## Note: This siganture is not machine - type safe# It will vary between machines (windows/Linux/...)## Inputs : $fe - Package entry of the package to process# $mode - Diagnostic: mode## Returns : A SHA1 hash over all the files##sub genPkgFingerPrint{my ($fe, $mode) = @_;my $genPkgFingerPrintSha1;my $genPkgFingerPrintCount;my @fpdata;## Get the package GbeFiles.cfg file# This is held in the interface directory and is created during the build# Since the fingerprint is only genertated AFTER a successful build the file will always# be available## All we need from this file is a list of Src directories that were discovered during# the build. Unfortuanatley they are not always below the root of the package.#my $ifaceDir = getpkgInterface($fe);return 0 unless defined $ifaceDir;return 0 unless ToolsetFiles::GetDataFile($ifaceDir);## Generate a list of directories in the package# This is the root directory and all other Src directories discovered#my @dirList = ToolsetFiles::GetSubTrees($ifaceDir);Error ("Internal:ToolsetFiles::GetDirList for $fe->{dname} not populated" ) unless @dirList;## Create the hash#$genPkgFingerPrintSha1 = Digest->new("SHA-1");push @fpdata, $mode;## Include all dependent packages in the fingerprint# We are using the sandbox fingerprint of dependent packages# This will ensure that a change in a package will ripple through#foreach my $idep ( sort keys %{$fe->{ideps}}){my $ipkg = $packages{$idep};my $tagFile = getPkgFingerPrintFile($ipkg);my $tag = TagFileRead($tagFile);my $text = "$tagFile: $tag";$genPkgFingerPrintSha1->add($text);#Debug0("genPkgFingerPrint: $text, ", $genPkgFingerPrintSha1->clone->hexdigest() );push @fpdata, $text . ':' . $genPkgFingerPrintSha1->clone->hexdigest();}## Anonymous sub: findFile wanted function# Unlike the generation of the package signature, we don't need to# exclude any files.#my $wanted = sub {my $item = $File::Find::name;## Get file info# Kill of the last access time - not useful## Need to exclude files that change during a null build# /interface/GbeFiles.cfg# /build.log## Symlinks present problems.# Some packages generate symlinks as they create file system images. The# links are not designed to be interpreted in the context of the current# computer. As a result.# Some may be broken - on the current machine# Some may address files that change - ie: /proc, /var, /dev# Some may address files that such as /afc, /root# Don't want to discard all symlinks. Many of them will address package# dependencies and we do want to detect changes, but those changes will# be picked up by the packages fingerprint.## Directories also appear to be a problem# The created and modified date-times appear to be modified for no good reason## Current solution: Do not include 'stat' data for ANY symlink or directory#my @data = stat($item);my $text;if ( $item =~ m~/build.log$~ || $item =~ m~/GbeFiles.cfg$~) {$text = "$item : SKIPPED FILE : ";} elsif (-d $item ) {$text = "$item : DIRECTORY"} elsif (-l $item ) {if ( ! @data) {$text = "$item : BROKEN SYMLINK : ";} else {my $linkTarget = readlink($item) || 'Read Error';$text = "$item :SYMLINK: $linkTarget";}} else {$data[8] = 0; # atime - will change$data[12] = '-'; # blocks - seen to change for unknown reasons$text = "$item : @data";}$genPkgFingerPrintCount++;$genPkgFingerPrintSha1->add($text);#Debug0("genPkgFingerPrint: $text, ", $genPkgFingerPrintSha1->clone->hexdigest() );push @fpdata, $text . ':' . $genPkgFingerPrintSha1->clone->hexdigest();};## Process all files in the package#$genPkgFingerPrintCount = 0;my $dir = $fe->{dir};File::Find::find( { wanted => $wanted , no_chdir => 1}, @dirList );#Debug0("genPkgFingerPrint: $dir, $genPkgFingerPrintCount, ", $genPkgFingerPrintSha1->clone->hexdigest() );push @fpdata, $dir . ':'. $genPkgFingerPrintCount . ':' . $genPkgFingerPrintSha1->clone->hexdigest();## Debugging - delete later# Save FP data to a file## my $fpDebugFile = catdir($GBE_SANDBOX, 'sandbox_dpkg_archive', $fe->{name} . '.' . $fe->{prj} . '_' . time() . '.fpd');# Debug0("fpDebugFile: $fpDebugFile");# FileCreate($fpDebugFile, @fpdata);return $genPkgFingerPrintSha1->hexdigest;}#-------------------------------------------------------------------------------# Function : getPkgFingerPrintFile## Description : Return the package file that contains the packages Fast Finger Print## Inputs : $fe - Package entry## Returns : Full path to the packages fingerprint tag file#sub getPkgFingerPrintFile{my ($fe) = @_;my $tagFile = catdir($GBE_SANDBOX, 'sandbox_dpkg_archive', $fe->{name}, 'sandbox.' . $fe->{prj} . '.ffp');return $tagFile;}#-------------------------------------------------------------------------------# 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 packages-entireSandbox - Process the entire sandbox-users - Process package users, not dependencies-[no]keepgoing - Ignore errors-[no]reScan - Recalculate and cache sandbox structureCommands: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 buildfilter[un]skip - Mark a package to be skipped during the buildfingerprint - Various fingerprint operationscmd - 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 specified 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 described 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 described 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 described under the '-toPackage' option.=item B<-[no]entireSandbox>This option will override the automatic package localisation that will occur if the user starts the comamndwithin a subdirectory of a package within the sandbox and will process the entire sabdbox.If the user start the command within a subdirectory of a package then the sandbox commands will be localisedto the current package and the dependencies of the package.=item B<-[no]users>This option will completely change the packages considered to be built. The normal operation is to considerthe current package and all packages that it depends upon.This option will consider all packages that 'use' the current package, either directly or indirectly. It does notinclude the 'current' pakage in this list. The assumption is that the current package has been sucessfully builtand needs to tested.This option will work when they is a current package to be processed and not the entire sandbox.The intended purpose of this option is to simplify regression testing.=item B<-[no]keepgoing>This options controls the behaviour of the command when an error is encountered.The default operation is to terminate the command on the package with theerror. This can be modified so that errors are ignored.=item B<-[no]reScan>This option controls the process of locating build files within the sandbox.The default operation is to scan the sandbox and to cache the location of the build files.If a package is added or removed from the sandbox, then the sandbox will need to be rescanned.Jats will detect when a package has been added or removed, but if the internal structure of thepackages has changed the cached data may be incorrect.=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 the 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 for 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-entireSandbox - Process the entire sandbox-users - Process package users, not dependencies-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 identified 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 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-usedby - Display package usage 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-entireSandbox - Process the entire sandbox-users - Process package users, not dependencies-fingerprint - Display fingerprint information=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.=item B<-usedby>This option will list all packages that use the current package, both directly andindirectly. These are packages that should be tested whan changes are made to thepackage.=item B<-fingerprint>This option will cause the information display to include that status of each packages fingerprint.This will slow down the display as the calculation can be time consuming.=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 2Usage information will also be displayed. This is the same as invoking the'-usedby' option.=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 Skip Build=head2 NAMEMark a package to be skipped during the build=head2 SYNOPSISjats sandbox [un]skip [command options] [PackageName]+Command Options-help[=n] - Command specific help, [n=1,2,3]-man - Same as -help=3-[no]machine - Skip on on this type of machine=head2 ARGUMENTSArguments to the 'skip' command are the names of packages to be marked.If no packages are named then the command will display all packages that are marked to be skipped.If the named package is '.', then the current package will be excluded.=head2 OPTIONSThe 'skip' command takes the following options:=over 8=item -[no]machineThis option will flag that the package will be skipped only on this type of build machine.=back=head2 DESCRIPTIONThe 'skip' command marked the named packages to be skipped during the build, or the mark will be removed.=head2 EXAMPLESThe commandjats sandbox skip package1will mark package1 to be skipped during the following builds.=head1 Sandbox Finger Print=head2 NAMEVarious fingerprint operations=head2 SYNOPSISjats sandbox finger[print] [options]Command Options-help[=n] - Command specific help, [n=1,2,3]-man - Same as -help=3-[no]generate - Generate a fingerprint over a package-[no]delete - Delete the fingerprint information=head2 ARGUMENTSArguments to the 'fingerprint' command are the names of packages to be processed.If no packages are named then the command will process the current package, if any.=head2 OPTIONSThe 'fingerprint' command takes the following options:=over 8=item -[no]generateThis option will cause the fingerprint of the package to be regenerated.=item -[no]deleteThis option will delete the fingerprint information associated wit a package.=back=head2 DESCRIPTIONThe 'fingerprint' command, will by default, examine the packages fingerprint and reportif the package has been modified since the fingerprint was created.Options allow different modes of operation.A fingerprint may only be created after the 'build.pl' file has been created. It requires thebuild process to generate metadata about the package.=head2 EXAMPLESThe commandjats sandbox fingerprint -generatewill regenerate the fingerprint of the current package. Useful after trivial edits toenable the sandbox builder to bypass the package and not to rebuild it and all of its dependents.=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-entireSandbox - Process the entire sandbox-users - Process package users, not dependencies-[no]keepgoing - Ignore errors-[no]skip - Skip if no source change (default:skip)=head2 ARGUMENTSArguments are passed to the 'make' phase of the process.=head2 OPTIONS=over=item B<-[no]skip>This operation overides the default smart building mechanism.By default, a package will not be built if the last build was successful andthere has not been any change to the source of the package, since the lastsuccesful build.=back=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-entireSandbox - Process the entire sandbox-users - Process package users, not dependencies-[no]keepgoing - Ignore errors-[no]skip - Skip if no source change (default:skip)=head2 ARGUMENTSArguments are passed to the 'make' phase of the process.=head2 OPTIONS=over=item B<-[no]skip>This operation overides the default smart building mechanism.By default, a package will not be built if the last build was successful andthere has not been any change to the source of the package, since the lastsuccesful build.=back=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-entireSandbox - Process the entire sandbox-users - Process package users, not dependencies-[no]keepgoing - Ignore errors=head2 ARGUMENTSNone=head2 OPTIONS=over=item B<-[no]keepgoing>This options controls the behaviour of the command when an error is encountered.The default operation is to terminate the command on the package with theerror. This can be modified so that errors are ignored.=back=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-entireSandbox - Process the entire sandbox-users - Process package users, not dependencies=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-entireSandbox - Process the entire sandbox-users - Process package users, not dependencies=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 operation 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