Rev 281 | Rev 325 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
######################################################################### Copyright (C) 2007 ERG Limited, All rights reserved## Module name : jats.sh# Module type : Makefile system# Compiler(s) : n/a# Environment(s): jats## Description : Top level Makefile supervisor# The function of this program is to call the target makefiles# in a sutable manner.## History : Once upon a time this was done with another makefile and# some really ugle shell, embedded within the makefile# This had a number of limitations, including# - Limited to makefile command line syntax# - Overly complex makefile# - Slow execution## Usage:##......................................................................#require 5.006_001;use strict;use warnings;use Pod::Usage; # For help supportuse JatsError;use JatsSystem;use JatsEnv;use JatsMakeInfo;use ReadBuildConfig qw(:All);use Getopt::Long;use FileUtils;use ArrayHashUtils;use JatsDPackage;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;my $opt_silent = 1;my $opt_default;## Global variables#our %cf_info;our $GBE_TOOLS; # Base of toolsour $GBE_ABT; # Optional ABT indicationour $GBE_PLATFORM; # Optional GBE_PLATFORMour $ScmInterface; # Current interface directoryour $ScmRoot; # Build rootmy $Tags; # Reference to hash of tagsmy %defs; # Make definitionsmy @defs; # Make definitionsmy @targets; # Make targetsmy $makelib; # Path to makelib.plmy $update = 0; # Makefiles updatedmy $build_error = 0; # Build Error encounteredmy $tlen = 4; # Max length of targetsmy %common_cmd; # Short circuit some commandsmy %gbe_platform; # Nice form of GBE_PLATFORM## Known commands# A hash of known commands an information about the commands# common - Command is not executed for Debug and Production# Flags: 1 - Command not executed for debug+prod# Flags: 2 - Command is only executed ONCE per platform# local - The command is handled locally.# nomakecheck - Don't check for makefile being out of date# IFLAG - iteration flag,# 0 No external dependencies.# 1 Source dependency list.# 2 Shared library dependency list.# 3 Application dependency list.# Note: gmake attempts to build includes prior to invoking other# rules when the source is non-existent, which can/do fail as a# result of the prereq rules not being executed, the IFLAG inhabits# includes during these prereq rules.#my %composite_commands = (## Composite commands#'all' => [ 'install', 'package', 'deploy' ] ,'install' => [ 'build' ] ,'build' => [ 'mkdepends', 'generate', 'install_hdr', 'depend','make_lib', 'install_lib', 'make_install_shlib','make_prog', 'make_test', 'install_prog', 'install_class' ] ,'lint' => [ 'mkdepends', 'generate_debug', 'install_hdr_debug','lint_lib', 'lint_shlib', 'lint_prog' ] ,'ctags' => [ 'mkdepends', 'generate_debug', 'ctags_debug' ] ,'test' => [ 'make_test' ] ,'mkdepends' => [ 'makefiles', 'make_init'] ,);my %commands = (## Basic commands / phases of the build process# This should be a list of all known makefile commands#'make_usage' => { 'tag' => 0, 'local' => \&DoHelp },'help' => { 'tag' => 0, 'local' => \&DoHelp },'show' => { 'tag' => 0, 'local' => \&ShowMakefiles },'rebuild' => { 'tag' => 0, 'local' => \&TestMakeFiles },'makefiles' => { 'tag' => 0, 'local' => \&TestMakeFiles },'unmakefiles' => { 'tag' => 0, 'local' => \&UnMakeFiles },'make_init' => { 'tag' => 1, 'nomakecheck' => 1, 'common' => 3, },'generate' => { 'tag' => 1 },'install_hdr' => { 'tag' => 1 },'depend' => { 'tag' => 1, 'unless' => ['NODEPEND','EXPERT'] },'make_lib' => { 'tag' => 1, 'IFLAG' => 1 },'make_mlib' => { 'tag' => 1, 'IFLAG' => 1 },'install_lib' => { 'tag' => 1, 'IFLAG' => 1 },'make_install_shlib' => { 'tag' => 1, 'IFLAG' => 2 },'make_prog' => { 'tag' => 1, 'IFLAG' => 3 },'make_test' => { 'tag' => 1, 'IFLAG' => 3 },'install_prog' => { 'tag' => 1, 'IFLAG' => 3 },'install_class' => { 'tag' => 1 },'package' => { 'tag' => 1, 'IFLAG' => 3 },'deploy' => { 'tag' => 1, 'IFLAG' => 1 },## Special# make_dir is directly invoked by jmake. It doesn't need# to be a part of a command sequence#'make_dir' => { 'nomakecheck' => 1, 'tag' => 1 },## Basic commands, that appear to be unused in composite commands# Not too sure why#'make_script' => { 'commands' => [], 'tag' => 1 },## Lint related targets#'lint_lib', => { 'tag' => 'make_lib' , 'IFLAG' => 1 },'lint_shlib', => { 'tag' => 'make_install_shlib' , 'IFLAG' => 2 },'lint_prog' => { 'tag' => 'make_prog' , 'IFLAG' => 3 },## Following commands should NOT be a part of a composite command#'run_tests' => { 'nomakecheck' => 1, 'tag' => 1, 'IFLAG' => 3},'run_unit_tests' => { 'nomakecheck' => 1, 'tag' => 1, 'IFLAG' => 3},'clean' => { 'nomakecheck' => 1},'clobber' => { 'nomakecheck' => 1},'rmlitter' => { 'nomakecheck' => 1},'unbuild' => { 'nomakecheck' => 1},'undepend' => { 'nomakecheck' => 1 , 'tag' => 'depend'},'ungenerate' => { 'nomakecheck' => 1 , 'tag' => 'generate'},'uninstall' => { 'nomakecheck' => 1},'uninstall_class' => { 'nomakecheck' => 1},'uninstall_hdr' => { 'nomakecheck' => 1 , 'tag' => 'install_hdr'},'uninstall_lib' => { 'nomakecheck' => 1 , 'tag' => 'install_lib'},'uninstall_prog' => { 'nomakecheck' => 1 , 'tag' => 'install_prog'},'unmake_lib' => { 'nomakecheck' => 1 , 'tag' => 'make_mlib'},'unmake_mlib' => { 'nomakecheck' => 1 , 'tag' => 'make_mlib'},'unmake_prog' => { 'nomakecheck' => 1 , 'tag' => 'make_prog'},'unmake_script' => { 'nomakecheck' => 1 , 'tag' => 'make_script'},'unmake_test' => { 'nomakecheck' => 1 , 'tag' => 'make_test'},'unobj' => { 'nomakecheck' => 1},'unpackage' => { 'nomakecheck' => 1 , 'tag' => 'package'},'unmake_dir' => { 'nomakecheck' => 1 , 'tag' => 'make_dir'},);# DebugDumpData ("Commands", \%commands);#-------------------------------------------------------------------------------# Function : Mainline Entry Point## Description :## Inputs :### Parse the user options#my $result = GetOptions ("help:+" => \$opt_help, # flag, multiple use allowed"manual:3" => \$opt_help, # flag, multiple use allowed"verbose:+" => \$opt_verbose, # flag, multiple use allowed"d|debug:+" => \$opt_debug, # flag, multiple use allowed"default=s" => \$opt_default, # string);## UPDATE THE DOCUMENTATION AT THE END OF THIS FILE !!!### Process help and manual options#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' => 'Make','verbose' => $opt_verbose,'debug' => $opt_debug );## Configure the 'System' process so that it will terminate on any error# and not report the complete command line#SystemConfig ( 'ExitOnError' => 2);## Extract non-option commands# Much of this is for backward compatability# Support the following arguments# XXXXX=yyyy Makefile Definition# -XXXX Flags passed to make# XXXX Raw target name#foreach my $arg ( @ARGV ){Verbose2 ("Arg: $arg");if ( $arg =~ m~(\w+)=(.+)~ ) {$defs{uc($1)} = $2;} elsif ( $arg =~ m/^-/ ) {push @defs, $arg;} else {push @targets, $arg;}}## Misc. Initialisation#InitFileUtils();EnvImport( 'GBE_TOOLS' );$makelib = "$GBE_TOOLS/makelib.pl";Error ("Cannot locate JATS file: $makelib") unless ( -f $makelib );EnvImportOptional ( 'GBE_ABT' );EnvImportOptional ( 'GBE_PLATFORM' );## Validate user options#@targets = ($opt_default)if ( $opt_default && ! @targets);Error ("No make targets specified" ) unless ( @targets );## Build the @defs array# This is a set of definitions passed to the makefiles# It is created on the fly to allow command line options to modify the definitions#foreach ( sort keys %defs ){next if ( m~^DEBUG$~i ); # Naughty userpush @defs, "$_=$defs{$_}"; # Makefile defs are XXXX=aaaa}## Look for OPTIONS=*args* and disable the silent make operation# This will allow the make to output messages#if (( exists $defs{OPTIONS} && $defs{OPTIONS} =~ m/args/ ) || $opt_verbose || $opt_debug ){$opt_silent = 0;}## Process GBE_PLATFORM and create a usable hash#if ( $GBE_PLATFORM ){Verbose2 ("GBE_PLATFORM: $GBE_PLATFORM");$gbe_platform{$_} = 1 foreach ( split( / /, $GBE_PLATFORM ) );$gbe_platform{GENERIC} = 1;}## Read in the local Makefile.gbe file# This will provide a path to the interface directory#ReadMakeInfo();## Read in the global build variables#ReadBuildConfig( "$::ScmRoot/$::ScmInterface" );## Determine the max size of the target name# Makes a nice display#foreach ( @BUILDPLATFORMS ){my $len = length ($_);$tlen = $len if ( $len > $tlen );}#-------------------------------------------------------------------------------# Scan the list of targets and perform 'alias' expansion# targets are of the form# platform_command_type# Where:# type may be _prod or _debug or not present# command may be either a composite command or a simple command# platform may be a platform or an alias##foreach my $arg ( @targets ){my $suffix = '';my @commands;my @platforms;## Remove the only available suffix ( _debug | _prod )# Also detect commnads of debug or prod#if ( $arg =~ m~(.+)_(debug)$~ ) {$suffix = $2;$arg = $1;} elsif ( $arg =~ m~(.+)_(prod)$~ ) {$suffix = $2;$arg = $1;} elsif ( $arg eq 'debug' ) {$suffix = $arg;$arg = '';} elsif ( $arg eq 'prod' ) {$suffix = $arg;$arg = '';}## Remove valid make commands from the remaining argument# Have a hash of all known makefile commands#foreach my $cmd ( keys %composite_commands, keys %commands ){if ( $arg eq $cmd ){@commands = ExpandComposite($cmd);$arg = '';}elsif ( $arg =~ m~(.+)_($cmd)$~ ){$arg = $1;@commands = ExpandComposite($2);last;}}## See of we can extract an platform alias from the command# Rip into target + command#if ( $arg ){if ( exists $ScmBuildAliases{$arg} ){@platforms = split (/ /, $ScmBuildAliases{$arg} );}elsif ( exists $ScmBuildPlatforms{$arg} ){@platforms = ($arg);}else{if ( @commands ){Error ("Unknown target: $arg");}else{Message ("Unknown command. Passed to make: $arg");@commands = $arg;}}}## Insert defaults#@platforms = @DEFBUILDPLATFORMSunless ( @platforms );@commands = ExpandComposite('all')unless ( @commands);## Perform GBE_PLATFORM filtering#@platforms = grep ( exists $gbe_platform{$_} , @platforms )if ( %gbe_platform );Error ("No platforms to be processed. Check GBE_PLATFORM")unless ( @platforms );## Iterate commands over platforms#foreach my $cmd ( @commands ){PerformCommand (\@platforms, $cmd, $suffix );}}## All done#exit (0);#-------------------------------------------------------------------------------# Function : PerformCommand## Description : Perform the make on a platform command## Inputs : $platform_ref - platforms to process# $cmd - simple make command# $suffix - debug,prod,none## Returns :#sub PerformCommand{my ($platform_ref, $cmd, $suffix) = @_;my $do_prod = 1;my $do_debug = 1;## Clean up some ugly commands from lint and ctags#if ( $cmd =~ m~(.+)_(debug)$~ ){$cmd = $1;$suffix = $2;}Verbose2 ("Processing Target: $cmd, $suffix");## Limit command to production or debug#if ( $suffix eq 'debug' ) {$do_prod = 0;} elsif ( $suffix eq 'prod' ) {$do_debug = 0;}## Some commands can be suppressed by options# These are an array of options#if ( exists $commands{$cmd}{'unless'} ){foreach my $check ( @{$commands{$cmd}{'unless'}} ){if ( exists $defs{$check} && $defs{$check} ){Verbose2 ("Skipping: $cmd because $check");return;}}}## Interecpt commands that are handled locally#if ( exists $commands{$cmd}{'local'} ){$commands{$cmd}{'local'}( $cmd, $platform_ref );return;}## Process and recurse directory tree#Verbose ("Processing: $cmd, @{$platform_ref}");## Determine the local tag name and state# The tag is used to short circuit makefile commands in recursive makes# It greatly reduces the work required## Many commands have a tag that is the same as the command, but a few don't#my $tag = 0;if ( exists $commands{$cmd}{tag} ){$tag = $commands{$cmd}{tag};$tag = $cmd if ( $tag eq '1' );}if ( $commands{$cmd}{'common'} ){InvokeSubMake( $::Cwd, $platform_ref ,$cmd, $tag, 1 );}else{InvokeSubMake( $::Cwd, $platform_ref ,$cmd, $tag, 1 ) if $do_debug;InvokeSubMake( $::Cwd, $platform_ref ,$cmd, $tag, 0 ) if $do_prod;}}#-------------------------------------------------------------------------------# Function : InvokeSubMake## Description : Build recursion# Determine if there is anything to be done for the current# target within a specified directory## Inputs : $dir# $pref - Ref to an array of platforms# $cmd# $tag# $do_prod## Returns :#sub InvokeSubMake{my ( $dir, $pref, $cmd, $tag, $do_debug) = @_;## Ensure the current directory is known to the Tags database#$Tags = ReadMaketags( $dir );Error ("Directory not in tag database", $dir ) unless ( $Tags );## Process commands in the current directory# If the command is tagged, then we may be able to skip it# Otherwise we need to do it#InvokeMake( $dir, $pref, $cmd, $tag, $do_debug );## Recurse into any subdirectories that may be a part of the build#unless ( exists $defs{NORECURSE} && $defs{NORECURSE} ){## Process subdirectories for the current command# If the command is tagged, then we may be able to skip it# Otherwise we need to do it#foreach my $subdir (@{$Tags->{$dir}{subdirs}} ){InvokeSubMake( CleanDirName( "$dir/$subdir"), $pref, $cmd, $tag, $do_debug );}}}#-------------------------------------------------------------------------------# Function : InvokeMake## Description : Actually invoke the make for debug and production as required## Inputs : $dir# $pref# $cmd# $tag# $do_debug### Returns :#sub InvokeMake{my ( $dir, $pref, $cmd, $tag, $do_debug ) = @_;my $text = 'C';Verbose2 ("InvokeMake: $dir");## Ensure that the current directory actually has makefile targets# Not all makefile.pl create .mk files# 1) Top-level makefile.pl# 2) Makefiles that use --NoPlatformBuilds#if ( $Tags->{$dir}{root} ){Verbose2 "Root directory has no makefiles: $dir";return;}if ( $Tags->{$dir}{noplatforms} ){Verbose2 "No make targets in $dir";return;}## Process each platform#foreach my $target ( @{$pref} ){unless ( exists $Tags->{$dir}{platforms}{$target} ){Verbose2 "No make targets in $dir, for $target";next;}## Do we need to do any thing for this target / tag#if ( $tag ){unless ( exists $Tags->{$dir}{full}{$target}{'%MakeTags'}{$tag} ){Verbose2 ("Skipping $cmd in $dir for $tag");next;}}## common mode == 2# Only process target ONCE for each platform# Don't care when its used#if ( exists $commands{$cmd}{'common'} && $commands{$cmd}{'common'} & 2 ){if ( $common_cmd{$cmd}{$target} ){Verbose2 "Already performed $cmd for $target";next;}$common_cmd{$cmd}{$target} = 1;}## Is the build limited to only debug or production#unless ( exists $commands{$cmd}{'common'} ){if ( $do_debug == 0 ) {next unless ( $Tags->{$dir}{platforms}{$target}{prod} );$text = 'P';} elsif ( $do_debug == 1 ) {next unless ( $Tags->{$dir}{platforms}{$target}{debug} );$text = 'D';}}## Final sanity test# Must have the makefile. We should have detected this error before now#Error ("Makefile not found - $target") unless ( -f "$dir/$target.mk" );## Export data into the user environment# Allows underlying tools to locate data files with little effort#$ENV{'GBE_MAKE_TYPE'} = $text; # P or D or C$ENV{'GBE_MAKE_TARGET'} = $target; # Target platform$ENV{'GBE_MAKE_CFG'} = $Tags->{$dir}{config}; # Ref to config data$ENV{'GBE_MAKE_CMD'} = $cmd; # Make phase## Build up the make command line# Examine command specfic flags#my @args = @defs;push @args, "IFLAG=$commands{$cmd}{IFLAG}" if ( exists $commands{$cmd}{IFLAG} );push @args, "NOSCMDEPEND=1";my $cdir = CleanDirName ($dir);my @make_command;push @make_command, "$ENV{GBE_BIN}/xmake";push @make_command, "-s" if $opt_silent;push @make_command, "--no-print-directory";push @make_command, "-C", $cdir unless ( $cdir eq $::Cwd );push @make_command, "-f", "$target.mk";push @make_command, @args;push @make_command, 'make_dir' unless exists $commands{$cmd}{nomakecheck} ;push @make_command, $cmd;Message ( sprintf ("[$text] %-${tlen}s, $cmd, $cdir", $target ));System (@make_command, "DEBUG=$do_debug" );}}#-------------------------------------------------------------------------------# Function : TestMakeFiles## Description : Test all the makefile dependent files to see if any of the# makefiles need to be rebuilt.## Walk the directory tree looking for makefiles to be# rebuilt.## Inputs : $cmd - Current command# $pref - Ref to an array of platforms## Returns :#sub TestMakeFiles{my ( $cmd, $pref ) = @_;Verbose ("Test makefile dependencies");## Read in the Tag file for the current directory# This will provide the current list of subdirectories## Process Test the current makefiles, then test any subdirs#TestSubMake( $cmd, $Cwd, $pref );## Report build errors# Done after all makefiles have been processed#if ( $build_error ){Error ("Error creating makefiles. Errors previously reported");}## Post makefile build processing# Only required if a files have been re-built#if ( $update ){## Sanity test the makefile structure# Ensure that makefiles have only one parent.#TestParents();## Sanity test the installed and packaged files# Generate warnings and errors if a file if packaged from two# locations#TestPackages();## Generate DPACKAGE file if required#JatsDPackage::DPackageGenerate( $::ScmRoot, $::ScmInterface );}### Check that the build.pl file is not newer that the tags file# This will not be an error, but it will be a nagging warning#my @build_warn;my $bstamp = -M "$::ScmRoot/$ScmBuildSrc";my $tstamp = -M "$::ScmRoot/Makefile.gbe";push @build_warn, "Missing: Makefile.gbe" unless ( defined $tstamp );push @build_warn, "Modified build file ($ScmBuildSrc)" if ( $tstamp && $bstamp < $tstamp );if ( @build_warn ){Warning ("The build file is newer than the generated files","The project needs to be built for these changes to be included",@build_warn );}}#-------------------------------------------------------------------------------# Function : TestSubMake## Description : Test the makefiles in the current directory# Recurse into subdirectories## Inputs : $cmd - Current command# $dir - Directory to test# $pref - Ref to an array of platforms## Returns :#sub TestSubMake{my ($cmd, $dir, $pref ) = @_;$Tags = ReadMaketags( $dir, 1 );unless ( $Tags ){Verbose2 ("TestSubMake :Directory not in tag database", $dir );MakeBuild( $dir);}else{TestMake( $cmd, $dir, $pref );}## Recurse into any subdirectories that may be a part of the build#unless ( exists $defs{NORECURSE} && $defs{NORECURSE} ){foreach my $subdir (@{$Tags->{$dir}{subdirs}} ){TestSubMake( $cmd, CleanDirName( "$dir/$subdir"), $pref );}}}#-------------------------------------------------------------------------------# Function : ShowMakefiles## Description : Show the makefiles used in the build## Inputs : $cmd - Current command# $pref - Ref to an array of platforms## Returns :#sub ShowMakefiles{my ( $cmd, $pref ) = @_;Verbose ("Show makefiles");print "\nPlatform targets\n";foreach ( sort keys %BUILDINFO ){print " $_\n";}my @alias = sort keys %ScmBuildAliases;if ( $#alias >=0 ){print "\nAlias targets\n";foreach ( @alias ){print " $_ - $ScmBuildAliases{$_}\n";}}## Read in the Tag file for the current directory# This will provide the current list of subdirectories## Process Test the current makefiles, then any subdirs#ShowSubMake( $cmd, $Cwd, $pref, 0 );}#-------------------------------------------------------------------------------# Function : ShowSubMake## Description : Test the makefiles in the current directory# Recurse into subdirectories## Inputs : $cmd - Current command# $dir - Directory to test# $pref - Ref to an array of platforms# $level - Recursion depth## Returns :#sub ShowSubMake{my ($cmd, $dir, $pref, $level ) = @_;$Tags = ReadMaketags( $dir, 1 );unless ( $Tags ){Verbose2 ("ShowSubMake :Directory not in tag database", $dir );MakeBuild( $dir);}elsif ( $level ){print ("Makefile : " . ' ' x (4 * ($level - 1) ) . DisplayPath(RelPath($dir)) ,"\n");} else {print ("\nBuildfile: ", DisplayPath($dir), "\n" );}## Recurse into any subdirectories that may be a part of the build#foreach my $subdir (@{$Tags->{$dir}{subdirs}} ){ShowSubMake( $cmd, CleanDirName( "$dir/$subdir"), $pref, $level + 1 );}}#-------------------------------------------------------------------------------# Function : TestMake## Description : Test the makefiles component files# and determine if the makefile(s) need to be rebuilt## Inputs : $cmd - Current command# $dir - Directory to test# $pref - Ref to an array of platforms## Returns :#sub TestMake{my ( $cmd, $dir, $pref ) = @_;my @must_rebuild;if ( $cmd eq 'rebuild' ){MakeBuild( $dir);return;}## Check that the local makefile.pl is not newer than# the Tags file. Not all makefile.pl's create .mk files#my $mstamp = -M "$dir/makefile.pl";my $tstamp = -M "$dir/Makefile.gbe";unless ( defined $tstamp ){UniquePush (\@must_rebuild, "Missing: $dir/Makefile.gbe");}if ( $mstamp && $mstamp < $tstamp ){UniquePush (\@must_rebuild, "Updated: $dir/makefile.pl");}else{if ( $Tags->{$dir}{root} ){Verbose2 "TestMake: Root directory has no makefiles: $dir";return;}if ( $Tags->{$dir}{noplatforms} ){Verbose2 "TestMake: No make targets in $dir";return;}## Process only the required build targets#foreach my $tgt ( keys %{$Tags->{$dir}{platforms}} ){next if ( %gbe_platform && ! exists $gbe_platform{$tgt} );## Locate all the makefile dependent files#my $data = $Tags->{$dir}{full}{$tgt}{'@ScmDepends'};if ( $data ){Verbose2 ("Processing: $dir : $tgt");my $base_stamp = -M "$dir/$tgt.mk";unless ( defined $base_stamp ){UniquePush (\@must_rebuild, "Missing: $dir/$tgt.mk");}else{foreach my $file ( "$dir/makefile.pl" ,@$data ){$file = "$dir/$file" if ( $file =~ m~^\.~ );my $stamp = -M $file;if ( defined $stamp ){Verbose3 ("Timestamp: $stamp, $file");if ( $stamp < $base_stamp ){UniquePush (\@must_rebuild, $file);}}else{UniquePush (\@must_rebuild, "Missing: $file");}}}}else{Warning ("No dependency information for: $tgt in $dir");}}}if ( @must_rebuild ){my @display;push (@display, CleanDirName($_) ) foreach ( @must_rebuild );Message ("One or more JATS source or internal files have changed, Makefiles will be rebuilt","Target is: $dir",@display );MakeBuild ($dir);}return;}#-------------------------------------------------------------------------------# Function : MakeBuild## Description : Rebuild the makefiles in the specified directory# This does not involve recursion - thats already been# done.## Once the makefiles have been re-built the tags database# must be read in again as it may have changed.## Inputs :## Returns :#sub MakeBuild{my ($dir) = @_;## No makefiles to build in the root directory# Can't rebuild the top-level Makefile.gbe file#return if ( $Tags->{$dir}{root} );## Try to rebuild the makefile#chdir $dir || Error ("Cannot change directory to $dir");my $result = System ('--NoExit', $ENV{GBE_PERL}, 'makefile.pl',$::ScmRoot, $makelib, "--interface=$::ScmInterface" );chdir $::Cwd || Error ("Cannot change directory to $::Cwd");## Re-read the tags database#$Tags = ReadMaketags( $dir, 2 );## Flag that makefiles have been modified#$update = 1;$build_error = 1 if ( $result );}#-------------------------------------------------------------------------------# Function : UnMakeFiles## Description : Delete generated makefiles and control files## Inputs : $cmd - Current command# $pref - Ref to an array of platforms## Returns :#sub UnMakeFiles{## Recurse down the tree# Recurse, then performs operations in the current directory#sub UnSubMakeFiles{my ($dir) = @_;$Tags = ReadMaketags( $dir, 1 );foreach my $subdir (@{$Tags->{$dir}{subdirs}} ){UnSubMakeFiles( CleanDirName( "$dir/$subdir") );}UnMake( $dir );}## Delete makefiles in the current directory#sub UnMake{my ($dir) = @_;Verbose("Unmake in $dir");foreach my $tgt ( keys %{$Tags->{$dir}{platforms}} ){Verbose2("Unmake. Delete ${tgt}.mk");unlink ("$dir/${tgt}.mk");}unlink ("$dir/Makefile.gbe");Verbose2("Unmake. Delete Makefile.gbe");}## Depth first recursion through the tree#UnSubMakeFiles ( $Cwd );}#-------------------------------------------------------------------------------# Function : TestParents## Description : Ensure that each makefile node has exactly one parent# Prevent the user from creating recursive loops## Inputs :## Returns :#my %parents;sub TestParents{Verbose ("Test makefile parents");## Init the hash. Command may be invoked multiple times#%parents = ();## Recurse down the tree# Recurse, then performs operations in the current directory#sub RecurseDown{my ($dir) = @_;$Tags = ReadMaketags( $dir, 1 );Verbose2("TestParents in $dir");foreach my $subdir (@{$Tags->{$dir}{subdirs}} ){my $subdir = CleanDirName( "$dir/$subdir");push @{$parents{$subdir}}, $dir;RecurseDown( $subdir );}}## Depth first recursion through the tree#RecurseDown ( $Cwd );## Test for only one parent# The root makefile won't have any#foreach my $dir ( sort keys %{$Tags} ){my $count = $#{$parents{$dir}};if ( $count > 0 ){if ( defined($GBE_ABT) ){Warning ("Error supressed by ABT");Warning ("makefile.pl with multiple parents","makefile.pl in: $dir","Parented by:", @{$parents{$dir}} );}else{unlink $Tags->{$dir}{config};Error ("makefile.pl with multiple parents","makefile.pl in: $dir","Parented by:", @{$parents{$dir}} );}}}}#-------------------------------------------------------------------------------# Function : TestPackages## Description : Ensure that files that are packaged and installed are# only done from one makefile. Warn if the same file# is being packaged from multiple makefiles.## Inputs :## Returns :#my %test_packages;sub TestPackages{Verbose ("Test packaged files");## Init the hash. Command may be invoked multiple times#%test_packages = ();## Recurse down the tree# Recurse, then performs operations in the current directory#sub TRecurseDown{my ($dir) = @_;Verbose2("TestPackages in $dir");$Tags = ReadMaketags( $dir, 1 );## Process makefile# Examine all the %PACKAGE_xxx and %INSTALL_xxx fields thare listed# in the @PACKAGE_VARS and @INSTALL_VARS array#foreach my $target ( keys %{$Tags->{$dir}{platforms}} ){next if ( %gbe_platform && ! exists $gbe_platform{$target} );foreach my $field ( @{$Tags->{$dir}{full}{$target}{'@PACKAGE_VARS'}},@{$Tags->{$dir}{full}{$target}{'@INSTALL_VARS'}} ){foreach my $entry ( keys %{$Tags->{$dir}{full}{$target}{$field}} ){Verbose3("TestPackages: $target, File: $entry");push @{$test_packages{$target}{$entry}}, $dir;}}}## Process subdirectories too#foreach my $subdir (@{$Tags->{$dir}{subdirs}} ){TRecurseDown( CleanDirName( "$dir/$subdir") );}}## Depth first recursion through the tree#TRecurseDown ( $Cwd );## Test and report files that are packaged in different makefiles# There are two issues:# 1) Not good practice# 2) May be different files# The warning message is a bit ugly - but it shouldn't pop up often.## DebugDumpData ("test_packages", \%test_packages);foreach my $target ( sort keys %test_packages ){foreach my $file ( sort keys %{$test_packages{$target}} ){## The descpkg file is known to be packaged from multiple dirs#next if ( $file =~ m~/descpkg~ );if ( $#{$test_packages{$target}{$file}} > 0 ){Warning ("File is packaged or installed from multiple makefiles","Target: $target, Packaged file: $file","Packaged from the following directories:", @{$test_packages{$target}{$file}} );}}}}#-------------------------------------------------------------------------------# Function : DoHelp## Description : Display basic help information## Inputs : None## Returns :#sub DoHelp{## Display basic usage information#pod2usage(-verbose => 0,-message => "Version: $VERSION",-exitval => 'NOEXIT');print "Platform targets\n";foreach ( sort keys %BUILDINFO ){print " $_\n";}my @alias = sort keys %ScmBuildAliases;if ( $#alias >=0 ){print "Alias targets\n";foreach ( @alias ){print " $_ - $ScmBuildAliases{$_}\n";}}}#-------------------------------------------------------------------------------# Function : ReadMaketags## Description : Read in the global make tags data base# This contains information on all the components within# the build. The tags allow recursive makes to be pruned.## The file may change while the build is running# It can be read multiple times## Inputs : $dir - Directory entry to read# $test - 1: Read if available# 2: Force read## Returns : Reference to the tag data for the requested directory# Will error if no available#our %cf_minfo;our %cf_minfo2;my %tag_data;sub ReadMaketags{my ($dir, $test) = @_;## Force data re-aquisition#delete $tag_data{$dir}if ( $test && $test == 2 );## Do we already have the entry#return \%tag_dataif ( exists ($tag_data{$dir} ));## If the entry is not known, then re-read the index file#unless ( $::cf_filelist{$dir} ){my $index_file = "$::ScmRoot/$::ScmInterface/Makefile.cfg";Error ("Makefile index missing. Rebuild required") unless ( -f $index_file );## Kill the entry in %INC to force the file to be read in#$::cf_filelist = ();delete $INC{ $index_file };require $index_file;}my $cfg_filen = $::cf_filelist{$dir};unless ( $cfg_filen ){return if ( $test );Error ("Makefile index entry missing: $dir. Rebuild required");}## Read in all the Makefile_x.cfg files#%cf_minfo = ();%cf_minfo2 = ();my $cfg_file = "$::ScmRoot/$::ScmInterface/$cfg_filen";unless ( -f $cfg_file ){return if ( $test );Error ("Make data file missing: $cfg_file. Rebuild required");}delete $INC{ $cfg_file };require $cfg_file;Error ("Makefile info2 not present")unless ( keys %::cf_info2 );Error ("Makefile info2 incorrect version. Rebuild required")unless ( exists $::cf_info2{version} && $::cf_info2{version} == 1 );%{$tag_data{$dir}} = %::cf_info2;$tag_data{$dir}{config} = $cfg_file;%{$tag_data{$dir}{full}} = %::cf_info;#DebugDumpData ("ReadMaketags", $tag_data{$dir});return \%tag_data;}#-------------------------------------------------------------------------------# Function : ExpandComposite## Description : Expand a command via composite command expansion# A composite command may contain other composite commands# Detect, as an error, recursive expansions## Inputs : $cmd - command to expand## Returns : An array of commands#sub ExpandComposite{my @scmds = $_[0];my @cmds;my %seen;while ( @scmds ){my $cmd = shift @scmds;if ( exists $composite_commands{$cmd} ){Error ("Internal. Recursion in composite command definitions: $cmd")if ($seen{$cmd});@scmds = (@{$composite_commands{$cmd}}, @scmds);$seen{$cmd} = 1;}else{push @cmds, $cmd;}}return @cmds;}#-------------------------------------------------------------------------------# Documentation#=pod=head1 NAMEjmake - JATS make support tool=head1 SYNOPSISUsage: jats make [options][targets][flags]Where options:-h, -h -h, -man - Help messages with increasing verbosity-verbose - Verbose operation-debug - Debug operation-default=target - Default 'target' if none is suppliedWhere flags are of the form Name=ValueSHOWENV=1 - Show make environmentLEAVETMP=1 - Leave temp working filesNODEPEND=1 - Ignore dependency checkingEXPERT=1 - Ignore dependency on makefilesOPTIONS=[opt] - Maketime options [args,allargs,filter...]Valid targets include:all - build and install everything(p*)build - build everything (pu)debug - build all things for debug (pu)prod - build all things for production (pu)install - install public components (pu*)lint - lints the source (assumes debug)(p)package - build all packages (pu*)package-{set} - build specific package (see below) (pu*)run_tests - Run the tests specified in the makefilerun_unit_tests - Run the automatic unit tests specified in the makefiledeploy - Run the deployment scripts (p*)rebuild - recursively rebuild makefilesdepend - construct the dependencies (u*)rmlitter - remove build litter (core, tmp and err) (*)clean - delete generate, obj, libraries and programs (p*)clobber - delete everything which can be remade (p*)help - A list of platforms and aliasesshow - A list of platforms, alias, and makefile paths(u) undo target available (ie uninstall)(p) optional [platform_] prefix targets (ie XXX_build)(*) optional [_debug|_prod] postfix targets (ie clean_debug)=head1 OPTIONS=over 8=item B<-help>Print a brief help message and exits.=item B<-help -help>Print a detailed help message with an explanation for each option.=item B<-man>Prints the manual page and exits.=item B<-verbose>Increase the level of verbosity of the program execution=item B<-debug>Increase the debug output during program execution=item B<-default=target>This options specifies the default target if none is provided on the commandline. Used by the jats wrapper to simplify processing.=back=head1 TARGETSTargets are described above.Most targets support three modifiers. These can be used in conjunction with eachother.=over 8=item undoMany target operation can be undone by prefixing the tarhet with 'un' ie:uninstall.=item platform prefixMany target operations can be limited to one platform by prefixing the targetwith a valid platform name and an underscore. ie WIN32_all=item build postfix. prod or debugMany targets operations can be limited toi either production of debug byadding '_prod' or '_debug' to the target. ie 'install_debug'=back=head1 FLAGSFlags are in the form TAG=value. This is a format that it used as they will bepassed directly to the underlying makefiles. The recognised flags are:=over 8=item B<SHOWENV=1>This flag will force the 'Environment' to be displayed before commands are executed=item B<LEAVETMP=1>This flag will cause temp files, created by the build process, to notr be deleted.=item B<NODEPEND=1>This flag will supress dependency checking. Makefiles will not be created whenthe makefile.pl is changed. Source files will not be scanned for header files.=item B<EXPERT=1>This flag will supress dependency checking between object files and thegenerated makefile. This option can be used while test building a large buildwhen the makefile has been rebuilt but the user does not wish all the objectfiles to be rebuilt.=item B<OPTIONS=list,list>This flags passes a list of comma seperated options into the makefile. The exactset of available options is target specific. Refer to the JATS manual.=back