Rev 7295 | Rev 7441 | 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 thismy $SCANDEPTH = 3; # Almost a constant## 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 buildfilesmy $opt_multiBuilders = 0; # How handle packages with multiple bulders: 0:Error, 1:Report, 2:Ignoremy $opt_onlyLevel = 0; # Single level processing## 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'};my $GBE_DPKG_ESCROW = $ENV{'GBE_DPKG_ESCROW'};## 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 anymy $scanDepth; # Depth for build file scanmy $maxDname = 0; # Pretty display## Known files#$GBE_DPKG_SBOX = '' unless defined $GBE_DPKG_SBOX;my $cacheFile = $GBE_DPKG_SBOX . '/location_cache';my $depthFile = $GBE_DPKG_SBOX . '/scanDepth';my $filterFile = $GBE_DPKG_SBOX . '/buildfilter';#-------------------------------------------------------------------------------# 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 );# Flag to subcommands that the build is occuring within a SANDBOX# Example usage: Prevent gradle subproject inclusion$ENV{'GBE_SANDBOX_BUILD'} = 1;## 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 );testLinks(@ARGV) if ( $cmd =~ m/^testlinks/i );setScanDepth(@ARGV) if ( $cmd =~ m/^scandepth/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{my $opt_force;GetOptions ("help:+" => \$opt_help,"manual:3" => \$opt_help,"exact" => \$opt_exact,"force!" => \$opt_force,) || 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 && !$opt_force );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;my $showDependencies = 1;my $showOrder = 1;my $showPath = 0;Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts,'verbose:+' => \$show,'usedby' => \$showUsage,'fingerprint!' => \$showFingerPrint,'dependencies!' => \$showDependencies,'buildorder!' => \$showOrder,'path!' => \$showPath,) || 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 $filterFile) ? ' - Local to sandbox' : ''));if ($showOrder){Message ("Build Order");foreach my $pname ( @stopped ) {Message( " Level:" . "--" . " [---] Name: " . $pname . ' (Stopped)');}foreach my $fe ( @build_order ){displayHeader($fe, { indent => ' ', testFingerPrint => $showFingerPrint, showSimplePath => $showPath });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 usedif ($showDependencies){Message("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");Verbose ("Fingerprint file: $tagFile");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 : testLinks## Description : Test the lnk files in the sandbox directory# These may be stake after packages are deleted## Inputs :## Returns : This function will not return#sub testLinks{my ( @cmd_opts ) = @_;my $opt_delete;GetOptions ("help:+" => \$opt_help,"manual:3" => \$opt_help,'delete!' => \$opt_delete,) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Sandbox Test Links") if ($opt_help || ($#ARGV >= 0) );Error ("Command must be executed from within a Sandbox") unless ( $GBE_SANDBOX );## Process all packages in the sandbox#foreach my $linkFile ( glob "$GBE_DPKG_SBOX/*/*.lnk"){my $pkg = getPackageLink( TagFileRead($linkFile) );unless ( -d $pkg ){if ($opt_delete){Message ("Delete: $linkFile");unlink $linkFile;}else{Message ("Broken link: $pkg", "Source link: $linkFile" );}}}exit (0);}#-------------------------------------------------------------------------------# Function : getPackageLink## Description : Read a package link file and process the data to generate# the link to the package## Inputs : $linkFile - File that contains the link## Returns : The resolved link#sub getPackageLink{my ($linkFile) = @_;my $pkg = TagFileRead($linkFile);$pkg =~ s~\\~/~g;if ($pkg =~ s~^GBE_SANDBOX/~$GBE_SANDBOX/~){# If the target sandbox is in the 'deploymode' then the package# will not be in the expected location. It will be in a 'build/deploy'# subdir. Remove the pkg/name dir to get to the root of the packagemy @dirs = File::Spec->splitdir( $pkg );splice(@dirs, -2);my $deployBox = catdir(@dirs, 'build', 'deploy');$pkg = $deployBox if ( -d $deployBox);}return $pkg;}#-------------------------------------------------------------------------------# Function : setScanDepth## Description : Set the depth of the build file scan usd in this sandbox## Inputs : Command line arguments## Returns : This function will not return#sub setScanDepth{my ( @cmd_opts ) = @_;GetOptions ("help:+" => \$opt_help,"manual:3" => \$opt_help,) || Error ("Invalid command line" );SubCommandHelp( $opt_help, "Sandbox Scan Depth") if ($opt_help || ($#ARGV > 0 ));Error ("Command must be executed from within a Sandbox") unless ( $GBE_SANDBOX );if (defined $ARGV[0]){FileCreate($depthFile, $ARGV[0]);## Force a rescan of the cached information#unlink $cacheFile;exit 0;}## Report the current scan depth#getScanDepth();Information ("Build File scan depth: ". $scanDepth);exit (0);}#-------------------------------------------------------------------------------# Function : getScanDepth## Description : Determine the build file scan depth for the current sandbox# Non-default data is maintained in a data file## Inputs : None## Returns : Nothing#sub getScanDepth{my $depth = $SCANDEPTH;if ( -f $depthFile){$depth = TagFileRead($depthFile);my $invalid = 0;if ($depth !~ m~^\d+$~) {$invalid = 1;} elsif ( $depth lt 0) {$invalid = 1;}if ($invalid){unlink $depthFile;Warning ("Invalid scandepth file. File deleted.");$depth = $SCANDEPTH;}}$scanDepth = $depth;}#-------------------------------------------------------------------------------# 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_ESCROW,'--NotEscrow',$GBE_DPKG_REPLICA,$GBE_DPKG,$GBE_DPLY,$GBE_DPKG_STORE ){next unless ( $dpkg );if ( $dpkg eq '--NotEscrow' ){last if ($GBE_DPKG_ESCROW);next;}if ( -d "$dpkg/$name/$ver" ){Verbose("Exists: $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# $opt_onlyLevel## 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 $buildFileDepth = 0;getScanDepth();## 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>){s~\s+$~~;my @locationEntry = split($;, $_);my $pname = $locationEntry[0];if ($locationEntry[1] eq ':STOP:') {push @stopped, $pname;Verbose("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=' . $scanDepth,'--ScanDependencies','--Stop' );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 );if ( -d $pname . '/sandbox_dpkg_archive' ) {Warning("Nested sandbox ignored: $pname");next ;}Verbose ("Package discovered: $pname");if ( -f "$pname/stop" || -f "$pname/stop.$GBE_MACHTYPE" ){push @stopped, $pname;Warning("Package contains stop file: $pname");push @locationData, join($;, $pname, ':STOP:');next;}## Locate the build files in each package# Scan the build files and extract dependancy information#my $bscanner = BuildFileScanner( $pname, 'build.pl','--LocateAll','--LimitDepth=' . $scanDepth,'--ScanDependencies','--Stop' );$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();## Determine max build file depth - just to show user#foreach ( @blist){my $count = () = $_->{dir} =~ m~/~g;$buildFileDepth = $count if ($count > $buildFileDepth)}}Message ("Max Build File Depth : " . ($buildFileDepth + 1));Message ("Build File Scan Depth: ". $scanDepth) if ($SCANDEPTH ne $scanDepth);## 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});if (length ($be->{dname}) > $maxDname ) {$maxDname = length ($be->{dname});}# 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;}## Detect, process and report packages that have multiple builders# An error that can be ignored#foreach my $dname ( sort keys %multi ){my $errFn = $opt_multiBuilders ? \&Warning : \&ReportError;if ( scalar @{$multi{$dname}} > 1 ){my @dirList;foreach my $be (@{$multi{$dname}}) {push @dirList, $be->{dir};}&$errFn("Multiple builders for : $dname", @dirList ) unless $opt_multiBuilders > 1;}else{## Add into dependency struct#foreach my $be (@{$multi{$dname}}) {$depends{$be->{tag}} = $be;}}}%multi = ();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;}## Select one level#if ($opt_onlyLevel && $opt_onlyLevel != $level) {$scan_add = 0;}$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 ) = @_;my $opt_reverse;Getopt::Long::Configure('pass_through');getOptionsFromArray ( \@cmd_opts,'reverse!' => \$opt_reverse,) || Error ("Invalid command line" );SubCommandHelp( $opt_help, $hcmd) if ($opt_help );## Determine Sandbox information# Populate global variables#calc_sandbox_info();if ($opt_reverse) {@build_order = reverse @build_order;}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 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: $dir");}## 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: $dir");}}}Verbose ("Save fingerprint: $tagFile");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#UniquePush (\@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#@filter_list = sort @filter_list;Message('BuildFilter:', @filter_list);## Write out a new file#if ($modified){Error ("Command must be executed from within a Sandbox") unless ( $GBE_SANDBOX );FileCreate($filterFile, @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) = @_;## Helper to parse --multiBuilders#my $parseMulti = sub {my ($ref, $value) = @_;$value = 'error' unless length($value);my %valid = (error => 0, report => 1, ignore => 2);unless (exists $valid{lc $value}) {die ("Invalid option for --$ref->{name}\n");};$opt_multiBuilders = $valid{lc $value};};## 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,'multiBuilders:s' => $parseMulti,'onlylevel:i' => \$opt_onlyLevel,);## 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, if the entry is active# ie: Between a --From and --To## Inputs : $fe - Build entry# Hash of options# indent => Text - Indent text# showPath => Bool# showSimplePath => Bool# testFingerPrint => Bool## Returns : True if this entry is to be fully process# False if its being skipped#sub displayHeader{my $fe = shift @_;if ($fe->{buildActive} || $fe->{buildSkip}){my $args = pop @_ if (@_ > 0 and UNIVERSAL::isa($_[-1],'HASH'));# my ($fe, %args) = @_;my $indent = $args->{indent} || '';my $showPath = $args->{showPath};my $showSimplePath = $args->{showSimplePath};my ($status, $estatus) = addBuildInfo($fe, $args);$estatus = '' if $showSimplePath;my $msg1 = $indent . sprintf('Level:%02d [%s] Name: %s', $fe->{level}, $status, $fe->{dname} . $estatus );if ($showSimplePath) {my $msg1Len = length($msg1);$msg1 = sprintf("%-*s Path: %s", 26 + $maxDname ,$msg1, $fe->{dir});}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## StatusFlags# Position 1: S - Build Skipped, s - Build Suppressed# Position 2: Blank - Not processed , - = NoBuild, B - Built * - Current package# Position 3: E - Exists in dPkg, L - Local, M - Both local and dPkg# Position 4: G - Good Fingerprint, b - Bad Fingerprint## Inputs : $fe - Package info## Returns : StatusFlags# StatusText#sub addBuildInfo{my ($fe, $args) = @_;my $txt = '';my $statusS = ' ';my $statusB = ' ';my $statusP = ' ';my $statusF = ' ';unless ($fe->{buildActive}) {$txt .= ($fe->{buildSkip}) ? ' (Build Skipped)' : ' (Build Suppressed)';$statusS = ($fe->{buildSkip}) ? 'S' : 's';}if ($fe->{buildCurrent}){$statusB = '*';$txt .= ' (Current Package)';}my $fpFile = getPkgFingerPrintFile($fe);if (-f $fpFile){my $nobFile = $fpFile;$nobFile =~ s~ffp$~nob~;if (-f $nobFile) {$txt .= ' [NoBuild]';$statusB = '-';} else {$txt .= ' [Built]';$statusB = 'B';}if ($args->{testFingerPrint}){if ( TagFileMatch($fpFile, genPkgFingerPrint($fe, 'Test')) ){$txt .= ' [GoodFinger]';$statusF = 'G';} else {$txt .= ' [BadFinger]';$statusF = 'b';}}}my $preBuilt = check_package_existance($fe->{name}, $fe->{version});if ($preBuilt) {$txt .= ' [dPkg]';$statusP = 'E';}my $plink = catdir( $GBE_DPKG_SBOX, $fe->{name}, 'sandbox.' . $fe->{prj} . '.lnk' );my $linkTarget = getPackageLink ($plink);Verbose ("Sandbox link: $plink -> $linkTarget");if (-d $linkTarget) {$txt .= ' [Local]';if ($preBuilt) {$statusP = 'M';} else {$statusP = 'L';}}return "$statusS$statusB$statusP$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 signature is not machine - type safe# It will vary between machines (windows/Linux/...)# At the moment the Sandbox is really a single machine tool## 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;## Generate a hash of toolset files and toolset-internal directories# These won't be included in the finger print as they are subject# to change by a 'build'.#my %toolsetFiles = map { $_ => 1 } ToolsetFiles::GetFiles($ifaceDir, 1);my %toolsetDirs = map { $_ => 1 } ToolsetFiles::GetBuildDirs($ifaceDir);## 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# These files are tracked in GbeBuild.cfg# Need to directories that change during a null build# These files are tracked in GbeBuild.cfg## 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 ( exists $toolsetFiles{$item}) {$text = "$item : SKIPPED FILE : ";} elsif (-d $item ) {$text = "$item : DIRECTORY";if ( exists $toolsetDirs{$item}) {$File::Find::prune = 1;$text = "$item : SKIP INTERNAL 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 reasonsfor my $ii ( 0 .. $#data) {unless (defined $data[$ii]) {$data[$ii] = 'xx';}}$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:-onlyLevel=number - Limit building to one level-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 dependenciesOptions common to all commands:-[no]keepgoing - Ignore errors-[no]reScan - Recalculate and cache sandbox structure-multiBuilders=mode - Handle conflicting packages. error|report|ignoreCommands: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 operationstestlinks - Test / Delete broken package symlinksscandepth - Set/Display the build file scan depthcmd - 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<-onlyLevel=number>This option is available in all commands that process multiple packages.Package processing will be limited to the specified level.This can be used to perform a multi-machine build. Level-1 builds are performedon all machines and the results merged into the common package store, then Level-2 buildsare performed on all machines.Level-1 packages have no dependencies.Level-2 packages only have dependancies on Level-1 packagesLevel-N packages only have dependancies on packages below Level N-1=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 commandwithin a subdirectory of a package within the sandbox and will process the entire sanbbox.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.=item B<-multiBuilders=mode>If a package-name can be built by multiple packages then the sandbox processing willnormally report an error.This option allow the error to be reported as a warning or to be ignored altogether. The named package will beexcluded from the build set.Valid values for 'mode' are: error, report and ignore. The default mode is 'error'.One workaround is to use a 'stop' file. Any directory that contains 'stop' file will not be scanned forbuild files. If a 'stop' file is added then it may defeat caching of build information. The information willneed to be refreshed with the '--rescan' option. The 'stop' file should not be version controlled.=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=for comment ===================================================================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-[no]force - Force creation of nested sandbox=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.=item -[no]forceNormally a sandbox should not be created within another sandbox.The use of this option overrides this limitation.A sandbox with a sandbox will be ignored by the parent sandbox.=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.=for comment ===================================================================head1 Populate Sandbox=head2 NAMEPopulate a Sandbox=head2 SYNOPSISjats sandbox populate [command options] [packageName packageVersion]Common Options:-help[=n], -man - Command specific help, [n=1,2,3]-onlyLevel=number - Limit building to one level-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-multiBuilders=mode - Handle conflicting packages. error|report|ignore-[no]keepgoing - Ignore errors-[no]reScan - Recalculate and cache sandbox structureCommand Specific Options-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=for comment ===================================================================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.=for comment ===================================================================head1 Sandbox Information=head2 NAMEDisplay Sandbox Information=head2 SYNOPSISjats sandbox info [command options]Common Options:-help[=n], -man - Command specific help, [n=1,2,3]-onlyLevel=number - Limit building to one level-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-multiBuilders=mode - Handle conflicting packages. error|report|ignore-[no]keepgoing - Ignore errors-[no]reScan - Recalculate and cache sandbox structureCommand Specific Options-verbose[=n] - Display more information-usedby - Display package usage information-fingerprint - Display fingerprint information-[no]dependencies - Display external dependencies (default)-[no]buildorder - Display build order (default)-[no]path - Display path to package=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.=item B<-[no]dependencies>This option will cause the information display to include all the external dependencies.=item B<-[no]buildorder>This option will cause the information display to include all the build order.=item B<-[no]path>This option will cause the information display to include all the path to the build file=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=for comment ==================================================================The build infomation display will show the state of each package as a cryptic set of four characters within square brackets.The first character may be one of:=over 8=item S - Build is being skipped=item s - Build is being suppressed=item blank - Build is active=backThe second character may be one of:=over 8=item * - This is the current package=item B - Has been build=item - - A 'noBuild'. Will not be built due to build filters=backThe third character may be one of:=over 8=item E - The package only exists in dpkg_archive=item L - The package has been built and exists within the sandbox=item M - The package has been built and exists in both the sandbox and dpkg_archive=backThe fourth character may be one of (requires the -fingerprint option):=over 8=item G - A good fingerprint=item B - A bad fingerprint. Files in the package have been modified since the last build.=item blank - The package has not been built and no fingerprint has been calculated.=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=for comment ===================================================================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.=for comment ===================================================================head1 Command all=head2 NAMEBuild packages in the sandbox=head2 SYNOPSISjats sandbox all [command options] [arguments]Common Options:-help[=n], -man - Command specific help, [n=1,2,3]-onlyLevel=number - Limit building to one level-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-multiBuilders=mode - Handle conflicting packages. error|report|ignore-[no]keepgoing - Ignore errors-[no]reScan - Recalculate and cache sandbox structureCommand Specific Options-[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=for comment ===================================================================head1 Command build=head2 NAMEBuild packages in the sandbox=head2 SYNOPSISjats sandbox build [command options] [arguments]Common Options:-help[=n], -man - Command specific help, [n=1,2,3]-onlyLevel=number - Limit building to one level-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-multiBuilders=mode - Handle conflicting packages. error|report|ignore-[no]keepgoing - Ignore errors-[no]reScan - Recalculate and cache sandbox structureCommand Specific Options-[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.=for comment ===================================================================head1 Clean=head2 NAMEClean all sandbox components=head2 SYNOPSISjats sandbox clean|clobber [command options]Common Options:-help[=n], -man - Command specific help, [n=1,2,3]-onlyLevel=number - Limit building to one level-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-multiBuilders=mode - Handle conflicting packages. error|report|ignore-[no]keepgoing - Ignore errors-[no]reScan - Recalculate and cache sandbox structure=head2 ARGUMENTSNone=head2 OPTIONSThe are no 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.=for comment ===================================================================head1 make=head2 NAMEMake packages in the sandbox=head2 SYNOPSISjats sandbox make [command options] [arguments]Common Options:-help[=n], -man - Command specific help, [n=1,2,3]-onlyLevel=number - Limit building to one level-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-multiBuilders=mode - Handle conflicting packages. error|report|ignore-[no]keepgoing - Ignore errors-[no]reScan - Recalculate and cache sandbox structure=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'.=for comment ===================================================================head1 cmd=head2 NAMEProcess each package with a specified command.=head2 SYNOPSISjats sandbox cmd [command options] [arguments]Common Options:-help[=n], -man - Command specific help, [n=1,2,3]-onlyLevel=number - Limit building to one level-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-multiBuilders=mode - Handle conflicting packages. error|report|ignore-[no]keepgoing - Ignore errors-[no]reScan - Recalculate and cache sandbox structureCommand Specific Options-[no]reverse - Reverse the processing order=head2 ARGUMENTSArguments are passed to a JATS command.=head2 OPTIONSThe 'cmd' command takes the following options:=over 8=item -[no]reverseThis option will controlls the order in which the packages will be processed.The default option is 'noreverse'. Packages will be processed in the build order.=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=for comment ===================================================================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.=for comment ===================================================================head1 Sandbox Test Links=head2 NAMETest and delete sandbox link files=head2 SYNOPSISjats sandbox testlinks [options]Command Options-help[=n] - Command specific help, [n=1,2,3]-man - Same as -help=3-[no]delete - Delete bad links=head2 ARGUMENTSThis command does not take any arguments=head2 OPTIONSThe 'fingerprint' command takes the following options:=over 8=item -[no]deleteThis option will delete the link files if they are bad.=back=head2 DESCRIPTIONThe 'testlinks' command, will by default, examine symbolic links within the sandbox and report onbroken links.An option allows the broken links to be deleted.Each package in the sandbox will have a symbolic link to the packages 'package' area. If a package is removedfrom the sandbox the link file may be left in the sandbox and cause a 'build' to fail.=head2 EXAMPLESThe commandjats sandbox testlinkswill test the symbolic links in the sandbox metadata.=for comment ===================================================================head1 Sandbox Scan Depth=head2 NAMESet and Display the build file scanner depth=head2 SYNOPSISjats sandbox scandepth [options] [depth]Command Options-help[=n] - Command specific help, [n=1,2,3]-man - Same as -help=3=head2 ARGUMENTSThis command takes one optional argument. A positive number that control thedepth of the build filter scanner.The default value is '3'. Deeper scans may be required for sandboxes containing badly formedpackages. The tradoff is speed for all sandbox operations.Setting the scan depth will force a rescan of the sandbox build files on the next command thatrequires the information.=head2 OPTIONSThe scandepth command only accepts the standard help and man options.=head2 DESCRIPTIONThe scandepth command, will by default, display the current value for the build file scanner.If a numeric argument is provided this will set the future build file scan depth.=head2 EXAMPLESThe commandjats sandbox scandepth 5will set future build file scan to a maximum of 5 directories below the root of the sandbox.=cut