# -*- mode: perl; tabs: 8; indent-width: 4; show-tabs: yes; -*- # Copyright (C) 1998-2008 ERG Transit Systems, All rights reserved # # Module name : makelib.pl2 # Module type : Makefile system # # Description: # This modules builds the platform definition makefiles(s) # # Notes: *** DO NOT DETAB *** # Beware the use of space v's tab characters within the # makefile generation sessions. # ############################################################################## # Globals: # $ScmVersion Makelib.pl2 version # $ScmRoot Command line parameter that gives the root directory # location for this directory tree. # $ScmMakelib Command line parameter that points to the location # of THIS script. ie. location of makelib.pl. # $ScmPlatform Current platform # $ScmProduct Current product (if any) # $ScmTarget Resulting target (derived from Platform) # @ScmPlatformArgs Platform arguments # $ScmToolset Toolset # @ScmToolsetArgs Toolset arguments # $ScmDebug Debug level # $ScmVerbose Verbose setting # $ScmSourceTypes Source types, aliasing for C, C++ and assembler # source. # @CFLAGS List containing all of the defined C flags # @CXXFLAGS List containing all of the defined C++ flags # @ASFLAGS List containing all of the defined assembler flags # @CLINTFLAGS List containing all of the defined C lint flags # @CXXLINTFLAGS List containing all of the defined C++ lint flags # @{G|L}_INCDIRS List containing all of include paths # @{G|L}_SRCDIRS List containing all of source search paths # @{G|L}_LIBDIRS List containing all of library search paths # @LDFLAGS List containing all of the defined linker flags # @SRCS List of ALL source files. ie. C/C++ and other (eg .x) # Key is source file, value is source path # @OBJS List of ALL (non-shared) object files. # %SHOBJ_LIB List of ALL shared library object files and associated library. # %OBJSOURCE List of ALL object files # from that should result from later makes. # Key is objectfile, value is source file # %OBJREFS List of ALL object files, built options. # @PROGOBJS List of ALL application object files. # %SRC_ARGS List of arguments that are to be used when making the # nominated source. Key is the source name, the # value is a string of arguments to apply. The # arguments are '$;' separated. # %SRC_TYPE Source file type (user override). # @CHDRS List of C header files. # @CSRCS List of C files # Key is objectfile, value is source file # @CXXSRCS List of C++ files # @ASHDRS List of assembler include files (.inc) # @ASSRCS List of assembler source files # @GENERATED List of files that should result from a 'generate' # make rule. The programmer is expected to provide # the necessary rule(s). # @RULES List of additional make rules the programmer # has specified to be included in the make. # %INSTALL_HDRS List of headers that are to be installed for later # "public" consumption. # %INSTALL_CLSS List of Java classes or JAR files that are to be installed # for later "public" consumption. # @LIBS List of libraries that are to be built. # $LIBS Ref to a collection of static library descriptors # Indexed by lib name # %INSTALL_LIBS List of libraries that are to be installed for later # public consumption. # @MLIBS List of libraries that are to be built via merging # $MLIBS Ref to a collection of merged lib descriptors # @SHLIBS List of shared libraries that are to be built. # $SHLIBS Ref to collection of shared library information # %INSTALL_SHLIBS List of libraries that are to be installed for later # public consumption. # @PROGS List of programs (binary executables) that are # to be built # $PROGS Ref to collection of program information # %SCRIPTS List of scripts to 'create' (key) and whether they # should be made executable or not (value). Script # set to executable is denoted by the value being # defined AND true. # %INSTALL_PROGS List of programs for "public" cosumption to install # where (key) is the file and where to install it # to vs. (value) which is composed of the original # location of the file, the destination directory # and a list of service providers this file applies to. # $ProjectBase Base of the user's project. This variable is designed to # be used by the user. #.... require 5.006_001; use strict; use warnings; use Getopt::Long; use Data::Dumper; use JatsError; use JatsEnv; use MakeEntry; use JatsLocateFiles; use JatsDPackage; use MakeIf; use ToolsetPrinter; use MakeObject; use JatsVersionUtils; our $ScmVersion = "2.34"; our $ScmGlobal = 0; our $ScmExpert = 0; our $ScmInterface = "interface"; # default 'interface' our $ScmPackage = 1; # package active by default. our $ScmProcessingRootMake = 0; # Processing root makefile.pl our $ScmPlatformSeen = 0; # Platform directive has been seen our $ScmToolsetVersion = ""; # version of toolset our $ScmToolsetGenerate = 1; # generate active by default. our $ScmToolsetProgDependancies = 1; # 1: Write program dependancies # 0: Don't write progdeps. Prog is Phony our $ScmToolsetSingleType = 0; # Toolset does not support Debug and Production our $ScmToolsetProgSource = (); # Toolset Program Source our $ScmToolsetSoName = 0; # 1: Shared library supports SoName our $ScmToolsetNillLibSrc = 0; # 1: Librarys created without source specified our $ScmRoot = ""; our $ScmMakelib = ""; our $ScmPlatform = ""; our $ScmMachType = ""; our $ScmSrcDir = ""; our @ScmPlatformDirs = (); our @ScmPlatformArgs = (); our $ScmBuildType = 0; # 0, P, D. 0 == P and D our $ScmProduct = ""; our $ScmTarget = ""; our $ScmTargetHost = ""; our $ScmToolset = ""; our @ScmToolsetArgs = (); our @ScmDepends = (); our %ScmSourceTypes = (); our $ScmDeploymentPatch = ""; our $ProjectBase = ""; # Base of the user's project our $ScmNoToolsTest = ""; # Supress compiler tests our $ScmDependTags = 0; # Create dependancy scanning tag our $ScmMakeUid; # Unique makefile id (number 1 .. ) our @CFLAGS = (); our @CFLAGS_DEBUG = (); our @CFLAGS_PROD = (); our @CLINTFLAGS = (); our @CLINTFLAGS_DEBUG = (); our @CLINTFLAGS_PROD = (); our @CXXFLAGS = (); our @CXXFLAGS_DEBUG = (); our @CXXFLAGS_PROD = (); our @CXXLINTFLAGS = (); our @CXXLINTFLAGS_DEBUG = (); our @CXXLINTFLAGS_PROD = (); our @ASFLAGS = (); our @ASFLAGS_DEBUG = (); our @ASFLAGS_PROD = (); our @LDFLAGS = (); our @LDFLAGS_DEBUG = (); our @LDFLAGS_PROD = (); our @INCDIRS = (); our @NODEPDIRS = (); our @S_INCDIRS = (); our @G_INCDIRS = (); our @L_INCDIRS = (); our @SRCDIRS = (); our @S_SRCDIRS = (); our @G_SRCDIRS = (); our @L_SRCDIRS = (); our @LIBDIRS = (); our @S_LIBDIRS = (); our @G_LIBDIRS = (); our @L_LIBDIRS = (); our %SRCS = (); our %SRC_ARGS = (); our %SRC_TYPE = (); our %SRC_DEPEND = (); our %SCRIPTS = (); our @COPYIN = (); our @INITS = (); our @DEFINES = (); our @OBJS = (); our %SHOBJ_LIB = (); our @PROGOBJS = (); our @TESTPROGOBJS = (); our %OBJSOURCE = (); our @CHDRS = (); our @CSRCS = (); our @CXXSRCS = (); our @ASHDRS = (); our @ASSRCS = (); our @GENERATED = (); our @GENERATED_NOTSRC = (); our @RULES = (); our @TOOLSETRULES = (); our @TOOLSETDIRS = (); our @TOOLSETDIRTREES = (); our @TOOLSETGENERATED = (); our @USERGENERATED = (); our @TOOLSETOBJS = (); our @TOOLSETLIBS = (); our @TOOLSETPROGS = (); our %INSTALL_HDRS = (); our %INSTALL_CLSS = (); our @TOOLSET_UTF_PRE = (); # Toolsets can extend rules run before all unit tests our @TOOLSET_UTF_POST = (); # Toolsets can extend rules run after all unit tests our @TOOLSET_UTF_COLLATE = (); # Toolsets can extend rules run to collate unit tests results our @LIBS = (); our $LIBS = (); our %LIB_PKG = (); our %LIB_INS = (); our %INSTALL_LIBS = (); our @MLIBS = (); our $MLIBS = (); our @SHLIBS = (); our $SHLIBS = (); our @SHLIB_TARGETS = (); our %SHLIB_PKG = (); our %SHLIB_INS = (); our %INSTALL_SHLIBS = (); our $TESTPROGS = (); our @TESTPROGS = (); our $PROGS = (); # Simplify tracking of progs our @PROGS = (); our @PROGS_EXTRA = (); # Look at doing better !! our %PROG_PKG = (); our %PROG_INS = (); our %INSTALL_PROGS = (); our %PACKAGE_DIST = (); our %PACKAGE_SETS = (); our %PACKAGE_HDRS = (); our %PACKAGE_LIBS = (); our %PACKAGE_CLSS = (); our %PACKAGE_SHLIBS = (); our %PACKAGE_PROGS = (); our %PACKAGE_FILES = (); our @PACKAGE_VARS = ( '%PACKAGE_CLSS', '%PACKAGE_FILES', '%PACKAGE_HDRS', '%PACKAGE_LIBS', '%PACKAGE_PROGS', '%PACKAGE_SHLIBS' ); our @INSTALL_VARS = ( '%INSTALL_CLSS', '%INSTALL_HDRS', '%INSTALL_LIBS', '%INSTALL_PROGS', '%INSTALL_SHLIBS'); our @LINTLIBS = (); our @LINTSHLIBS = (); our @TESTS_TO_RUN = (); # Info from 'RunTest' directives our @TESTPROJECT_TO_URUN = (); # List of Unit Tests and Projects names (Non Auto) our @TESTPROJECT_TO_ARUN = (); # List of Auto Tests and Projects names my $TESTS_TO_AUTORUN = undef; # Flag - Auto Test found my $TESTS_TO_RUN = undef; # Flag - Unit Test found #our $CurrentTime = ""; #our $CurrentDate = ""; #our $Cwd = ""; our @GENERATE_FILES = (); our %DEPLOYPACKAGE = (); our $DEPLOYPACKAGE = 0; our %MakeTags; # # Some toolset options that affect the generation of the makefile # our $UseAbsObjects = 0; # Default is relative paths to objects our $UseRelativeRoot = 0; # Default is absolute paths to build root our $DPackageDirective = 0; # # Arrays of hook functions # our %MF_RegisterSrcHooks; # Hook source file discovery ############################################################################### # # Packaging and Installation Information # Held in a structure as its used in a few places # Items # PBase - Package Base directory. Used for user overrides # IBase - Local Install Base directory # Dir - Default directory suffix for components. Added to Pbase and IBase # # our %PackageInfo = ( 'File' => { 'PBase' => '$(PKGDIR)' ,'IBase' => '$(LOCALDIR)' , 'Dir' => '' }, 'Hdr' => { 'PBase' => '$(INCDIR_PKG)' ,'IBase' => '$(INCDIR_LOCAL)' , 'Dir' => ''}, 'Lib' => { 'PBase' => '$(LIBDIR_PKG)' ,'IBase' => '$(LIBDIR_LOCAL)' , 'Dir' => '/$(GBE_PLATFORM)'}, 'Prog' => { 'PBase' => '$(BINDIR_PKG)' ,'IBase' => '$(BINDIR_LOCAL)' , 'Dir' => '/$(GBE_PLATFORM)$(GBE_TYPE)'}, 'Jar' => { 'PBase' => '$(CLSDIR_PKG)' ,'IBase' => '$(CLSDIR_LOCAL)' , 'Dir' => ''}, 'Tool' => { 'PBase' => '$(PKGDIR)' ,'IBase' => '$(LOCALDIR)' , 'Dir' => '/tools/bin/$(GBE_HOSTMACH)'}, ); ############################################################################### # # An array of reserved names # Used to attempt to prevent developers from naming toolset targets with names reserved # within the build system our @reservedMakeTargets = qw ( preprocess_tests postprocess_tests collate_test_results ); MakeLib2Init(); # Runtime initialisation sub MakeLib2Init { #.. Test environment # EnvImport( "GBE_CORE" ); EnvImport( "GBE_BIN" ); EnvImport( "GBE_PERL" ); EnvImport( "GBE_TOOLS" ); EnvImport( "GBE_CONFIG" ); EnvImport( "GBE_MACHTYPE" ); #.. Common stuff # require "$::GBE_TOOLS/common.pl"; # Common stuff push( @ScmDepends, "$::GBE_TOOLS/common.pl" ); CommonInit( "makelib2" ); Debug( "version: $ScmVersion" ); #.. Cache arguments # CommandLine(); #.. Build defaults # $ScmSourceTypes{ ".h" } = ".h"; $ScmSourceTypes{ ".hpp" } = ".h"; $ScmSourceTypes{ ".c" } = ".c"; $ScmSourceTypes{ ".C" } = ".c"; $ScmSourceTypes{ ".cpp" } = ".cc"; $ScmSourceTypes{ ".cc" } = ".cc"; $ScmSourceTypes{ ".asm" } = ".asm"; $ScmSourceTypes{ ".x" } = "--Ignore"; $ScmSourceTypes{ ".ini" } = "--Ignore"; $ScmSourceTypes{ ".sh" } = "--Ignore"; $ScmSourceTypes{ ".pl" } = "--Ignore"; $ScmSourceTypes{ ".awk" } = "--Ignore"; #.. Get the stuff from the build configuration file # ConfigLoad(); $ScmMakeUid = GetMakfilefileUid(); Debug("ScmMakeUid: $ScmMakeUid"); if ( (%::ScmBuildPlatforms) ) # Interface/build.cfg { AddPlatformArg( split( /$;/, $::ScmBuildPlatforms{ $ScmPlatform } )); } if ( (%::ScmBuildIncludes) ) # Interface/build.cfg { my( @includes ) = split( ',', $::ScmBuildIncludes{ $ScmPlatform } ); my( $global ) = $ScmGlobal; $ScmGlobal = 1; # Follow defs are "global's" ... foreach my $elem ( @includes ) { AddIncDir( "*", $elem ) if ($elem); } $ScmGlobal = $global; # Restore global status ... } if ( (%::ScmBuildLibraries) ) # Interface/build.cfg { my( @libraries ) = split( ',', $::ScmBuildLibraries{ $ScmPlatform } ); my( $global ) = $ScmGlobal; $ScmGlobal = 1; # Follow defs are "global's" ... foreach my $elem ( @libraries ) { AddLibDir( "*", $elem ) if ($elem); } $ScmGlobal = $global; # Restore global status ... } #.. Determine the value of $ScmMachType # In the makefile GBE_MACHTYPE will be set to $ScmMachType. # # There is an compatibility issue here. # A lot of (legacy) package.pl files use GBE_MACHTYPE to specify platform # specfic directories and names. This is not to be encouraged. # # Allow for a platformm specific override # if ( exists( $::BUILDINFO{$ScmPlatform}{'SCMMACHTYPE'} )) { $ScmMachType = $::BUILDINFO{$ScmPlatform}{'SCMMACHTYPE'}; Verbose("Override ScmMachType: $ScmMachType"); } else { $ScmMachType = $ScmPlatform; } #.. Get the stuff from the Package definition file # A convention is that package.pl provide a package name via $Pbase # This may be different to the BUILDNAME. Generate a default $Pbase # to allow the package.pl to use the package name part of the buildname # $::Pbase = $::ScmBuildPackage; if ( -f "$ScmRoot/package.pl" ) { Warning ("package.pl file used. Use is being deprecated"); my( $global ) = $ScmGlobal; # Follow defs are "global's" ... $ScmGlobal = 1; require "$ScmRoot/package.pl"; $ScmGlobal = $global; # Restore global status ... if ( defined ($::ScmBuildPackage) && defined ($::Pbase) ) { # Special case. # $Pbase is set to ".". Set $Pbase to the Build Name to force # construction of a well formatted package. # $::Pbase = $::ScmBuildPackage if ( $::Pbase eq "." ); # # Error if Pbase has changed # Error ("Pbase is not the same as the BuildName (Check package.pl)", "Pbase : $::Pbase", "BuildName: $::ScmBuildPackage") if ( $::Pbase ne $::ScmBuildPackage ); } } # # Create objects to keep track of Libraies and Programs # $LIBS = MakeObject::NewType( 'Library', \@LIBS, '$(LIBDIR)/', \&GenLibName); $MLIBS = MakeObject::NewType( 'MergedLibrary', \@MLIBS, '$(LIBDIR)/', \&GenLibName); $SHLIBS = MakeObject::NewType( 'SharedLibrary', \@SHLIBS, '$(LIBDIR)/', \&GenLibName); $PROGS = MakeObject::NewType( 'Program', \@PROGS, '$(BINDIR)/', \&GenProgName); $TESTPROGS = MakeObject::NewType( 'TestProgram', \@TESTPROGS,'$(BINDIR)/', \&GenProgName); } #------------------------------------------------------------------------------- # Function : GenLibName # # Description : Helper function to generate a (static) library name # Used by MakeObject::NewType # # If the toolset doesn't support Debug and Prod, then # The library name will not have the suffix # # Inputs : arg0 - Base name of the library # arg1 - Mode: 1 == Plain. No P or D # # Returns : Name of the library as used in the makefiles # Does not include base directory # sub GenLibName { if ( $ScmToolsetSingleType || $_[1] ) { return "$_[0].$::a" } else { return "$_[0]\$(GBE_TYPE).$::a" } } #------------------------------------------------------------------------------- # Function : GenProgName # # Description : Helper function to generate a program name # Used by MakeObject::NewType # # Inputs : arg0 - Base name of the library # # Returns : Name of the program as used in the makefiles # Does not include base directory # sub GenProgName { return "$_[0]$::exe" } #------------------------------------------------------------------------------- # Function : CommandLine # # Description : Process the command line. # Arguments describes below # # Arguments : ARG0 - Root of the project # ARG1 - Path to this script # ARG2 - Target Platform # # Options follow # --interface=name - Name of interface dir # --arg=xxx - Platform argument # # Otherwise display a usage message # # Returns : Nothing # sub CommandLine { Verbose ("Command Line: @ARGV"); # # Extract options first # my $opt_help = 0; my $result = GetOptions ( "help+" => \$opt_help, "interface=s" => \$::ScmInterface, "arg=s" => sub{ AddPlatformArg( "--$_[1]") } ); Usage() if ( $opt_help || !$result ); # # Need 3 Arguments # $ScmRoot = ${ARGV[0]}; $ScmRoot = RelPath( $ScmRoot ); $ProjectBase = $ScmRoot; $ScmMakelib = ${ARGV[1]}; $ScmPlatform = ${ARGV[2]}; $ScmTarget = $ScmPlatform; Message ("[$ScmPlatform] Generate Makefile"); Debug( "root\t=$ScmRoot" ); Debug( "makelib\t=$ScmMakelib" ); Debug( "platform\t=$ScmPlatform" ); } # Usage --- # Command line usage help. #.. sub Usage { Error ( "Usage: perl makefile.pl2 [options ...]", "Valid options:", " --interface=name Set interface directory", " --arg=text Specify platform argument", ); } #------------------------------------------------------------------------------- # Function : SubDir # # Description : Include a sub-makefile # When called when processing by this script this directive # does nothing. The processing will be done by makelib.pl # # This directive MUST occur before the Platform directive # # Inputs : None that are used # # Returns : Nothing # sub SubDir { Error ("SubDir directive not allowed after the Platform directive") if ( $ScmPlatformSeen ); } ############################################################################### # Platform support ############################################################################### sub Platform { my( $global, $file ); Debug( "Platform( $ScmPlatform, @ScmPlatformArgs )" ); #.. Sanity test # Error ("Platform directive is not allowed in common makefile.pl") if ( $ScmProcessingRootMake ); Error ("Only one Platform directive is allowed") if ( $ScmPlatformSeen ); $ScmPlatformSeen = 1; #.. Arguments # $ScmTargetHost = $::ScmHost; # default #.. Common configuration # $global = $ScmGlobal; # Follow defs are "global's" ... $ScmGlobal = 1; #.. Common rules (ScmHost specific) # push( @ScmDepends, "$ScmMakelib" ); # parent $file = Require( "$::GBE_CONFIG", "Rules", "Common rules " ); push( @ScmDepends, "$file" ); #.. Platform (defines ScmToolset) # if ( ( %::ScmBuildProducts ) && # interface/build.cfg $::ScmBuildProducts{ $ScmPlatform } ) { my( @args ) = split( ',', $::ScmBuildProducts{ $ScmPlatform } ); $ScmProduct = $args[0]; $ScmTarget = $args[1]; Debug( " mapping to product $ScmProduct" ); # Platform/target specific MakeIf::PackageDirs( \@ScmPlatformDirs, $ScmPlatform, $ScmTarget ); push @ScmPlatformDirs, "$::GBE_CONFIG"; # .. plus default @ScmPlatformArgs = ( "--product=$ScmProduct", @ScmPlatformArgs ); $file = Require( "PLATFORM", $ScmTarget, "Platform definition ", @ScmPlatformDirs ); } else # standard { Debug( " native platform" ); # Platform specific MakeIf::PackageDirs( \@ScmPlatformDirs, $ScmPlatform ); push @ScmPlatformDirs, "$::GBE_CONFIG"; # .. plus default # Map all GENERIC builds onto the one platformm definition my $platformDefs = $ScmPlatform; $platformDefs = 'GENERIC' if ($::BUILDINFO{$ScmPlatform}{IS_GENERIC}); $file = Require( "PLATFORM", $platformDefs, "Platform definition ", @ScmPlatformDirs ); } push( @ScmDepends, "$file" ); Error( "Toolset undefined for platform $ScmPlatform ...") unless( $ScmToolset ); #.. Toolset # $file = Require( "$::GBE_CONFIG/TOOLSET", $ScmToolset, "Toolset definition " ); push( @ScmDepends, "$file" ); #.. Package definitions # # Global DPACKAGE definitions, which may pull in $ScmTarget specific definitions. # MakeIf::PackageLoad( $ScmPlatform ); # DPACKAGE's (if any) #.. Package extensions # Import, into the current package, files of the form gbe/DIRECTIVES # These allow the JATS directives to be extended by the contents of a package # without the need to update the core JATS release. # # Intended use: Associate a directive with a tool script, such that the # new directive simplifies the use of the tool script. # # # First: Extend the Perl Search Space to include the toolset extensions # Although the directives are in gbe/DIRECTIVES/*.pm, they may need # to reference other packages that are not. # # Look in the 'interface' and 'link' packages # The 'build' packages are duplicated into the 'interface' # for my $path ( ToolExtensionPaths() ) { UniquePush (\@INC, $path) if (glob( "$path/*.pm") || glob( "$path/*/*.pm")); } for my $entry (@{$::ScmBuildPkgRules{$ScmPlatform} }) { next if ( $entry->{'TYPE'} eq 'build' ); my $cfgdir = $entry->{'CFGDIR'}; next unless ( $cfgdir ); my $base_dir = $entry->{'ROOT'} . $cfgdir . '/DIRECTIVES'; next unless ( -d $base_dir ); foreach my $file ( glob ("$base_dir/*.pm") ) { push( @ScmDepends, "$file" ); require $file; } } # # Include local toolset extensions # These are rooted in the build directory and are not to be confused with # extensions that may be packaged # my $local_base_dir = "$ScmRoot/gbe/DIRECTIVES"; if ( -d $local_base_dir ) { foreach my $file ( glob ("$local_base_dir/*.pm") ) { push( @ScmDepends, "$file" ); require $file; } } # # All makefile.pl's will include a makefile.pl found in the build # root directory ( The same directory as build.pl ). This makefile.pl # is a little bit different - It should not "require "$ARGV[1]", nor # should it use a Platform directive. # # Note: This makefile is processed AFTER the toolset has been initialised # so that toolset extensions are available to the directives # $file = "$ScmRoot/makefile.pl"; if ( -e $file ) { $ScmProcessingRootMake = 1; require "$file"; $ScmProcessingRootMake = 0; push( @ScmDepends, "$file" ); } # # Sanity Test for platforms that do not support both debug and production # builds at the same time. This information is flagged by the toolset # which we have now loaded. # if ( $ScmToolsetSingleType ) { unless ( $ScmBuildType ) { Error ("The toolset used by the \"$ScmPlatform\" platform does not support", "both Production and Debug Builds" ); } } # # Restore global status ... # $ScmGlobal = $global; } sub PlatformRequire { my( $script, @arguments ) = @_; my( $file ); Debug( "PlatformRequire($script, @arguments)" ); push( @ScmPlatformArgs, @arguments ); # additional arguments $file = Require( "PLATFORM", $script, "PlatformRequire ", @ScmPlatformDirs ); push( @ScmDepends, "$file" ); } sub PlatformInclude { my( $script, @arguments ) = @_; my( $file ); Debug( "PlatformInclude( @_ )" ); $file = Require2( \@arguments, "PLATFORM", $script, "PlatformInclude ", @ScmPlatformDirs ); push( @ScmDepends, "$file" ); } sub PlatformDefine { Debug2( "PlatformDefine(@_)" ); Define( @_ ); } sub PlatformDefines { my( $script ) = @_; my( $line ); Debug2( "PlatformDefine(@_)" ); $script = Exists( "PLATFORM", $script, # locate image "PlatformDefines", @ScmPlatformDirs ); push( @DEFINES, "# PlatformDefines from: $script" ); open( my $fh, '<', $script ) || Error( "Opening $script" ); while (<$fh>) { $_ =~ s/\s*(\n|$)//; # kill trailing whitespace & nl push( @DEFINES, $_ ); } push( @ScmDepends, "$script" ); # makefile dependencies close( $fh ); } sub PlatformEntry { my( $prelim, $postlim, $prefix, $postfix, @elements ) = @_; my $str = "$prelim"; foreach my $element ( @elements ) { $str .= "${prefix}${element}${postfix}"; } $str .= "$postlim"; PlatformDefine( $str ); } # # Add arguments to the ScmPlatformArgs, but remove "Global" arguments # --OnlyDebug # --OnlyProduction # --NoToolSet # # Capture OnlyDebug and OnlyProd information # Will be sanitized by caller. # sub AddPlatformArg { Debug("AddPlatformArg: @_" ); foreach ( @_ ) { if ( m~^--OnlyDebug~ ) { $ScmBuildType = 'D'; } elsif ( m~--OnlyProd~ ) { $ScmBuildType = 'P'; } elsif ( m~--NoToolSet~ ) { $ScmNoToolsTest = 1; } else { UniquePush( \@::ScmPlatformArgs, $_ ); } } Debug("AddPlatformArg: Result: @::ScmPlatformArgs" ); 1; } ############################################################################### # Toolset support # # Toolset( 'platform [, ... ]', name, [arg, ... ] ) # Specify the toolset for a platform # # ToolDefine( ) # ToolDefines( ) # Specifies toolset defines for insertion into the target makefile. # # ToolsetDir # Define toolset created directory(s) for removal during # 'clean' operations. # # ToolsetGenerate # Define toolset created file(s) for removal during # 'clean' operations. # # ToolsetObj # Define toolset created object(s) for removal during # 'clean' operations. # # ToolsetLib # Define toolset created library(s) for removal during # 'clean' operations. # # ToolsetProg # Define toolset created prog(s) for removal during # 'clean' operations. # # ToolsetRule( ) # ToolsetRules( ) # Specifies toolset rules for insertion into the target makefile. # ############################################################################## sub Toolset { my( $platforms, $toolset, @arguments ) = @_; Debug2( "Toolset(@_)" ); return 1 if ( ! ActivePlatform($platforms) ); $ScmToolset = $toolset; @ScmToolsetArgs = @arguments; return 1; } sub ToolsetRequire { my( $script, @arguments ) = @_; my( $file ); Debug2( "ToolsetRequire(@_)" ); @ScmToolsetArgs = @arguments; $file = Require( "", $script, "ToolsetRequire", "$::GBE_CONFIG/TOOLSET", @::BUILDTOOLSPATH ); push( @ScmDepends, "$file" ); } sub ToolsetDefine { Debug2( "ToolsetDefine(@_)" ); Define( @_ ); } sub ToolsetDefines { Debug2( "ToolsetDefines(@_)" ); Defines( "$::GBE_CONFIG/TOOLSET", @_ ); } sub ToolsetDir { Debug2( "ToolsetDir(@_)" ); UniquePush ( \@TOOLSETDIRS, @_ ); } sub ToolsetDirTree { Debug2( "ToolsetDirTree(@_)" ); UniquePush ( \@TOOLSETDIRTREES, @_); } sub ToolsetGenerate { Debug2( "ToolsetGenerate(@_)" ); UniquePush( \@TOOLSETGENERATED, @_ ); } sub ToolsetObj { Debug2( "ToolsetObj(@_)" ); foreach my $obj ( @_ ) { UniquePush( \@TOOLSETOBJS, "$obj.$::o" ); } } sub ToolsetLib { Debug2( "ToolsetLib(@_)" ); foreach my $lib ( @_ ) { UniquePush( \@TOOLSETLIBS, GenLibName( $lib ) ); } } sub ToolsetProg { Debug2( "ToolsetProg(@_)" ); foreach my $prog ( @_ ) { UniquePush( \@TOOLSETPROGS, GenProgName( $prog ) ); } } sub ToolsetRule { Debug2( "ToolsetRule(@_)" ); push( @TOOLSETRULES, @_ ); } sub ToolsetRules { my( $script ) = @_; my( $line ); Debug2( "ToolsetRules(@_)" ); $script = Exists( "$::GBE_CONFIG/TOOLSET", $script, "ToolsetRules" ); push( @TOOLSETRULES, "# ToolsetRules from: $script" ); open( my $fh, '<', $script ) || Error( "Opening $script" ); while (<$fh>) { $_ =~ s/\s*(\n|$)//; # kill trailing whitespace & newline push( @TOOLSETRULES, $_ ); } push( @ScmDepends, "$script" ); # makefile dependencies close( $fh ); } #------------------------------------------------------------------------------- # Function : ToolsetAddUnitTestPreProcess # ToolsetAddUnitTestPostProcess # ToolsetAddUnitTestCollateProcess # # Description : Functions to allow toolsets to add recipes to be run before # and after Unit Tests are run. # # Inputs : $target - Name of the recipe to be run # # Returns : Nothing # sub ToolsetAddUnitTestPreProcess { _ToolsetAddUnitTest(\@TOOLSET_UTF_PRE, @_ ); } sub ToolsetAddUnitTestPostProcess { _ToolsetAddUnitTest(\@TOOLSET_UTF_POST, @_ ); } sub ToolsetAddUnitTestCollateProcess { _ToolsetAddUnitTest(\@TOOLSET_UTF_COLLATE, @_ ); } #------------------------------------------------------------------------------- # Function : _ToolsetAddUnitTest # # Description : Internal helper function used by ToolsetAddUnitTest* # # Inputs : $aref - Ref to an array of names to extend # $target - Name of recipe to run # # Returns : Nothing # sub _ToolsetAddUnitTest { my ($aref, $target ) = @_; # Determine name of parent function my $fname = (caller(1))[3]; $fname =~ s~.*::~~; Debug2( "$fname ($target)" ); # # Ensure user is not using a reserved target # if (grep {$_ eq $target} @reservedMakeTargets) { Error("Internal: $fname uses reserved make taget: $target"); } push @$aref, $target; } ############################################################################### # User interface: # # AddFlags( 'platform [, ... ]', 'flags' [, 'flag' ... ] ) # This subroutine takes the C and C++ compiler flags # specified adding them to a global list for later # inclusion in the built makefile. # # AddCFlags( 'platform [, ... ]', 'flags' [, 'flag' ... ] ) # This subroutine takes the C compiler flags # specified adding them to a global list for later # inclusion in the built makefile. # # AddCXXFlags( 'platform [, ... ]', 'flags' [, 'flag' ... ] ) # This subroutine takes the C++ compiler flags # specified adding them to a global list for later # inclusion in the built makefile. # # AddLintFlags( 'platform [, ... ]', 'flags' [, ... ] ) # This subroutine takes the Lint flags specified # adding them to a global list for later inclusion # in the built makefile. # # AddASFlags( 'platform [, ... ]', 'flags' [, ... ] ) # This subroutine takes the Assemler flags specified # adding them to a global list for later inclusion # in the built makefile. # # AddLDFlags( 'platform [, ... ]', 'flags' [, ... ] ) # This subroutine takes the Linker flags specified # adding them to a global list for later inclusion # in the built makefile. # # AddDir # This subroutine takes the directories specified adding # them to a global include and source directory list for # later inclusion in the built makefile. # # AddIncDir( 'platform [, ... ]', 'dir' [, ... ] ) # This subroutine takes the include file directories # specified adding them to a global list for later # inclusion in the built makefile. # # AddSrcDir( 'platform [, ... ]', 'dir' [, ... ] ) # This subroutine takes the source file directories # specified adding them to a global list used to resolve # Src() definitions. # # AddLibDir( 'platform [, ... ]', 'dir' [, ... ] ) # This subroutine takes the library directories # specified adding them to a global list for later # inclusion in the built makefile. # # AddSourceType( 'ext', '.c|.cc|.asm' ) # This subroutine takes the extension(s) specified by the # programmer and adds them to a global list for later # inclusion in the built makefile. This list contains # the extensions to be recognised as 'C', 'C++' or # assembler file types. # # AddSourceFile( 'platform [, ... ]', 'file' [, ... ] ) # This subroutine takes the non-standard source file(s) # and adds them add it to either C, C++ or assembler # sources and the object list. # # Init( 'platform [, ... ]', 'rule' ) # Initialisation rule # # Generate( 'platform [, ... ]', 'file' [, ... ] ) # This subroutine is used to add the list of given # source files to the generate sources list, and if # the generated source is of type C, C++ or assember # also adds it to either C, C++ or assembler sources and # the object lists. # # --c Treat as a C source file. # --cpp Treat as a C++ source file. # --asm Treat as a assembler source file. # # Rule( 'platform [, ... ]', definition ) # This subroutine is used to add the non-standard make # rules required to build the system. eg. any rules # necessary to produce a .cc & .h file from a .x file. # # Src( 'platform [, ... ]', 'file' [, ... ], [ 'arg' [, ...]] ) # This subroutine is used to add the list of given source # files to the sources list, and if the source is of type # C, C++ or assember also adds it to either C, C++ or # assembler sources and the object lists. The optional # list of arguments is assigned to all source files. # # --c Treat as a C source file. # --cpp Treat as a C++ source file. # --asm Treat as a assembler source file. # --Shared Shared, produces position-independent # code (on targets where required). # # Lib( 'platform [, ... ]', 'name', 'obj' [, ... ] [, '-arg' [, ... ]] ) # This subroutine takes a library definition list and adds # the entries to the 3 libraries definition lists. 'name' # of the library to be created. List of the object files # 'obj' that make up this library. List of special # arguments 'arg' to pass to the librarian. # # MergeLibrary( 'platform [, ... ]', 'name', 'lib' [, ... ] ) # This subroutine takes a library merge list and adds # the entries to the 2 merge libraries definition lists. 'name' # of the library to be created. List of the libraries to be merged # # LocalScript( 'platform [, ... ]', name, ['1'] ) # Script( 'platform [, ... ]', name, ['1'] ) # This subroutine takes a list that defines the name of # the script to be placed in the platform 'bin' directory, # and an optional second element that defines whether the # script should be made executable or not. # # Prog( 'platform [, ... ]', 'name', ['obj', ... ], # ['-llib', ... ], ['options'] ) # This subroutine takes a list that defines which program # (binary) is to be made, what libraries and object it is # made from, and any special commands required to perform # the program creation. # # @PROGS Updated list of programs to create # # TestProg( 'platform [, ... ]', 'name', ['obj', ... ], # ['-llib', ... ], ['options'] ) # This subroutine takes a list that defines which test program # (binary) is to be made, what libraries and object it is # made from, and any special commands required to perform # the program creation. # # @TESTPROGS Updated list of programs to create # # InstallHdr( 'platform [, ... ]', 'file' [, ...], ['-arg'] ) # This subroutine takes the given list of files and adds them # to the install header files list. Files in this list will be # installed into the 'local header directory' area for public # consumption. This is generally API files for other modules # to use. # # --Strip Strip directory from source # --Full Install using full path # --Subdir=subdir Install within the specified sub-directory # --Prefix=subdir " " " " " " # # InstallLib( 'platform [, ... ]', 'file', ['subdir'] ) # This subroutine takes the given list of files and adds them # to the install libraries files list. Files in this list will # be installed into the 'local library directory' area for # public consumption. # # InstallProg( 'platform [, ... ]', 'file', ['subdir'] ) ) # This subroutine takes a list that defines the executable file # that is to be installed. The file in this list will be # installed into the 'local executable directory' specified for # public consumption. # ############################################################################### sub Include # User include { my( $path, $name ) = @_; my( $file ); $file = Require( $path, $name, "Include" ); push( @ScmDepends, "$file" ); } sub ForceCCompile { CompileOptions( $_[0], 'compile_as_c' ); # Backward compatability } #------------------------------------------------------------------------------- # Create a data structure to define the global compiler options # The hash is keyed by compiler option # The value contains another hash. # The key is a makefile variable to set ( or remove ) # The value is the value to assign to the makefile variable # If the value is 'undef' then the variable will be deleted # # Keys of the form key=value are also supported # # If the value is a CODE reference, then routine will be called with the key # and value as arguments. The return value will be utilised. # our %ScmCompilerOptions = ( 'strict_ansi' => { 'USE_STRICT_ANSI' => '1' }, 'no_strict_ansi' => { 'USE_STRICT_ANSI' => '' }, # Default 'profile' => { 'USE_PROFILE' => '1' }, 'no_profile' => { 'USE_PROFILE' => '' }, # Default 'prod_no_optimise' => { 'PROD_USE_OPTIMISE' => '' }, 'prod_no_debuginfo' => { 'PROD_USE_DEBUGINFO' => '' }, # Default 'prod_optimise' => { 'PROD_USE_OPTIMISE' => '1' }, # Default 'prod_debuginfo' => { 'PROD_USE_DEBUGINFO' => '1' }, 'debug_no_optimise' => { 'DEBUG_USE_OPTIMISE' => '' }, # Default 'debug_no_debuginfo' => { 'DEBUG_USE_DEBUGINFO' => '' }, 'debug_optimise' => { 'DEBUG_USE_OPTIMISE' => '1' }, 'debug_debuginfo' => { 'DEBUG_USE_DEBUGINFO' => '1' }, # Default 'compile_as_cpp' => { 'FORCE_CC_COMPILE' => '1', 'FORCE_C_COMPILE' => undef }, 'compile_as_c' => { 'FORCE_C_COMPILE' => '1', 'FORCE_CC_COMPILE' => undef }, 'no_define_source_file' => { 'DISABLE__SOURCE__' => '1' }, 'define_source_file' => { 'DISABLE__SOURCE__' => undef }, # Default ); # # The toolset can extend the options by setting the following hash # our %ScmToolsetCompilerOptions = (); # # Define default compiler options # These are makefile variables that will be assigned # our %ScmCompilerOpts = ( 'USE_STRICT_ANSI' => '', 'USE_PROFILE' => '', 'PROD_USE_DEBUGINFO' => '', 'PROD_USE_OPTIMISE' => '1', 'DEBUG_USE_OPTIMISE' => '', 'DEBUG_USE_DEBUGINFO' => '1', ); sub CompileOptions { my( $platforms, @elements ) = @_; return if ( ! ActivePlatform($platforms) ); for (@elements) { my $oref; # # The toolset option may be a text string or a definition # Name - A text string # Name=Value - A value # my $value; my $key = $_; if ( $key =~ m~(.*=)(.*)~ ) { $key = $1; $value = $2 || ''; } $key = lc( $key ); # # Examine the global flags # Then the toolset extensions # Then just drop it # unless ( $oref = ($ScmCompilerOptions{$key} || $ScmToolsetCompilerOptions{$key}) ) { Warning ("Compile Option ignored: $_"); next; } # # Parse the definition and adjust makefile variables as required # Set the value of a make variable or remove the definition # # If the user value is a code reference, then call the code # and use the returned value as the value. # while ( (my($ukey, $uvalue)) = each %{$oref} ) { if ( defined( $uvalue) ) { if ( ref($uvalue) eq "CODE" ) { $uvalue = &$uvalue( $key, $value, $ukey); unless ( defined $uvalue ) { Warning ("Compile Option ignored: $_"); next; } } elsif ( defined $value ) { $uvalue = $value; } $ScmCompilerOpts{$ukey} = $uvalue; } else { delete $ScmCompilerOpts{$ukey}; } } } } #------------------------------------------------------------------------------- # Function : AddFlags # AddCFlags # AddCXXFlags # AddASFlags # AddLDFlags # AddLintFlags # # Description : Add target specfic flags to the C compiler # This SHOULD only be used to add Defines to the compiler # but it can be absued. # # Inputs : $platform - Platforms for which the directive is active # ... - list of flags to add # # Embedded options include: # --Debug - Following options are added to the debug build # --Prod - Following options are added to the production build # # Returns : Nothing # sub AddFlags { my( $platforms, @elements ) = @_; AddCFlags( $platforms, @elements ); AddCXXFlags( $platforms, @elements ); } sub AddCFlags { my( $platforms, @elements ) = @_; Debug2( "AddCFlags($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); WarnIfNastyFlag( @elements ); __AddFlags( "CFLAGS", \@elements, \@CFLAGS, \@CLINTFLAGS, \@CFLAGS_DEBUG, \@CLINTFLAGS_DEBUG, \@CFLAGS_PROD, \@CLINTFLAGS_PROD ); } sub AddCXXFlags { my( $platforms, @elements ) = @_; Debug2( "AddCXXFlags($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); WarnIfNastyFlag( @elements ); __AddFlags( "CXXFLAGS", \@elements, \@CXXFLAGS, \@CXXLINTFLAGS, \@CXXFLAGS_DEBUG, \@CXXLINTFLAGS_DEBUG, \@CXXFLAGS_PROD, \@CXXLINTFLAGS_PROD ); } sub AddASFlags { my( $platforms, @elements ) = @_; Debug2( "AddASFlags($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); __AddFlags( "ASFLAGS", \@elements, \@ASFLAGS, undef, \@ASFLAGS_DEBUG, undef, \@ASFLAGS_PROD, undef ); } sub AddLDFlags { my( $platforms, @elements ) = @_; Debug2( "AddLDFlags($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); foreach ( @elements ) { next if ( m~^--(Debug|Prod)~ ); Warning("Use of linker flag discouraged (will be used): $_"); } __AddFlags( "LDFLAGS", \@elements, \@LDFLAGS, undef, \@LDFLAGS_DEBUG, undef, \@LDFLAGS_PROD, undef ); } sub AddLintFlags { my( $platforms, @elements ) = @_; return if ( ! ActivePlatform($platforms) ); Debug2( "AddLintFlags($platforms, @elements)" ); __AddFlags( "LINTFLAG", \@elements, \@CLINTFLAGS, \@CXXLINTFLAGS, \@CLINTFLAGS_DEBUG, \@CXXLINTFLAGS_DEBUG, \@CLINTFLAGS_PROD, \@CXXLINTFLAGS_PROD ); } #------------------------------------------------------------------------------- # Function : __AddFlags # # Description : Generic flag adding to lists routine # Internal use only # # Supports --Debug and --Prod options # if the appropriate list is present. # # Inputs : Lots # References to compiler and lint flags for # common, debug and product builds. # # Not all the lists are needed. # # Returns : Nothing # sub __AddFlags { my ($textname, $eref, $f_all, $flint_all, $f_debug, $flint_debug, $f_prod, $flint_prod ) = @_; # # Start added flags to the ALL lists # my $list = $f_all; my $lintlist = $flint_all; my $nowarn = 0; # # Process flags up front # $nowarn = 1 if ( grep (/^--NoWarn$/, @$eref) ); # # Process all the user arguments # ADD: foreach my $element ( @$eref ) { # # Skip flags # if ( $element eq '--NoWarn' ) { next; } # # Detect --Debug and --Prod options and swap # lists accordingly. # if ( $element eq '--Debug' ) { Error ("--Debug not supported for $textname") unless ( $f_debug ); $list = $f_debug; $lintlist = $flint_debug; next; } if ( $element eq '--Prod' ) { Error ("--Prod not supported for $textname") unless ( $f_prod ); $list = $f_prod; $lintlist = $flint_prod; next; } # # Scan all the lists for a possible duplicates # foreach my $temp ( @$f_all, @$f_debug, @$f_prod ) { if ($temp eq $element) { Warning( "Duplicate $textname ignored '$element'") unless $nowarn; next ADD; } } # # Add the flag to the compiler and lint lists # push( @$list, $element ) if $list; push( @$lintlist, $element ) if $lintlist; } } sub WarnIfNastyFlag { foreach ( @_ ) { Warning("Use of compiler flags discouraged (will be used): $_") unless ( m/^-[DU]/ || m/^--Debug/ || m/^--Prod/ || /^--NoWarn/ ); } } sub AddDir { AddIncDir( @_); AddSrcDir( @_ ); } sub AddIncDir { _AddDir( 'AddIncDir', 'INCDIR', \@INCDIRS, \@S_INCDIRS, \@G_INCDIRS, \@L_INCDIRS, @_ ); } sub AddSrcDir { _AddDir( 'AddSrcDir', 'SRCDIR', \@SRCDIRS, \@S_SRCDIRS, \@G_SRCDIRS, \@L_SRCDIRS, @_ ); } sub AddLibDir { _AddDir( 'AddLibDir', 'LIBDIR', \@LIBDIRS, \@S_LIBDIRS, \@G_LIBDIRS, \@L_LIBDIRS, @_ ); } #------------------------------------------------------------------------------- # Function : _AddDir # # Description : Internal routine to add a directory to list of directories # Common code to simplify implementation of other directives # # Inputs : $name - Name of function # $udir - User name of dir list # $dirref - Reference to directory array # $s_dirref - Reference to system directory array # $g_dirref - Reference to global directory array # $l_dirref - Reference to local directory array # @args - User arguments # - platforms # - Directories and --Options # sub _AddDir { my( $name, $udir, $dirref, $s_dirref, $g_dirref, $l_dirref, $platforms, @elements ) = @_; Debug ( "$name($platforms, @elements)" ); Error ( "$name: Insufficient arguments") unless ( @elements ); return if ( ! ActivePlatform($platforms) ); # # Cleanup user parameters # foreach ( @elements ) { s/^\s+//; # Remove leading space s/\s+$//; # Remove trailing spaces s~/$~~; # Remove trailing / s~//~/~g; # Remove multiple / } #.. Collect arguments my $tlist_ref = $ScmGlobal ? $g_dirref : $l_dirref; # "current" scope .... my $nowarn = 0; my $nodepend = 0; my @dirs; foreach ( @elements ) { if ( ! /^--/ ) { # Collect directories push @dirs, $_; } elsif (/^--Local$/) { # "local" scope .... $tlist_ref = $l_dirref; } elsif (/^--Global$/) { # "global" scope ... $tlist_ref = $g_dirref; } elsif (/^--System$/) { # "system" scope ... $tlist_ref = $s_dirref; } elsif (/^--NoDepend$/) { # Split from dependency list if ( $udir eq 'INCDIR' ) { # AddIncDir only $nodepend = 1; } } elsif (/^--NoWarn$/) { # Disable warnings $nowarn = 1; } elsif (/^--(.*)/) { Message( "$name: unknown option $_ -- ignored\n" ); } } Error ( "$name: No directory specified: ($platforms, @elements)" ) unless ( @dirs ); #.. Push source path(s) foreach ( @dirs ) { # # Add to complete list of directories # Warn on duplicates # unless ( UniquePush( $dirref, $_) ) { Warning( "Duplicate $udir ignored '$_'" ) unless ( $nowarn ); next; } # # Check that the directory actually exists # If the path contains a $(XXXXX) then it can't be checked # if ( index( $_, '$' ) == -1 ) { Warning( "$name. Directory not found: $_", "Current directory : $::Cwd", "Cannot resolved Directory : " . AbsPath($_, $::Cwd, 1), ) unless ( $nowarn || -d $_ ); } # # Add to suitable list # push @{$tlist_ref}, $_; # # Add to the no dependancy list (ie generated depend file) # Only used by AddIncDir, accepted by AddSrcDir # push( @NODEPDIRS, $_ ) if ($nodepend); } } sub AddProg { my( $platforms, @progs ) = @_; Debug2( "AddProg($platforms, @progs)" ); return if ( ! ActivePlatform($platforms) ); foreach my $prog (@progs) { my $pProg = $PROGS->Get($prog); Warning( "Duplicate prog ignored '$prog'" ) if ( $pProg ); $pProg = $PROGS->NewAdd($prog) } } sub AddSourceType { my( $ext, $type ) = @_; Debug2( "AddSourceType(@_)" ); # # Default Source Type (C) # $type = ".c" unless ( $type ); Error ("Source type '$ext' not allowed") if ( $ext !~ /^\.\w+$/ ); $type = lc($type) if ( $::ScmHost ne "Unix" ); $ScmSourceTypes{ $ext } = $type; } sub AddSourceFile { my( $platforms, @elements ) = @_; Debug2( "AddSourceFile($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); foreach my $path ( @elements ) { __AddSourceFile( 1, $path ); } } #------------------------------------------------------------------------------- # Function : __AddSourceFile # # Description : Internal function # Add a source file to internal lists # # Assumes that the current platform is ACTIVE # # Inputs : push 0: Don't push onto OBJS (non-shared objfiles) # path Filename.extension # obj object file name (optional) # type Type of file. "" -> auto detect # # Returns : True - File is a 'known' source file # False - File is not a 'known' source file # sub __AddSourceFile { my( $push, $path, $obj, $type ) = @_; my( $filename, $ext, $srcfile, $is_obj, $ext_type, $result ); $filename = StripDir($path); # file name $ext = StripFile($path); # extension $ext = lc($ext) if ( $::ScmHost ne "Unix" ); if (! ($srcfile = $SRCS{$filename})) { $srcfile = $path; # generated } $obj = StripExt( $filename ) # Base name of object file if ( ! defined($obj) || $obj eq "" ); $type = "" # optional type if ( ! defined( $type ) ); # # Push file onto a suitable source file list # $result = 0; $ext_type = ""; # map extension $ext_type = $ScmSourceTypes{ $ext } if ( exists( $ScmSourceTypes{ $ext } ) ); $result = 1 if ( $ext_type ); if ( $type eq "" && defined $::ScmToolsetProgSource{$ext} ) { Debug( "SourceFile: $path is ToolsetProgSource -> $srcfile" ); push( @CSRCS, $srcfile ); $result = 1; } elsif ( ($type eq "" && $ext_type eq ".h") || ($type eq ".h") ) { Debug( "SourceFile: $path is .h -> $srcfile" ); push( @CHDRS, $srcfile ); } elsif ( ($type eq "" && $ext_type eq ".inc") || ($type eq ".inc") ) { Debug( "SourceFile: $path is .inc -> $srcfile" ); push( @ASHDRS, $srcfile ); } elsif ( ($type eq "" && $ext_type eq ".c") || ($type eq ".c") ) { Debug( "SourceFile: $path is .c -> $srcfile=$obj" ); push( @CSRCS, $srcfile ); $is_obj = 1; } elsif ( ($type eq "" && $ext_type eq ".cc") || ($type eq ".cc") ) { Debug( "SourceFile: $path is .cc -> $srcfile=$obj" ); push( @CXXSRCS, $srcfile ); $is_obj = 1; } elsif ( ($type eq "" && $ext_type eq ".asm") || ($type eq ".asm") ) { Debug( "SourceFile: $path is .asm -> $srcfile=$obj" ); push( @ASSRCS, $srcfile ); $is_obj = 1; } elsif ( $ext_type eq "--Ignore" ) { # ignored ... # .x "rpcgen" source files # .ini Configuration # .sh Shell script } else { Debug( "SourceFile: $path is unknown file type" ); # # Insert source files with unknown extensions onto lists # of there own type # if ( $ext ) { (my $varname = uc ( $ext . 'SRCS')) =~ s~\.~~g; no strict 'refs'; push @$varname, $srcfile; use strict 'refs'; } } # # See if there is a hook function for this type of source file # Invoke user function to perform additional processing on the file # if ( %MF_RegisterSrcHooks ) { my @listeners; push @listeners, @{$MF_RegisterSrcHooks{$ext}} if ( exists $MF_RegisterSrcHooks{$ext} ); push @listeners, @{$MF_RegisterSrcHooks{'*'}} if ( exists $MF_RegisterSrcHooks{'*'} ); while ( @listeners ) { Debug( "RegisterSrcHook: Invoke SrcHook function" ); my ($fname, @args) = @{shift @listeners}; &$fname ( $srcfile ,$filename, $obj, $ext ,@args ); } } # # Object files are saved in # OBJSOURCE - Generate a recipe to create the object # OBJS - A list of ALL non-shared object files # if ( $is_obj && $::o ) { $OBJSOURCE{ "$obj" } = $srcfile; push( @OBJS, $obj ) if ($push); } # # Indicate to the user that the file is a 'known' source file # This implies that the file is required early in the build process # and may need to be generated early. # return $result; } #------------------------------------------------------------------------------- # Function : SetValue # # Description : Defines a variable that can be used within the makefile.pl # Use sparingly # An attempt to formalise a mechanism that is used anyway, but # with correct platform detection # # Inputs : $platform - Platform selector # $name - Name to set # $value - Value to set # options - Options # --NoWarn # --Project=xxxx[,xxxx]+ # -- # sub SetValue { my( $platforms, @elements ) = @_; my $name; my $value; my $nowarn; my $nomoreswicthes = 0; Debug2( "SetValue($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); # # Process elements extracting values and options # foreach ( @elements ) { if ( m/^--$/ ) { $nomoreswicthes = ! $nomoreswicthes; next; } if ( m/^--/ && ! $nomoreswicthes ) { if ( m/^--NoWarn/ ) { $nowarn = 1; } elsif ( m/^--Project=(.*)/ ) { return unless ( ActiveProject( $1) ); } else { Error ("SetValue: Unknown option: $_"); } } elsif ( ! defined $name ) { $name = $_; } elsif ( ! defined $value ) { $value = $_; } else { Error ("SetValue: $name. Too many parameters" ); } } # # Warn if the named variable already exists # It may be a JATS internal or it may be a user. # unless ( $nowarn ) { no strict 'refs'; Warning("SetValue: $name. Redefined") if defined ( $$name ); use strict 'refs'; } # # Set the value # no strict 'refs'; $$name = $value; use strict 'refs'; } #------------------------------------------------------------------------------- # Function : SetList # # Description : Defines a list variable that can be used within the makefile.pl # Use sparingly # An attempt to formalise a mechanism that is used anyway, but # with correct platform detection # # Inputs : $platform - Platform selector # $name - Name to set # $value,... - Values to set # options - Options # --NoWarn # --Project=xxxx[,xxxx]+ # --Unique # --Clear # --Append # -- # my %SetList_names; sub SetList { my( $platforms, @elements ) = @_; my $name; my @value; my $nowarn; my $unique; my $clear; my $nomoreswicthes = 0; Debug2( "SetList($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); # # Process elements extracting values and options # foreach ( @elements ) { if ( m/^--$/ ) { $nomoreswicthes = ! $nomoreswicthes; next; } if ( m/^--/ && ! $nomoreswicthes ) { if ( m/^--NoWarn/ ) { $nowarn = 1; } elsif ( m/^--Project=(.*)/ ) { return unless ( ActiveProject( $1) ); } elsif ( m/^--Unique/ ) { $unique = 1; } elsif ( m/^--Clear/ ) { $clear = 1; } elsif ( m/^--Append/ ) { $clear = 0; } else { Error ("SetList: Unknown option: $_"); } } elsif ( ! defined $name ) { $name = $_; } else { push @value, $_; } } Error ("SetList: No name specified") unless ( $name ); # # Warn if the named variable already exists # It may be a JATS internal or it may be a user. # # Only do this iff the name is not known to this function # Keep a list a names that have been set. # if ( ! $SetList_names{$name} && ! $nowarn ) { no strict 'refs'; Warning("SetList: $name. Defined outside the ScanList/SetList directive","May clash with Jats internals") if ( @$name ); use strict 'refs'; } $SetList_names{$name} = 1; # # Clear list # if ( $clear ) { no strict 'refs'; @$name = (); use strict 'refs'; } # # Set the value # no strict 'refs'; if ( $unique ) { UniquePush( \@$name, @value); } else { push @$name, @value; } use strict 'refs'; } #------------------------------------------------------------------------------- # Function : ScanList # # Description : Create a list by scanning for files in a directory # The files may be in a local directory or within a package # Care must be taken when using a package as the results # may differ bewteen BuildPkgArchive and LinkPkgArchive # # Interworks with SetList # # Inputs : $platform - Platform selector # $name - Name to set # $value,... - Values to set # options - Options # --NoWarn # --Project=xxxx[,xxxx]+ # --Unique # --Clear # --Append # # --Package=xxxx[,ext] # --Dir=xxx # # --Subdir=yyy # --DirListOnly # --FileListOnly # --Recurse (default) # --NoRecurse # --FullPath (default) # --NoFullPath # # --FilterIn=xxx # --FilterInRe=xxx # --FilterOut=xxx # --FilterOutRe=xxx # # Returns : # sub ScanList { my( $platforms, @elements ) = @_; my $name; my $package; my $dir; my $subdir; my @set_args; my $search = JatsLocateFiles->new('Recurse','FullPath' ); Debug2( "ScanList($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); # # Process elements extracting values and options # foreach ( @elements ) { if ( m/^--Unique|--Clear|--Append|--NoWarn/ ) { push @set_args, $_; } elsif ( m/^--Project=(.*)/ ) { return unless ( ActiveProject( $1) ); } elsif ( m/^--Package=(.*)/ ) { $package = $1; } elsif ( m/^--Dir=(.*)/ ) { $dir = $1; } elsif ( m/^--Subdir=(.*)/ ) { $subdir = $1; } elsif ( $search->option( $_ ) ) { Verbose ("Search Option: $_" ); } elsif ( m/^--/ ) { Error ("ScanList: Unknown option: $_"); } elsif ( ! defined $name ) { $name = $_; } else { Error ("ScanList $name: Unknown option: $_"); } } Error ("ScanList: No variable name specified") unless ( $name ); Error ("ScanList: Must Specify --Dir or --Package") unless ( $dir || $package ); Error ("ScanList: --Dir and --Package are mutually exclusive") if ( $dir && $package ); # # Locate the base of the scan # This may be either a package name or a local directory # # Its no use allowing the user to use OBJ/LIB/BIN directories as the # directories MUST exist at build time. Don't really want the user doing # that level of poking out of a package # if ( $package ) { $dir = GetPackageBase( "ScanList", $package ); Error ("ScanList: Package not found: $package") unless ( $dir ); } else { Error ("ScanList: Root directory not found: $dir") unless ( -d $dir ); } if ( $subdir ) { $dir .= "/" . $subdir; Error ("ScanList: Sub directory not found: $subdir") unless ( -d $dir ); } # # Use SetList to do the rest of the work # SetList( $platforms, $name, @set_args, '--', $search->search($dir) ); } sub Init { push( @INITS, @_ ); } #------------------------------------------------------------------------------- # Function : Generate # # Description : Legacy Function - don't use unless you have too. # Flags files that are to be generated during the # early 'generate' make phase. Will also add named files # to various internal lists # # Intended to be used in conjunction with the 'Rule' directive # to flag header and source files that need to be created early # in the build process. # # Inputs : See GenerateSrcFile # # Returns : # sub Generate { my( $platforms, @elements ) = @_; Debug2( "Generate($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); Message("Generate directive used. Consider replacing with GenerateFiles"); # # Use Non-warning version to do the hard work # GenerateSrcFile( 1, @elements ); } #------------------------------------------------------------------------------- # Function : Generated # # Description : Legacy Function - don't use unless you have too. # Flags files that are generated by misc Rules # # Intended to be used in conjunction with the 'Rule' directive # to mark files that have been generated, so that they can be # cleaned up. # # Note the difference to the 'Generate' directive which will # ensure that the Rule will be run in the 'generate' phase, # this directive doesn't. # # Inputs : Files with internal Makefile Paths and codes # Eg: Generated( '*', "\$(LIBDIR)/libcsf\$(GBE_TYPE).\${a}" ); # See why its YUK! # # Returns : # sub Generated { my( $platforms, @elements ) = @_; my( @args ); return if ( ! ActivePlatform($platforms) ); Debug2( "Generated($platforms, @elements)" ); #.. Collect arguments # foreach ( @elements ) { if ( /^-(.*)/ ) { Debug( "Gen: arg $_" ); push ( @args, $_); } } #.. Push source file(s) # foreach ( @elements ) { if ( ! /^-(.*)/ ) { Debug( "Generated: $_ (@args)" ); push (@USERGENERATED, $_); # # Add the file to the list of known source files # This will allow them to be packaged # GenerateSrcFile (0, $_ ); } } } #------------------------------------------------------------------------------- # Function : GenerateSrcFile # # Description : Internal Function (No $platform) # Determine how to handle a 'Generated' file # # # Inputs : $generated - 0: Don't add to GENERATED List # 1: Add to GENERATED List # 2: Add to GENERATED List, if a source file # FileName(s) - Name of one or more files to process # All files are processed in the same way # These file may contain Makefile prefixes # ie: $(OBJDIR)/file.obj # Options: # --c - Hint: Its a "C" file # --cpp - Hint: Its a C++ file # --asm - Hint: Its an ASM file # -* - Save as argument attached to the file # # Returns : Number of 'source' file # sub GenerateSrcFile # Internal Function - no $platform { my( $generated, @elements ) = @_; my( $type, @args ); my $result = 0; Debug2( "GenerateSrcFile($generated,@elements)" ); #.. Collect arguments # $type = ""; foreach ( @elements ) { if ( /^--c$/ ) { Debug( "Gen: --c" ); $type = ".c"; } elsif ( /^--cpp$/ ) { Debug( "Gen: --cpp" ); $type = ".cc"; } elsif ( /^--asm$/ ) { Debug( "Gen: --asm" ); $type = ".asm"; } elsif ( /^-(.*)/ ) { Debug( "Src: arg $_" ); push @args, $_; } } #.. Process source file(s) # # Determine if file is already a known SRCS file - skip if already known # Update SRCS data # Update SRC_TYPE data # Update SRC_ARGS data # Add the file to a suitable source file list ie: @CHDRS,... # Flag as a GENERATED file - These will be processed during the 'generate' phase # foreach my $source ( @elements ) { next if ( $source =~ /^-(.*)/ ); # Not a source file my $basename = StripDir( $source ); Debug( "Generate: $source=$basename (@args)" ); if ($SRCS{ $basename }) { Warning( "Duplicate src ignored '$source'" ); next; } $SRCS{ $basename } = $source; HashJoin( \%SRC_ARGS, $;, $basename, @args ) if (@args); $SRC_TYPE{ $basename } = $type if ($type); # # Add the file to any source file lists that it may like to know # about this file. # # If the file was a known source file, then it may need to be generated # very early in the build process. # my $src_file_type = __AddSourceFile( 1, $basename ); if ($generated == 1 || ($src_file_type && $generated > 1) ) { push(@GENERATED, $source); $result++; } else { push(@GENERATED_NOTSRC, $source); } } return $result; } #------------------------------------------------------------------------------- # Function : GenerateFiles # # Description : Generate files in a controlled manner using a specified # tool to perform the task # # Inputs : $1 - platform specifier '*' (comma delemitered) # $2 - Tool Name # $3... - Command line argument to generate files with embedded information # - or options. Multiple command line arguments will be joind with # a single space # # The generated files will be placed in the OBJ directory for # the current target platform. This allows different files to # be generated for each platform, without collision. # # The full name of the generated files will be added to the list of # source files. Thus the user does not need to know the # full name of the file - it will be tracked by JATS. # # If a generated file is a header file, then the OBJ directory # will be added as AddIncDir() so that the header files can be # extracted # # If a generated file is a "C"/"C++" source file, then it will # compiled and the object file made available # # The tool name may be: # --Tool=name or "name" # Look in the tool paths in Packages # Look in the JATS tool directory for named script # Look in the JATS bin directory for the named exe # Look in the users path ( and generate a warning ) # Give up and hope magic happens later # --Script=name # Resolve the name using known Src paths # The script may be generated and need not exist # at the time the makefile is created. # --Shell # The command line argument is a shell script that # will be passed to a simple shell. # --Prog=name # Resolve to a program generated within this makefile # # # The command line argument contains keywords to allow # information to be extracted from the line. Keywords are: # # --Generated(xxx) - xxx is a generated file # It will be placed in the OBJDIR # --GeneratedCommon(xxx) - xxx is a generated file # File will be placed in the local directory # and will be shared by by all platforms # --GeneratedObject(xxx) - xxx is a generated object file # It will be placed in the OBJDIR and will # have a suitable object suffix appended # --GeneratedProg(xxx) - xxx is a generated program file # It will be placed in the BINDIR # --Prerequisite(xxx) - xxx is a prerequisite file # The full name of the file will be located # and used within the command. The file will # be added to the list of recipe prerequisites # --GeneratedDirectory(xxx) # --GeneratedCommonDirectory(xxx) # --GeneratedObjectDirectory(xxx) # --GeneratedProgDirectory(xxx) # - xxx is a generated file, but is not placed # on the command line. It is flagged as # a generated files # --PackageBase(xxx) - xxx is a package. The keyword will be replaced # with the pathname to the package. If the package # has been copied in the the interface directory # then the interface directory will be used. # --PackageInfo(xxx,--opt)- xxx is a package. The keyword will be replaced # with the information requested. # Options are: # --path # --version # --fullversion # --project # # Where "xxx" may be of the form: # name,option[,option] # # Flag options are: # --file - The file part of the full name # --dir - The directory part of the full name # --abspath - Abs path # --absdrive - Abs path with drive letter # # --Var(Name,opt) - Name is the name of a recognised varable # Refer to ExpandGenVar function for details # of Name and available options # The expanded text will be replaced with an # suitable makefile variables that will be # replaced at run-time. # # The keyword will be replaced with the resolved name. This may be a file, # a directory or other text. # # Options do not alter command line text. They do affect the way the command is # processed. # Options include: # --Prereq=name - The name of a file to add as a prerequisite # The file does not form part of the command line # --Created=name - The name of a file to treat as a generated file # --CreatedCommon=name The file does not form part of the command line # --CreatedObject=name # --CreatedProg=name # # --NoVarTag - Modifes --Var operation to suppress tags # --NoWarn - Don't warn if no prerequistes found # --NoGenerate - Don't warn if no generated files are found # Will create a dummy rule name and the recipe will # always be executed during the 'GenerateFiles' phase # --UnknownPreq - Prerequisites are not fully known. # Rebuild the target whenever it is required. # --AutoGenerate - Examine the generated file to determine when the # tools is to be run. # Must be before any options that declare # creation of files. # --Text= - Display text for command # # --Clean[=arg] - Call script with arg[-clean] for cleaning. # --PreDelete - Delete generated files before running the command # # Eg: GenerateFiles ( '*', "--Tool=mod_if.pl", # "-src --Prerequisite(udh_module.cfg)", # "-direct -hdr --Generated(udp.h) -quiet" ); # my $NoGenIndex = 0; sub GenerateFiles { my ( $platforms, $tool, @args) = @_; return if ( ! ActivePlatform($platforms) ); Debug2( "GenerateFiles:($platforms, $tool, @args)" ); my @preq_files; my $preq_unknown; my @gen_files; my $shell_script; my $shell_cmds; my @tool_args; my $no_warn; my $clean_tag; my $text; my $gtype = 1; my @has_source; my @var_opts; my @genreq_seen; my $predelete; # # Process the first argument - this describes the program that will be used # to generate the files. It may be: # --Tool - A Jats Tool or Plugin # --Script - A shell script file # --Shell - Raw shell commands # --Prog - A program created within the Makefile # # if ( $tool =~ /^--Tool=(.*)/ || $tool =~ /^([^-].*)/) { $tool = $1; my $tool_no_prereq = 0; # # Process the Tool name and determine the location of the tool # Support --Tool=name and "name" # Locate the tool one of the many well known locations # 1) Tool paths from Package Archives # 2) JATS tool and binaries # 3) User PATH (!YUK) # # # Create a list of known extensions to scan # Basically present so that we can use .exe files without the .exe name # my @extension_list; push @extension_list, '.exe' if ( $::ScmHost ne "Unix" ); push @extension_list, '.pl', '.sh', '.ksh', ''; TOOL_SEARCH: { # # Locate tool with package # if ( my $fname = ToolExtensionProgram( $tool, @extension_list )) { $tool = $fname; last TOOL_SEARCH; } # # Search the JATS tools and Bin directory # Retain the symbolic name of the JATS directory # for my $ext ( @extension_list ) { foreach my $jdir ( qw( / /DEPLOY/ /LOCAL/ ) ) { if ( -f "$::GBE_TOOLS$jdir$tool$ext" ) { $tool = "\$(GBE_TOOLS)$jdir$tool$ext"; last TOOL_SEARCH; } } if ( -f "$::GBE_BIN/$tool$ext" ) { $tool = "\$(GBE_BIN)/$tool$ext"; last TOOL_SEARCH; } } # # Has the user provided an absolute PATH # This is not good, but for testing we can use it # if ( $tool =~ m~^/~ || $tool =~ m~^.:~ ) { Warning("Absolute path program specified. Uncontrolled tool: $tool"); for my $ext ( @extension_list ) { if ( -f "$tool$ext" ) { $tool = "$tool$ext"; last TOOL_SEARCH; } } } # # May have a relative path to a local tool # if ( -f $tool ) { UniquePush (\@preq_files, $tool); last TOOL_SEARCH; } # # Search the users PATH # Generate a warning if the program is found. These programs are # not nice as they are not really controlled. # for my $dir (split( $::ScmPathSep, $ENV{'PATH'} ) ) { for my $ext ( @extension_list ) { if ( -f "$dir/$tool$ext" ) { Warning("External program found in the user's PATH. Uncontrolled tool: $tool"); $tool = "$dir/$tool$ext"; # # Do not make the program a pre-requisite if we are running # under Windows. This avoids two problems: # 1) May have spaces in pathname # 2) May have driver letter in pathname # $tool_no_prereq = 1 if ( $::ScmHost eq "WIN" ); last TOOL_SEARCH; } } } # # Specified progrom not found # Generate a warning and use the raw name # Warning("Tool not found: $tool"); $tool_no_prereq = 1; } UniquePush (\@preq_files, $tool) unless ($tool_no_prereq); } elsif ( $tool =~ /^--Script=(.*)/ ) { # # Locate the script in a known source directory and make # the script a prerequisite of the target files, since the # script may be generated. # $tool = MakeSrcResolve ( $1 ); UniquePush (\@preq_files, $tool); } elsif ( $tool =~ /^--Shell$/ ) { # # The user has provided a shell script within the command body # This will be executed directly by a shell # directores will need to use a "/" separator # $tool = "InternalShell"; $shell_script = 1; $shell_cmds = 1; } elsif ( $tool =~ /^--Prog=(.*)$/ ) { # # Using a program that has been created within this script # my $prog = $1; if ( my $pProg = $PROGS->Get($prog) ) { $tool = $pProg->getPath() unless ( $tool = $SRCS{$prog} ); UniquePush (\@preq_files, $tool); } else { Error ("Unknown program: $prog"); } } else { # # Currently generate a warning and then use the raw tool name # Error ("Unknown TOOL syntax: $tool"); } # # May need to quote the path # If the toolpath contains spaces then ugliness can occur - so quote the program # $tool = '"' . $tool . '"' if ( (! $shell_script ) && $tool =~ m~\s~ ); # # Determine special startup for various programs # Perl - use known implemenatation # Shell - use known implemenatation # Otherwise - simply run it # # Windows: Shell and Perl don't need '\' in paths # if ( $tool =~ /\.pl$/ ) { $tool = "\$(GBE_PERL) $tool"; $shell_script = 1; } elsif ( $tool =~ /\.k?sh$/ ) { $tool = "\$(GBE_BIN)/sh $tool"; $shell_script = 1; } Debug( "GenerateFiles: Tool: $tool" ); # # Process the remaining arguments # These will be command line arguments or options/flags # Command line arguments are concatenated together # for my $arg (@args) { if ( $arg =~ /^--PreDelete$/ ) { # # Delete generated files before running the generation process # Some programs refuse to overwrite existing files # $predelete = 1; next; } if ( $arg =~ /^--NoVarTag$/ ) { # # Modify the operation of --Var to supress the tags # Should be usd early as will only affect following --Var usage # push @var_opts, "--notag"; next; } if ( $arg =~ /^--NoWarn$/ ) { # # Supress warnings - No prequisites found # This is acceptable, but normally a tool should take an input # and create some output from it. # $no_warn = 1; next; } if ( $arg =~ /^--NoGenerate$/ ) { # # Tool does generate a definable output # Should only be used internally # # Need to create a dummy name for the rule # Use a base name and a number # my $dummy_target = 'generate_files_' . $NoGenIndex; UniquePush (\@gen_files, $dummy_target ); UniquePush (\@GENERATED, $dummy_target); next; } if ( $arg =~ /^--UnknownPreq/ ) { # # Indicate that the prequisites are not known, or too complex to # describe. ie: All files in a directory. May be used by packaging # tools. # The recipe will be run EVERY time we want to use the target. # $preq_unknown = 1; $no_warn = 1; next; } if ( $arg =~ /^--AutoGenerate/ ) { # # Determine when to run the tool based on the types of files that # are generated. Existance of a source file will force the tool # to be run during the 'generate' phase, othewise the tool will be run # when the generated components are required. # $gtype = 2; Warning ("AutoGenerate MUST occur before options that declare generation of files", "Have seen:", @genreq_seen) if (@genreq_seen); next; } if ( $arg =~ /^--Prereq=(.*)/ ) { # # Specify a prerequisite file, that is not a part of the command line # Simply add the files to the list of preq files # my $fn = LocatePreReq ($1); UniquePush ( \@preq_files, $fn ); Debug( "GenerateFiles: ExtraPrereq: $fn" ); next; } if ( $arg =~ /^--Created(.*)=(.*)/ ) { # # Specify a generated file, that is not a part of the command line # Add the files to the list of generated files # my $type = $1; my $fn = $2; # # Append object suffix to CreatedObject # $fn .= '.' . $::o if ( $type =~ m/Object/ ); # # If the files is 'created' in a subdir, then add the dir # as a prerequisite. # if ( $type =~ m/Prog/ ) { $fn = "\$(BINDIR)/$fn"; UniquePush (\@preq_files, '$(GBE_BINDIR)'); } elsif ( $type !~ m/Common/ ) { $fn = "\$(OBJDIR)/$fn"; UniquePush (\@preq_files, '$(GBE_OBJDIR)'); } # # Examine the file and see if it needs to be compiled # Add to the list of source files # push @genreq_seen, $arg; if ( UniquePush (\@gen_files, $fn) ) { if ( GenerateSrcFile ( $gtype, $fn ) && $gtype == 2 ) { push @has_source, $fn; } } Debug( "GenerateFiles: ExtraCreated: $fn" ); next; } if ( $arg =~ /^--Clean($|=(.*))/ ) { # # Detect Clean option # $clean_tag = $2 ? $2 : '-clean'; # # Shell command with a --Clean will only # be run during a clean phase. They should not have any prereq # and should not generate any files, so simplify the interface. # push @args, '--NoWarn', '--NoGenerate' if ( $shell_cmds ); next; } if ( $arg =~ /^--Text=(.*)/ ) { # # Display this text when executing commands # $text = $1; next; } # Not an option. Must be an argument to the tool/program # Process the tool arguments and extract file information # Extract all fields of the form: # --xxxxx(yyyyyy[,zzzzz]) # --xxxxx{yyyyyyy} # --xxxxx[yyyyyyy] to allow embedded brackets # while ( $arg =~ m/--(\w+) # --CommandWord $1 ( # Just for grouping \((.*?)\) | # Stuff like (yyyyy) $3 {(.*?)} | # or like {yyyyy} $4 \[(.*?)\] # or like [yyyyy] $5 )/x ) # Allow comments and whitespace { my $cmd = $1; # The command my $ufn = $3 || $4 || $5; # User filename + options my $mb = $-[0]; # Match begin offset my $me = $+[0]; # Match end my $flags = ''; # Optional flags ( --dir or --file ) my $raw_arg = $ufn; # Raw arguments my $all = substr( $arg, $mb, $me ); # All of match. Avoid use of $& my $is_path = 1; Error ("GenerateFiles. Empty element not allowed: $all") unless ( defined($ufn) ); $ufn =~ s/\s+$//; $ufn =~ s/^\s+//; $ufn =~ s~//~/~g; # Remove multiple / if ( $ufn =~ m/(.*?),(.*)/ ) # Extract out any flags { $ufn = $1; $flags = $2; } my $fn = $ufn ; # Replacement filename my $fnp = ''; # Prefix to $fn Error ("GenerateFiles. Empty element not allowed: $all" ) if ( length ($ufn) <= 0 ); # # Process found user command # if ( $cmd =~ /^Generated/ ) { my $use_dir = ""; # # Generated filename # Determine the target directory # Determine the full name of the file. # Flag the file as generated # if ( $cmd =~ /Prog/ ) { # # Generated Prog are generated in the BIN directory # Ensure the directory exists by using its symbolic name # as a prerequisite. # $use_dir = '$(BINDIR)'; UniquePush (\@preq_files, '$(GBE_BINDIR)'); } elsif ( $cmd !~ /Common/ ) { # # Files that are not Common are generated in the # object directory. This directory must exist, so it # symbolic name GBE_OBJDIR is made a prerequisite too. # # If the file is a header file, then add the directory # to the include search path too. # $use_dir = '$(OBJDIR)'; UniquePush (\@preq_files, '$(GBE_OBJDIR)'); AddIncDir( $platforms , '$(OBJDIR)', '--NoWarn' ) if ( $ScmSourceTypes{ StripFile($fn) } && $ScmSourceTypes{ StripFile($fn) } eq ".h" ); } # # Append a toolset specfic object file name suffix # for Object files only # $fn .= ".$::o" if ( $cmd =~ /Object/ ); # # Merge directory and filename parts # $fn = $use_dir . ( $use_dir ? "/" : "" ) . $fn; # # Save for later user # Flag the file as a generated file # push @genreq_seen, $cmd; if ( UniquePush (\@gen_files, $fn) ) { if ( GenerateSrcFile ( $gtype, $fn ) && $gtype == 2 ) { push @has_source, $fn; } } # # Use the directory or the full name # If using the directory then ensure that we have a name # even if its "." # $fn = ($use_dir) ? "$use_dir" : "." if ( $cmd =~ /Directory/ ); Debug( "GenerateFiles: Generate: $fn" ); } elsif ( $cmd =~ /^Prereq/ ) { # # Prerequisite filename # Resolve the full name of the file. It may be known # as a source file (possibly generated) or it may be # located in a known source directory # $fn = LocatePreReq ($ufn); UniquePush (\@preq_files, $fn); Debug( "GenerateFiles: Prereq: $fn" ); } elsif ( $cmd =~ /^PackageBase/ ) { $fn = GetPackageBase( "GenerateFiles", $raw_arg ); UniquePush (\@preq_files, $fn); } elsif ( $cmd =~ /^PackageInfo/ ) { $fn = GetPackageInfo( "GenerateFiles", $raw_arg ); } elsif ( $cmd =~ /^Var/ ) { ($fnp, $fn, $is_path) = ExpandGenVar( "GenerateFiles", $raw_arg, @var_opts ); $flags = ''; if ( $raw_arg eq 'ObjDir' ) { UniquePush (\@preq_files, '$(GBE_OBJDIR)'); } elsif ( $raw_arg eq 'BinDir' ) { UniquePush (\@preq_files, '$(GBE_BINDIR)'); } elsif ( $raw_arg eq 'LibDir' ) { UniquePush (\@preq_files, '$(GBE_LIBDIR)'); } } else { Warning ("GenerateFiles: Unknown replacement command: $cmd"); $fn = $ufn; } # # Process path modification flags # $fn = ProcessPathName( $fn, $flags ); # # Minor kludge under windows. Ensure directores have a "\" sep # Unless the user has specified a straight shell command # $fn = "\$(subst /,\\,$fn)" if ( $is_path && $::ScmHost eq "WIN" && ! defined($shell_script) ); # # Prepend any $fn Prefix # This will be a tag and is not subject to path processing # $fn = $fnp . $fn; # # Replace the found string with the real name of the file # Note: 4 argument version of substr is not always available # so we must do it the hard way # substr( $arg, $mb, $me - $mb, $fn); # $arg = substr( $arg, 0, $mb ) . $fn . substr( $arg, $me ); Debug2( "GenerateFiles: subs: $all -> $fn" ); } # # Save the tool arguments in an array # push @tool_args, $arg; } # # Sanity test. Ensure that some file have been marged as generated # Warn if no prerequisites found # Warning( "GenerateFiles. --AutoGenerate option has no effect", "The following files are 'source' files", @has_source ) if ( @has_source ); Warning( "No Prerequisite files found in $tool",@tool_args) unless ( $no_warn || $#preq_files >= 0 ); Error ( "No generated files found in $tool",@tool_args) unless ($#gen_files >= 0); # # Save information # Will be used to create makefile statements later # my %gen_data; $gen_data{'index'} = $NoGenIndex++; $gen_data{'shell'} = $shell_cmds; $gen_data{'gen'} = \@gen_files; $gen_data{'preq'} = \@preq_files; $gen_data{'tool'} = $tool; $gen_data{'toolargs'} = \@tool_args; $gen_data{'clean'} = $clean_tag; $gen_data{'text'} = $text || $gen_files[0]; $gen_data{'preq_sus'} = 1 if ( $preq_unknown ); $gen_data{'predelete'} = 1 if ( $predelete ); push(@GENERATE_FILES, \%gen_data); Debug2( "GenerateFiles: cmd: $tool @tool_args" ); } #------------------------------------------------------------------------------- # Function : MakePerlModule # # Description : Build Perl Module(s) using the Perl Build System # This is a thin wrapper around a specialised script # # The user can do the same job with correct use of # a GenerateFiles, but this is a lot tidier. # # Inputs : $1 - platform specifier '*' (comma delemitered) # $* - Paths to Perl Modules[,command options] # Options to the BuildPerl script # # Returns : # sub MakePerlModule { my ( $platforms, @args) = @_; return if ( ! ActivePlatform($platforms) ); Debug2( "MakePerlModule:($platforms, @args)" ); my @opts; # # Extract options from paths to Perl Packages # Package names do not start with a '-' # foreach my $arg ( @args ) { if ( $arg =~ /^-/ ) { push @opts, $arg; } else { # # Perl Package Directory Name # This may also contain embedded command to the Make command # These will be seperated with a comma # ie: module,-options=fred # my ($name,$options) = split( ',', $arg ); push @opts, "-PerlPackage=$arg"; push @opts, "--Prereq=$name/Makefile.PL"; } } # # Invoke GenerateFiles with a bunch of additional arguments # GenerateFiles ($platforms, "--Tool=jats_buildperl.pl", '--Var(MachType)', # Build Machine type '--Var(PackageDir)', # Package dir '--NoGenerate', # Don't know the output '--Text=Make Perl Module', # Pretty print '--NoWarn', '--Clean=-clean_build', # Jats clean support '--NoVarTag', # No more Tags @opts, ); } #------------------------------------------------------------------------------- # Function : MakeLinuxDriver # # Description : Build a Linux Device Driver using the Linux Device Driver # Build System # This is a thin wrapper around a specialised script # # The user can do the same job with correct use of # a GenerateFiles, but this is a lot tidier. # # Inputs : $1 - platform specifier '*' (comma delemitered) # $2 - name of the driver. No extension # $* - Driver sources # Options to the script # # Returns : # sub MakeLinuxDriver { my ( $platforms, $driver_name, @args) = @_; return if ( ! ActivePlatform($platforms) ); Error ("No driver name specified") unless ( $driver_name ); Debug2( "MakeLinuxDriver:($platforms, $driver_name ,@args)" ); my @srcs; my @opts; # # Extract options from source files # Package names do not start with a '-' # foreach my $arg ( @args ) { if ( $arg =~ /^--Define=(.)/ ) { push @opts, $arg; } elsif ( $arg =~ /^-/ ) { push @opts, $arg; Warning ("MakeLinuxDriver: Unknown option: $arg. Passed to script"); } else { push @srcs, $arg; push @opts, "--Prereq=$arg"; } } # # Cleanup the drive name # $driver_name =~ s~\.ko$~~; # # Remove the specified sources from the list of object files # that will be build. This will ensure that some internal rules are # not generated. # foreach ( @srcs ) { my $file = StripExt(StripDir( $_ )); delete $OBJSOURCE{ $file }; @OBJS = grep(!/^$file$/, @OBJS); } # # Invoke GenerateFiles with a bunch of additional arguments # At runtime the include directories will be added as # absolute paths # GenerateFiles ($platforms, "--Tool=jats_buildlinux.pl", "-Output=--GeneratedProg($driver_name.ko)", "-Driver=$driver_name", "-GccPath=\$(GCC_CC)", "-Arch=\$(HOST_CPU)", "-LeaveTmp=\$(LEAVETMP)", "-Verbose=\$(CC_PRE)", "-Type=\$(GBE_TYPE)", "-Platform=\$(GBE_PLATFORM)", '$(patsubst %,-Incdir=%,$(INCDIRS))', @opts, @srcs ); } #------------------------------------------------------------------------------- # Function : GetPackageBase # # Description : Helper routine # Given a package name, determine the base address of the # package # # Inputs : $dname - Directive name (Reporting) # $name - Required package # Allows two forms: # package_name # package_name,ext # # Returns : Path to the directory in which the files are installed # This may be the interface directory # sub GetPackageBase { my ($dname, $fname) = @_; my $pkg; my ($name, $ext) = split(',', $fname); $pkg = GetPackageEntry( $name, $ext ); Error ("$dname: Package not found: $fname") unless ( $pkg ); # # If a BuildPkgArchive then use the interface directory # return ( $pkg->{'TYPE'} eq 'link' ) ? $pkg->{'ROOT'} : '$(INTERFACEDIR)'; } #------------------------------------------------------------------------------- # Function : GetPackageInfo # # Description : Helper routine # Given a package name, return some information about the package # Only one information item is allowed with each call # # Inputs : $dname - Directive name (Reporting) # $name - Required package # Allows two forms: # package_name # package_name,ext # Selector # --path # --version # --fullversion # --project # # Returns : Package information my %GetPackageInfo = qw(path ROOT version DVERSION fullversion VERSION project DPROJ); sub GetPackageInfo { my ($dname, $args) = @_; my $pkg; my $name; my $ext; my $info; # # Split up the arguments # Options start with '--' # First non-option is the package name # 2nd non-option is the packag extension # # Only one option allowed # Convert it into a known package info item # # foreach ( split(',', $args) ) { if ( m/^--(.*)/ ) { Error( "$dname: Too many info requests: $args") if ( $info ); $info = $GetPackageInfo{$1}; Error( "$dname: Unknown info type: $_") unless ($info); } elsif ( $ext ) { Error("$dname: Too many names: $args"); } elsif ( $name ) { $ext = $_; } else { $name = $_; } } $pkg = GetPackageEntry( $name, $ext ); Error ("$dname: Package not found: $args") unless ( $pkg ); # # If a BuildPkgArchive then use the interface directory # Default data item - path to the package # $info = 'ROOT' unless ( $info ); if ( $info eq 'ROOT' && $pkg->{'TYPE'} ne 'link' ) { return ( '$(INTERFACEDIR)'); } return ( $pkg->{$info} ); } #------------------------------------------------------------------------------- # Function : GetPackageEntry # # Description : Return the package class pointer given a package name # # Inputs : $name - Required package # $ext - Option package extension # # Returns : Class pointer # sub GetPackageEntry { my ($name, $ext) = @_; $ext = '' unless ( $ext ); for my $entry (@{$::ScmBuildPkgRules{$ScmPlatform} }) { next unless ( $entry->{'NAME'} eq $name ); next if ( $ext && $entry->{'DPROJ'} ne $ext ); return $entry; } return; } #------------------------------------------------------------------------------- # Function : ExpandGenVar # # Description : Expand a known variable for the Generate Files option # # Inputs : $dname - Directive name (Reporting) # $arg - Raw argument # This of the form of # Tag[,--option]+ # Tags are specified in %ExpandGenVarConvert # # Options are: # --tag # --notag # --tag= # --absdrive # --abspath # --default=text # --allownone # Not all options are avalaible on all variables # @opts - Options # --notag - Default is --notag # # Returns : Tag - Any tag component of the expansion # Path/Value - Path/Value of the component # is_path - Above is a path # is_abs - Path is absolute # # # Create a Hash to simplify the process of converting Var names # into makefile variables. There are two data items, separated by a comma. # The first is the runtime expansion value # The second describes the first: # NotPresent - Expansion is not a path # '-' - Expansion is a path and is relative to CWD # '+' - Expansion is a path and is absolute # my %ExpandGenVarConvert = ( 'BuildName' => '$(GBE_PBASE)', 'BuildVersion' => '$(BUILDVER)', 'BuildVersionNum' => '$(BUILDVERNUM)', 'PackageDir' => '$(PKGDIR),+', 'PackagePkgDir' => '$(PKGDIR)/pkg/pkg.$(GBE_PLATFORM),+', 'PackageIncDir' => '$(INCDIR_PKG),+', 'PackageLibDir' => '$(LIBDIR_PKG)/$(GBE_PLATFORM),+', 'PackageBinDir' => '$(BINDIR_PKG)/$(GBE_PLATFORM)$(GBE_TYPE),+', 'PackageToolDir' => '$(PKGDIR)/tools,+', 'PackageToolBin' => '$(PKGDIR)/tools/bin/$(GBE_HOSTMACH),+', 'PackageToolScript' => '$(PKGDIR)/tools/scripts,+', 'LibDir' => '$(LIBDIR),+', 'BinDir' => '$(BINDIR),+', 'ObjDir' => '$(OBJDIR),+', 'InterfaceDir' => '$(INTERFACEDIR),+', 'InterfaceIncDir' => '$(INCDIR_INTERFACE),+', 'InterfaceLibDir' => '$(LIBDIR_INTERFACE)/$(GBE_PLATFORM),+', 'InterfaceBinDir' => '$(BINDIR_INTERFACE)/$(GBE_PLATFORM)$(GBE_TYPE),+', 'LocalDir' => '$(LOCALDIR),+', 'LocalIncDir' => '$(INCDIR_LOCAL),+', 'LocalLibDir' => '$(LIBDIR_LOCAL)/$(GBE_PLATFORM),+', 'LocalBinDir' => '$(BINDIR_LOCAL)/$(GBE_PLATFORM)$(GBE_TYPE),+', 'Platform' => '$(GBE_PLATFORM)', 'Product' => '$(GBE_PRODUCT)', 'Target' => '$(GBE_TARGET)', 'Type' => '$(GBE_TYPE)', 'Arch' => '$(HOST_CPU)', 'Architecture' => '$(HOST_CPU)', 'MachType' => '$(GBE_HOSTMACH)', 'BuildRoot' => '$(GBE_ROOT),+', 'Verbose' => '$(CC_PRE)', 'LeaveTmp' => '$(LEAVETMP)', 'Cwd' => '$(CURDIR),-', 'CompilerPath' => '$(SCM_COMPILERPATH)', 'PkgArch' => '$(PACKAGE_ARCH)', ); sub ExpandGenVar { my ($dname, $args, @uopts) = @_; my $expansion; my $prefix=''; my ($tag, @opts) = split('\s*,\s*', $args); my $no_prefix; my $default_value; my $allow_none; my $is_abs = 0; # # Parse options lists # Options provided by the caller # Options embedded in the argument foreach ( @uopts ) { if ( m/^--notag$/ ) { $no_prefix = 1; } else{ Error ("$dname: Unknown option: $_") } } foreach ( @opts ) { if ( m/^--default=(.+)/i ) { $default_value = $1; } elsif ( m/^--allownone$/i ) { $allow_none = 1; } } # # Perform run-time update on the %ExpandGenVarConvert # Most of it can be initialised at startup - but not all of it. # $ExpandGenVarConvert{CompilerPath} = undef unless $::ScmToolsetCompilerPath; $ExpandGenVarConvert{Product} = '$(GBE_PLATFORM)' unless $ScmProduct; # # Look up a hash of conversions # Could allow for a code ref, but not needed yet # Error ("$dname: Unknown expansion --Var($tag)") unless ( exists $ExpandGenVarConvert{$tag} ); # # Handle undefined expansions # Only 'CompilerPath', but it can be a pain in user land # $expansion = $ExpandGenVarConvert{$tag}; unless ( defined $expansion ) { return '' if ( $allow_none ); $expansion = $default_value; Error ("$dname: Expansion --Var($tag) not be supported by toolset: $ScmToolset") unless ( $expansion ); } ($expansion,my $is_path) = split (',', $expansion ); $is_abs = 1 if ($is_path && $is_path eq '-' ); # # Process options # Assume that a tag will be provided # $prefix = $no_prefix ? '' : "-$tag="; foreach my $opt ( @opts ) { if ( $opt =~ /^--tag=(.*)/i ) { $prefix = "$1="; } elsif ( $opt =~ m/^--tag$/i ) { $prefix = "-$tag="; } elsif ( $opt =~ m/^--notag/i ) { $prefix = ''; } elsif ( $is_path && !$is_abs && $opt =~ /--abspath|--absdrive/i ) { $expansion = '$(CURDIR)/' . $expansion; $is_abs = 1; } elsif ( $opt =~ m/^--default=(.+)/i ) { # Already processed } elsif ( $opt =~ m/^--allownone$/i ) { # Already processed } else { Error ("$dname: Unsupported option($opt) for --Var(@_)"); } } Debug ("ExpandGenVar: args $args --> $prefix$expansion"); return $prefix , $expansion, $is_path ? 1 : 0, $is_abs; } #------------------------------------------------------------------------------- # Function : ProcessPathName # # Description : Massage a pathname according to a set of flags # # Inputs : $fn - Patchname to massage # $flags - Flags in a string # --dir - only the directory part ( or a "." ) # --file - only the file part # --abspath - Absolute path # --absdrive - Absolute path with drive letter(WIN) # # Returns : Massaged pathname # sub ProcessPathName { my ( $fn, $flags ) = @_; # # Process flags # --dir - only the directory part ( or a "." ) # --file - only the file part # --abspath - Absolute path # --absdrive - Absolute path with drive letter(WIN) # if ( $flags =~ /--dir/ ) { $fn = '.' unless ( $fn =~ s~/[^/]*$~~); } if ( $flags =~ /--file/ ) { $fn =~ s~.*/~~; } if ( $flags =~ /--abspath/ ) { $fn = AbsPath( $fn ); } elsif ( $flags =~ /--absdrive/ ) { $fn = AbsPath( $fn ); if ( $::ScmHost eq "WIN" ) { $fn = $::CwdDrive . '/' . $fn unless ( $fn =~ m~^\w:/~ ); $fn =~ s~//~/~g; } } return $fn; } #------------------------------------------------------------------------------- # Function : LocatePreReq # # Description : Locate a file known to JATS # There are many places to search # 1) Src files - specified with a Src directive # 2) Scripts - specified with a script directive # 3) Search - Files in the specified search path # 4) Programs specified with a 'Prog' directive # # Should also look in other locations (Libs, SharedLibs) # Not done yet. May be issues of a name clash if a program # and a library have the same name. # # Inputs : Name to locate # # Returns : Full pathname of file # sub LocatePreReq { my ( $name ) = @_; Debug ("LocatePreReq:Looking for $name"); # # Try a Src file first # if ( exists $SRCS{ $name } ) { return $SRCS{ $name }; } # # Try a script # if ( exists $SCRIPTS{ $name } ) { return $SCRIPTS{ $name }; } # # Try a PROG # if ( my $pProg = $PROGS->Get($name) ) { return $pProg->getPath(); } # # Try searching for the file # Uses Data from AddSrcDir # # Done: last because it generates warning messages # return MakeSrcResolve( $name ); } #------------------------------------------------------------------------------- # Function : ToolExtensionPaths # # Description : Return a list of toolset extension directories # The data will have been discovered by the build process # and will have been saved for the makefile creation phase # # Inputs : None # # Returns : Return an ordered unique list # sub ToolExtensionPaths { Debug( "ToolExtensionPaths:", @::BUILDTOOLSPATH ); return @::BUILDTOOLSPATH; } #------------------------------------------------------------------------------- # Function : ToolExtensionProgram # # Description : Determine if the named program exists within the PATH # that also includes the toolset extension # # Inputs : program - Name of program # elist - An array of possible program extensions # # Returns : Full path the to program or an empty elelent (not undef) # sub ToolExtensionProgram { my ($program, @elist ) = @_; # # If elist is empty then insert a defined entry # push @elist, '' unless ( @elist ); # # Scan all toolset directories # for the program # for my $dir ( ToolExtensionPaths() ) { for my $ext ( @elist ) { my $tool = "$dir/$program$ext"; Debug( "ToolsetExtensionProgram: Look for: $tool" ); return $tool if ( -f $tool ); } } } sub Define { Debug2( "Define(@_)" ); push( @DEFINES, @_ ); } sub Defines { my( $path, $script ) = @_; my( $line ); Debug2( "Defines($path, $script)" ); $script = Exists( $path, $script, "Defines" ); push( @DEFINES, "# Defines from: $script" ); open( my $fh, '<', $script ) || Error( "Opening $script" ); while (<$fh>) { $_ =~ s/\s*(\n|$)//; # kill trailing whitespace & nl push( @DEFINES, $_ ); } push( @ScmDepends, "$script" ); # makefile dependencies close( $fh ); } #------------------------------------------------------------------------------- # Function : Rule # # Description : Add a Rule and Recipe to the generated makefile # This is not encouraged as it has been misused to create # unreadable and unmaintainable makefiles. # # Rules will be added to the makefile after the rules and # recipes created by JATS directives # # Inputs : $platform - Platform predicate # @rule - Array of rules to add # # Returns : # sub Rule { my( $platforms, @rule ) = @_; return if ( ! ActivePlatform($platforms) ); push( @RULES, @rule ); Message("Rule directive used. Consider replacing with GenerateFiles"); } #------------------------------------------------------------------------------- # Function : Rules # # Description : Add a file of Rules and Recipes to the generated makefile # Used internally ONLY as there is no platform predicate # Similar to 'Rule()' # # Inputs : $path - path to script # $script - File fo Rules # # Returns : # sub Rules { my( $path, $script ) = @_; my( $line ); $script = Exists( $path, $script, "Rules" ); push( @RULES, "# Rules from: $script" ); open( my $fh, '<', $script ) || Error( "Opening $script" ); while (<$fh>) { $_ =~ s/\s*(\n|$)//; # kill trailing whitespace & nl push( @RULES, $_ ); } push( @ScmDepends, "$script" ); # makefile dependencies close( $fh ); } #------------------------------------------------------------------------------- # Function : AddRule # # Description : Inernal function # Add a line to the Rules area # # Inputs : @elements - Array of lines to add # # Returns : Nothing # sub AddRule { push( @RULES, @_ ); } #------------------------------------------------------------------------------- # Function : Src # # Description : This directive is used to identify files to JATS # Once a file has been identified as a 'Source' file, then it # can be used by name, without the need to locate the file again. # This implies that filenames must be unique. # The directories cannot be used to make files of the same name # unqiue - this is not the JATS way # # Source files will be classified as one of: # c, c++, header, assembler or other # # # Inputs : $platform - Active Platform Predicate # @elements - A list of files and options # # Valid options are: # --c - Specifies the type of file # --cpp # --h, --headers # --asm # --FromPackage - Search packages for the file # --List=xxx - Append file to a named list # --Depends=xxx - Manually name a dependency # # Options are processed before file elements # Thus options apply to all files in the list # # Returns : Nothing # sub Src { my( $platforms, @elements ) = @_; my( $type, @args, $source, $basename, $from_package, @lists ); my( @depends, @srcs ); $platforms = '' unless ( $platforms ); Debug2( "Src($platforms, @elements)" ); # # Ensure that there is a file within the list # Warning( "Src directive does not specify any files: Src($platforms, @elements)" ) unless (grep( /^[^-]/, @elements ) ); return if ( ! ActivePlatform($platforms) ); # # Remove spaces from both ends of the arguments. # It is easier to remove spaces now than to detect them later # foreach ( @elements ) { s/^\s+//; s/\s+$//; s~//~/~g; # Remove multiple / } #.. Collect arguments # $type = ""; foreach ( @elements ) { if ( /^--c$/ ) { Debug( "Src: --c" ); $type = ".c"; } elsif ( /^--cpp$/ ) { Debug( "Src: --cpp" ); $type = ".cc"; } elsif ( /^--h$/ || /^--header$/ ) { Debug( "Src: --h" ); $type = ".h"; } elsif ( /^--asm$/ ) { Debug( "Src: --asm" ); $type = ".asm"; } elsif ( /^--FromPackage$/ ) { $from_package = 1; } elsif ( /^--List=(.*)/ ) { my $list_name = $1; Error( "Bad list name: $list_name" ) unless ( $list_name =~ m/^[A-Za-z]\w+/ ); push @lists, $list_name; } elsif ( /^--Depends=(.*)/ ) { foreach ( split( ',', $1) ) { my $full = MakeSrcResolveExtended( $from_package, $_ ); push @depends, $full; } } elsif ( /^-(.*)/ ) { Debug( "Src: arg $_" ); push @args, $_; } else { push @srcs, $_; Warning ("Src files contains a '\\' character: $_" ) if (m~\\~); } } #.. Push source file(s) foreach ( @srcs ) { if ( ! /^-(.*)/ ) { $source = MakeSrcResolveExtended( $from_package, $_ ); $basename = StripDir( $source ); Debug( "Src: $_ -> $source=$basename (@args),(@depends)" ); if ( $SRCS{ $basename } ) { Warning( "Duplicate src ignored '$source'"); next; } $SRCS{ $basename } = $source; HashJoin( \%SRC_ARGS, $;, $basename, @args ) if (@args); HashJoin( \%SRC_DEPEND, $;, $basename, @depends ) if ( @depends ); $SRC_TYPE{ $basename } = $type if ($type); foreach (@lists) { my $lname_short = "LIST_$_"; my $lname_full = "LIST_FULL_$_"; no strict 'refs'; push @$lname_short,$basename; push @$lname_full ,$source; use strict 'refs'; } __AddSourceFile( 1, $source, "", $type ); } } } ############################################################################### # sub LibNameSplit # Just a little help to deal with major/minor stuff for shared libs - # given the name of the library as the argument, split out major and minor # parts and return the basename, i.e name without major and minor and # the pair of major and minor. ############################################################################### sub LibNameSplit { my ( @bits ) = split('\.', $_[0]); my ( $major, $minor ); if ($#bits >= 1) { $major = $bits[0]; $minor = $bits[1]; } elsif ($#bits >= 0) { $major = $bits[0]; $minor = 0; } else { $major = 1; $minor = 0; } Debug( "LibName: $@_[0] ($major.$minor)" ); return ($major, $minor); } #------------------------------------------------------------------------------- # Function : Lib # # Description : Generate a static library # # Inputs : Platform specifier # Name of the library # Arguemnts ... # # Returns : # sub Lib { my( $platforms, $lib, @args ) = @_; return if ( ! ActivePlatform($platforms) ); Error ("Lib: Library name not defined") unless ( $lib ); # # May be a shared library or a static library - for historic reasons # If the user has specified a --Shared then its a shared library # return SharedLib( @_ ) if ( grep (/^--Shared/, @args) ); # # Does this toolset support libraries # Error ("Libraries are not supported") unless ( defined $::a ); #.. Fully qualify library path for addition to library list. $lib = "lib$lib" if ( $ScmTargetHost eq "Unix" && $lib !~ m/^lib/); Debug( "Lib: $lib" ); # # Create a new object to describe the library # Ensure that only one such lib exists # Add the library to the list of static libraries # Error( "Library of the same name already defined: $lib" ) if ( $LIBS->Get($lib) ); $LIBS->NewAdd($lib); # # Process arguments # push( @LINTLIBS, $lib ); _LibArgs( $lib, @args ); } #------------------------------------------------------------------------------- # Function : SharedLib # # Description : Generate a shared library # # Inputs : Platform specifier # Name of the library # Arguemnts ... # # Returns : # sub SharedLib { my( $platforms, $lib, @args ) = @_; return if ( ! ActivePlatform($platforms) ); Error ("SharedLib: Library name not defined") unless ( $lib ); Error ("Shared Libraries are not supported") unless ( defined $::so ); #.. Fully qualify library path for addition to library list. $lib = "lib$lib" if ( $ScmTargetHost eq "Unix" && $lib !~ m/^lib/); Debug( "ShLib: $lib" ); # # Ensure that only one such lib exists # Error( "Library of the same name already defined: $lib" ) if ( $SHLIBS->Get($lib) ); $SHLIBS->NewAdd($lib); # # If the user has not specified a --Shared parameter then provide one # push @args, "--Shared=Current" unless ( grep (/^--Shared/, @args) ); # # Process arguments # push( @LINTSHLIBS, $lib ); _SharedLibArgs( $lib, @args ); } #------------------------------------------------------------------------------- # Function : LibArgs # # Description : Add arguments to an existing library directive # # Inputs : Platform specifier # Name of the library # Arguemnts ... # # Returns : # sub LibArgs { my( $platforms, $lib, @args ) = @_; return if ( ! ActivePlatform($platforms) ); #.. Fully qualify library path for addition to library list. $lib = "lib$lib" if ( $ScmTargetHost eq "Unix" && $lib !~ m/^lib/); Debug( "LibArgs: $lib" ); # # Process the arguments # _LibArgs( $lib, @args ); } #------------------------------------------------------------------------------- # Function : _LibArgs # # Description : Process static library arguments # Internal use only # # Inputs : Name of the library # Arguments to process # sub _LibArgs { my( $lib, @elements) = @_; my $obj; # # Ensure that only one such lib exists # my $libp = $LIBS->Get($lib); Error("Library name not defined: $lib") unless ( $libp ); # # Process each element # foreach (@elements) { if ( /^\s+/ ) { Error ("Argument cannot start with a space: '$_'"); } if ( /^--Shared/ ) { Error( "--Shared not valid for a static library" ); } if ( /^-l(.*)/ || /^--l(.*)/ || /^-L(.*)/ || /^--L(.*)/ ) { #.. Target library specified - add to library list. # Warning( "$_ within non shared library specification" ); next; } if ( /^--if(.*)/ ) { Warning( "$_ within non shared library specification" ); next; } if ( /^--(.*)/ ) { Debug( "LibArgs: arg $_" ); #.. Argument specified - add to argument list # $libp->addItem('ARGS', $_); next; } if ( %::ScmToolsetProgSource ) { # # Toolset provides support for some file types # to be passed directly to the librarian builder # my $ext = StripFile($_); if ( exists ($::ScmToolsetProgSource{$ext}) ) { my $full_path = MakeSrcResolve ( $_ ); my $flag = $::ScmToolsetProgSource{$ext}; Debug( "LibArgs: src $_" ); $libp->addItem('ARGS', "$flag$full_path" ); next; } } if ( $::o ) { #.. Object specified - add to object list. # $obj = _LibObject( "", $_ ); #.. Add to object list. # Note: Object path must be explicit as several # toolsets add additional objects. # $libp->addItem('OBJS', "\$(OBJDIR)/$obj" ); next; } # # Don't know how to handle this type of argument # Error ("LibArgs: Don't know how to handle: $_" ); } } #------------------------------------------------------------------------------- # Function : SharedLibArgs # # Description : Add arguments to an existing shared library directive # # Inputs : Platform specifier # Name of the library # Arguemnts ... # # Returns : # sub SharedLibArgs { my( $platforms, $lib, @args ) = @_; return if ( ! ActivePlatform($platforms) ); #.. Fully qualify library path for addition to library list. $lib = "lib$lib" if ( $ScmTargetHost eq "Unix" && $lib !~ m/^lib/); Debug( "ShLibArgs: $lib" ); _SharedLibArgs( $lib, @args ); } #------------------------------------------------------------------------------- # Function : _SharedLibArgs # # Description : Process shared library arguments # Internal use only # # Inputs : Name of the library # Arguments to process # sub _SharedLibArgs { my ( $lib, @elements) = @_; my $libp = $SHLIBS->Get($lib); Error("Library name not defined: $lib") unless ( $libp ); # #.. Collect --Shared arguments # Need to process this one first so that we have a version number # foreach (@elements) { if ( /^\s+/ ) { Error ("Argument cannot start with a space: '$_'"); } next unless ( /^--Shared/ ); my $shared; if ( /^--Shared$/ ) { #.. Shared library, default library version 1.0 # $shared = "1.0"; } elsif ( /^--Shared=Current$/ ) { #.. Shared library, using 'current' build version # $shared = $::ScmBuildVersion; $shared = "1.0" if ($shared eq ""); } elsif ( /^--Shared=(.*)/ ) { #.. Shared library, specific version # my($M, $m) = LibNameSplit($1); $shared = "$M.$m"; } # # Update the shared Names # if ( defined $shared ) { Warning( "multiple --Shared arguments" ) if (exists $libp->{ VERSION }); Debug( "ShLibArgs: shared $_ ($shared)" ); $libp->{ VERSION } = $shared; } else { Error ("ShLibArgs: --Shared argument not understood"); } } #.. Parse all of the object and argument entries. # foreach (@elements) { next if ( /^--Shared(.*)/ ); if ( /^[-]{1,2}([lL])(.*)/ ) { #.. Target library specified - add to library list. # Support --L and -L and --l and -l # Debug( "ShLibArgs: lib -$1$2" ); $libp->addItem('LIBS', "-$1$2" ); next; } if ( /^--if(.*)/ ) { #.. Library conditional - add to library list. # Debug( "ShLibArgs: cond $_" ); $libp->addItem('LIBS', $_); next; } if ( /^--SoName=(.*)/i ) { #.. Specify the SoName of the library # Not supported by all toolsets # my $soMode = $1; if ( !$ScmToolsetSoName ) { Warning ("Toolset does not support --SoName. Option ignored"); next; } Error ("SharedLib: $lib. Multiple --SoName arguments not allowed") if ( $libp->{ SONAME } ); my ($major, $minor, $patch, $build, $raw_patch) = SplitVersion($::ScmBuildVersionFull); my $soname = '.'; if ( $soMode =~ m/Major/i ) { $soname .= $major; } elsif ( $soMode =~ m/^Minor/i ) { $soname .= "$major.$minor"; } elsif ( $soMode =~ m/^Patch/i ) { $soname .= "$major.$minor.$patch"; } elsif ( $soMode =~ m/^Build/i ) { $soname .= "$major.$minor.$patch.$build"; } elsif ( $soMode =~ m/^Full/i ) { $soname .= $libp->{ VERSION }; } elsif ( $soMode =~ m/^None/i ) { $soname = ''; } elsif ( $soMode =~ m/^[0-9.]+$/ ) { $soname .= $soMode; } else { Error ("Unknown --SoName mode: $soMode"); } $libp->addItem('ARGS', '--SoNameSuffix=' . $soname); $libp->{ SONAME } = 1; next; } if ( /^-(.*)/ ) { #.. Argument specified - add to argument list # Debug( "ShLibArgs: arg $_" ); $libp->addItem('ARGS', $_); next; } if ( %::ScmToolsetProgSource ) { # # Toolset provides support for some file types # to be passed directly to the program builder # my $ext = StripFile($_); if ( exists ($::ScmToolsetProgSource{$ext}) ) { my $full_path = MakeSrcResolve ( $_ ); my $flag = $::ScmToolsetProgSource{$ext}; Debug( "ShLibArgs: src $_" ); $libp->addItem('ARGS', "$flag$full_path"); next; } } if ( $::o ) { #.. Object specified - add to object list. # my ($obj) = _LibObject( $lib, $_ ); #.. Add to object list. # Note: Object path must be explicit as several # toolsets add additional objects. # $SHOBJ_LIB{ $obj } = $lib; $libp->addItem('OBJS', "\$(OBJDIR)/$obj"); next; } # # Don't know how to handle this type of argument # Error ("SharedLib: Don't know how to handle: $_" ); } } #------------------------------------------------------------------------------- # Function : _LibObject # # Description : Process library object file # Common processing routine for static and shared library # Internal use only # # Inputs : shared - Name of the shared library is shared, if defined # fname - Name of file # # Returns : Name of the object file # sub _LibObject { my ($shared, $fname) = @_; my ($file, $ext, $obj, $srcfile, $delete_obj); #.. Object specified - add to object list. # # Want to handle several cases # Normal - User has provided the name of an object file (without the obj suffix) # Other - User has provided the name of a source file # Need to perform implicit source file processing # # The hard part is detecting the difference # Just can't use the existence of a '.' # if ($OBJSOURCE{$fname}) { $file = $fname; # Already know about this file $ext = ''; # Don't need to split it } else { $file = StripDirExt($fname); # file name, without extension or Dir $ext = StripFile($fname); # extension } if ($shared) { $obj = "$shared/$file"; # library specific subdir } else { $obj = "$file"; } Debug( "LibObjs: obj [$shared]$fname ($file$ext)" ); #.. Unqualified object name # if ( $ext eq '' ) { # # Object file not covered by a "Src" statement # Assume that it will be created # unless ( $srcfile = $OBJSOURCE{$file} ) { # # If the object is "generated" then it will be in the # SRCS list # unless ( $srcfile = $SRCS{"$file.$::o"} ) { Warning( "No source for object '$fname' ($file)" ); } } $delete_obj = 1; } #.. Qualified object name (ie has extension) # Strip extension and resolve ... # Assume that the named file can be built into an object file # else { #.. Resolve # if ( !($srcfile = $OBJSOURCE{ "$file" }) ) { $srcfile = MakeSrcResolve( $fname ); $SRCS{ $fname } = $srcfile; __AddSourceFile( 0, $fname, $obj ); $delete_obj = 1; } } #.. Delete generated object file # Ensure that the object file is added to the delete list # Add it to the ToolsetObj deletion list as the main OBJ deleltion # list will aready have been processed # ToolsetObj( "\$(OBJDIR)/$obj" ) if ( $delete_obj ); #.. Shared library objects, # Must explicitly relate source and object, as shared libraries # objects are built within a library specific subdirs. # $OBJSOURCE{ $obj } = $srcfile if ( $shared && defined $srcfile ); return $obj; } # MergeLibrary # Merge a list of libraries into one library # sub MergeLibrary { my( $platforms, $lib, @elements ) = @_; return if ( ! ActivePlatform($platforms) ); #.. Fully qualify library path for addition to library list. $lib = "lib$lib" if ( $ScmTargetHost eq "Unix" && $lib !~ m/^lib/); Debug( "MergeLibrary: $lib" ); # # Create a new object to describe the library # Ensure that only one such lib exists # Add the library to the list of static libraries # Error( "Merged Library of the same name already defined: $lib" ) if ( $MLIBS->Get($lib) ); my $libp = $MLIBS->NewAdd($lib); #.. Parse all of the object and argument entries. # foreach (@elements) { if ( /^--(.*)/ ) { $libp->addItem('ARGS', $_); } else { my ($llib); # # Collect the source libraries # These must have been installed and will be in a known area # Create full names for the libaries # if ( $ScmTargetHost eq "Unix" ) { $llib = "lib$_"; # Prefix "lib" .... $lib =~ s/^liblib/lib/; # @LIBS already has lib added } else { $llib = $_; } Debug( "MergeLibrary: merge $llib" ); $libp->addItem('LIBS', $llib); } } } #------------------------------------------------------------------------------- # Function : Script # # Description : Locate a script for test purposes # # Inputs : $platforms - Platform selector # $script - A single script name # $execute - Flag to indicate that the script is to # marked as executable when used in a TestProg # This flag is NOT used as the script will # be forced executable # # Returns : Nothing # sub Script { my( $platforms, $script, $execute ) = @_; Debug2( "Script(@_)" ); return if ( ! ActivePlatform($platforms) ); # # Locate the script as a source file # my $file = MakeSrcResolve ( $script ); $script = StripDir( $file ); $SCRIPTS{ $script } = $file; } #------------------------------------------------------------------------------- # Function : RunTest # # Description : Define a test to be run with the 'run_tests' and 'run_unit_tests' # # Inputs : $platform - Enabled for these platforms # $prog - Program to run # This SHOULD return a non-zero exit status # on error. The program may be a 'TestProg' # or a 'Script'. # @elements - Options and test arguments # Options are: # --Auto - Non interactive unit test # --Unit - Same and --Auto # --UtfFormat=nnn - Specifies Automated Unit Test, # results post processed with formatter # --UtfArg=nnn - Argument passed into the UTF formatter # --Name=nnn - Test Name. # --CopyIn=file - A file to be copied into # The test directory. # # Non Options are passed to the test program. # --PackageBase(xxx) - Base of package # --PackageInfo(xxx) - Package information # --File(xxx) - Resolved name of file # --Var(xxx) - Expanded variable # --Local(xxx) - File within the local directory # # Toolset Framework support (ie NUNIT in csharp.pl) # --FrameWork=name - Name of framework # --xxxx - Args passed to framework constructor # # Returns : Nothing # my %RunTestNames; # Unique Name Tests sub RunTest { my( $platforms, $prog, @elements ) = @_; my $command = './'; # program prefix / command my $winprog = 1; # 1: Convert / -> \ (WIN32 only) my $framework; my @framework_opts; my @copy = (); my $auto; my $utfFormat; my @utfArgs; my $utfName; return if ( ! ActivePlatform($platforms) ); # # Scan @elements and extract useful information # Need to process twice as some args will modify the # processing done later # my @args; foreach ( @elements ) { if ( m/^--FrameWork=(.+)/ ) { $framework = $1; } elsif ( m/^--Auto/ || m/^--Unit/) { $auto = 1; } elsif ( m/^--Name=(.*)/) { $utfName = $1; Error("Duplicate Test Name: $utfName") if (exists $RunTestNames{$utfName} ); $RunTestNames{$utfName} = 1; } elsif ( m/^--UtfFormat=(.*)/) { $utfFormat = $1; } elsif ( m/^--UtfArg=(.*)/) { push @utfArgs, $1; } elsif ( m/^--CopyIn=(.*)/ ) { push @copy, MakeSrcResolve ( $1 ); } elsif ( $framework && m/^--\w+=(.+)/ ) { push @framework_opts, $_; } else { push @args, $_; } } @elements = @args; @args = (); # # Determine the source of the test prog # If using a plug-in framework, then we don't know # If not, then may be a script or a TESTPROGS # unless ( $framework ) { if ( $TESTPROGS->Get($prog) || $PROGS->Get($prog) ) { # # Append a suitable EXE suffix # $prog = GenProgName( $prog ); } elsif ( exists $SCRIPTS{$prog} ) { # # Script names are raw # Perl script are invoked directly # $command = "\$(GBE_PERL) -w " if ( $prog =~ /\.pl$/ ); # # Pass / to shells # $winprog = 0 unless ( $prog =~ m~\.bat$~ ) } else { Warning("RunTest program not known: $prog", "It is not a TestProg, Prog or a Script", "The test may fail" ); } } # # Extract and process options # my @uargs = (); my @preq_files; foreach my $arg (@elements) { # # Process the tool arguments and extract file information # Extract all fields of the form: # --xxxxx(yyyyyy[,zzzzz]) # --xxxxx{yyyyyyy} # --xxxxx[yyyyyyy] to allow embedded brackets # while ( $arg =~ m/--(\w+) # --CommandWord $1 ( # Just for grouping \((.*?)\) | # Stuff like (yyyyy) $3 {(.*?)} | # or like {yyyyy} $4 \[(.*?)\] # or like [yyyyy] $5 )/x ) # Allow comments and whitespace { my $cmd = $1; # The command my $ufn = $3 || $4 || $5; # User filename + options my $mb = $-[0]; # Match begin offset my $me = $+[0]; # Match end my $flags = ''; # Optional flags ( --dir or --file ) my $raw_arg = $ufn; # Raw arguments my $all = substr( $arg, $mb, $me ); # All of match. Avoid use of $& my $is_abs; my $is_path = 1; Error ("RunTest. Empty element not allowed: $all") unless ( defined($ufn) ); $ufn =~ s/\s+$//; $ufn =~ s~//~/~g; # Remove multiple / if ( $ufn =~ m/(.*?),(.*)/ ) # Extract out any flags { $ufn = $1; $flags = $2; } my $fn = $ufn ; # Replacement filename my $fnp = ''; # Prefix to $fn Error ("RunTest. Empty element not allowed: $all" ) if ( length ($ufn) <= 0 ); # # Process found user command # if ( $cmd =~ /^File/ ) { # # Prerequisite filename # Resolve the full name of the file. It may be known # as a source file (possibly generated) or it may be # located in a known source directory # $fn = MakeSrcResolve ( $ufn ); UniquePush (\@preq_files, $fn); Debug( "RunTest: Prereq: $fn" ); } elsif ( $cmd =~ /^PackageBase/ ) { $fn = GetPackageBase( "RunTest", $raw_arg ); UniquePush (\@preq_files, $fn); } elsif ( $cmd =~ /^PackageInfo/ ) { $fn = GetPackageInfo( "RunTest", $raw_arg ); } elsif ( $cmd =~ /^Var/ ) { ($fnp, $fn, $is_path, $is_abs) = ExpandGenVar( "RunTest", $raw_arg ); $flags = ''; } elsif ( $cmd =~ /^Local/ ) { $fn = '$(LOCALDIR)/' . $ufn ; UniquePush (\@preq_files, $fn); } else { Warning ("RunTest: Unknown replacement command: $cmd"); $fn = $ufn; } # # Process path modification flags # --dir - only the directory part ( or a "." ) # --file - only the file part # --abspath - Absolute path # --absdrive - Absolute path with drive letter(WIN) # $fn = ProcessPathName( $fn, $flags ); # # The program is going to be executed within a subdirectory # so add one more level of indirection to the path, but only if # the path is relative # if ( $is_path && ! $is_abs ) { unless ( $fn =~ m~^/|^\w:/~ ) { $fn = '../' . $fn unless( $fn =~ s~=~=../~ ); $fn =~ s~/.$~~; } } # # Minor kludge under windows. Ensure directores have a "\" sep # Unless the user has specified a straight shell command # $fn = "\$(subst /,\\,$fn)" if ( $::ScmHost eq "WIN" && $winprog ); # # Prepend any $fn Prefix # This will be a tag and is not subject to path processing # $fn = $fnp . $fn; # # Replace the found string with the real name of the file # Note: 4 argument version of substr is not always available # so we must do it the hard way # substr( $arg, $mb, $me - $mb, $fn); # $arg = substr( $arg, 0, $mb ) . $fn . substr( $arg, $me ); Debug2( "RunTest: subs: $all -> $fn" ); } push(@uargs, "'$arg'"); } # # Create the test entry # This is a structure that will be placed in an array # The array preserves order and uniqness # my %test_entry; $test_entry{'framework'}= $framework if ( $framework ); $test_entry{'framework_opts'}= \@framework_opts if ( $framework ); $test_entry{'command'} = $command . $prog unless ( $framework); $test_entry{'prog'} = $prog; $test_entry{'copyprog'} = 1; $test_entry{'args'} = \@uargs; $test_entry{'auto'} = $auto if ( $auto ); $test_entry{'utfformat'}= $utfFormat if ( $utfFormat ); $test_entry{'utfargs'} = \@utfArgs; $test_entry{'utfname'} = $utfName; $test_entry{'copyin'} = \@copy; $test_entry{'copyonce'} = (); $test_entry{'preq'} = \@preq_files; $test_entry{'testdir'} = 'BINDIR'; push ( @TESTS_TO_RUN, \%test_entry ); # # Flag Auto Run processing required # $TESTS_TO_RUN = 1; $TESTS_TO_AUTORUN = 1 if ( $auto ); } sub TestProg { my( $platforms, $prog, @elements ) = @_; Debug2( "TestProg($platforms, $prog, @elements)" ); return if ( ! ActivePlatform($platforms) ); Error ("TestProg: Program name not defined") unless ( $prog ); Error ("Programs are not supported") unless ( defined $::exe ); # # Create a new Prog object, or retrieve any existing one # my $pProg = $TESTPROGS->Get($prog); $pProg = $TESTPROGS->NewAdd($prog) unless ( $pProg ); #.. Parse all of the object, library and argument entries Debug( "TestProg: $prog" ); foreach (@elements) { if ( /^[-]{1,2}([lL])(.*)/ ) { #.. Target Library specified - add to library list. # Debug( "TestProg: lib -$1$2" ); $pProg->addItem('LIBS', "-$1$2"); next; } if ( /^--if(.*)/ ) { #.. Library conditional - add to library list. # Debug( "TestProg: cond $_" ); $pProg->addItem('LIBS', $_); next; } if ( /^-(.*)/ ) { #.. Argument specified - add to argument list # Debug( "TestProg: arg $_" ); $pProg->addItem('ARGS', $_); next; } if ( %::ScmToolsetProgSource ) { # # Toolset provides support for some file types # to be passed directly to the program builder # my $ext = StripFile($_); if ( exists ($::ScmToolsetProgSource{$ext}) ) { my $full_path = MakeSrcResolve ( $_ ); my $flag = $::ScmToolsetProgSource{$ext}; Debug( "TestProg: src $_" ); $pProg->addItem('ARGS', "$flag$full_path"); next; } } if ( $::o ) { #.. Object specified - add to object list. # my $obj = _LibObject( "", $_ ); #.. Add to program object list. $pProg->addItem('OBJS', "\$(OBJDIR)/$obj"); next; } # # Don't know how to handle this type of argument # Error ("TestProg: Don't know how to handle: $_" ); } } sub Prog { my( $platforms, $prog, @elements ) = @_; Debug2( "Prog($platforms, $prog, @elements)" ); return if ( ! ActivePlatform($platforms) ); Error ("Prog: Program name not defined") unless ( $prog ); Error ("Programs are not supported") unless ( defined $::exe ); # # Create a new Prog object, or retrieve any existing one # my $pProg = $PROGS->Get($prog); $pProg = $PROGS->NewAdd($prog) unless ( $pProg ); #.. Parse all of the object, library and argument entries Debug( "Prog: $prog" ); foreach (@elements) { if ( /^[-]{1,2}([lL])(.*)/ ) { #.. Target Library specified - add to library list. # Debug( "Prog: lib -$1$2" ); $pProg->addItem('LIBS', "-$1$2"); next; } if ( /^--if(.*)/ ) { #.. Library conditional - add to library list. # Debug( "Prog: cond $_" ); $pProg->addItem('LIBS', $_); next; } if ( /^-(.*)/ ) { #.. Argument specified - add to argument list # Debug( "Prog: arg $_" ); $pProg->addItem('ARGS', $_); next; } if ( %::ScmToolsetProgSource ) { # # Toolset provides support for some file types # to be passed directly to the program builder # my $ext = StripFile($_); if ( exists ($::ScmToolsetProgSource{$ext}) ) { my $full_path = MakeSrcResolve ( $_ ); my $flag = $::ScmToolsetProgSource{$ext}; Debug( "Prog: src $_" ); $pProg->addItem('ARGS', "$flag$full_path"); next; } } if ( $::o ) { #.. Object specified - add to object list. # my $obj = _LibObject( "", $_ ); #.. Add to program object list. $pProg->addItem('OBJS', "\$(OBJDIR)/$obj"); next; } # # Don't know how to handle this type of argument # Error ("Prog: Don't know how to handle: $_" ); } } #------------------------------------------------------------------------------- # Function : ProgAddExtra # # Description : This (internal) function allows a toolset to list additional # binaries as a part of a program. This will ensure that the # binaries are generated in the 'make_prog' phase with the main # program. # # The files are not listed for packaging, by this function # # The function does not ensure that the files are not already # listed as a @PROG ( as @PROGS is not fully resolved at this point ) # # Inputs : $name - Tag name of program being built # Not used (yet) # $prog - Fully resolved path to a file # # Returns : Nothing # sub ProgAddExtra { my ($name, $prog) = @_; Debug2( "ProgAddExtra($name: $prog)" ); UniquePush(\@PROGS_EXTRA, $prog); } our %PROJECTS; # Project information my @PROJECTS_ORDER; #------------------------------------------------------------------------------- # Function : MakeProjectName # # Description : Create a uniq project name # # Inputs : srcPath # # Returns : A unique project name # sub MakeProjectName { my ($srcPath) = @_; my $suffix = ""; my $index = 1; my $proj = StripDir( $srcPath ); while (exists $PROJECTS{$proj . $suffix}) { $suffix = '.' . $index++; } return $proj . $suffix; } #------------------------------------------------------------------------------- # Function : MakeProject # # Description : A nasty directive that is intended to build a Microsoft # project for WINCE, WIN32 and .NET builds. # # There are many constraints: # Cannot be mixed with multi-platform builds # Some parameters are tool specific # # Allow programs to be Installed as well as Packaged # The 'Progect' is treated' as a program and it doesn't work # to well if we Install libraries. # # Only Reason to Install Programs is to allow the Cab Maker # to locate them. # # Inputs : Platform - Active platform # Project - Project Name with extension # Options - Many options # # Returns : # sub MakeProject { my( $platforms, $proj, @elements ) = @_; Debug2( "MakeProject($platforms, $proj, @elements)" ); return if ( ! ActivePlatform($platforms) ); # # Sanity test # Error ("MakeProject: Project name not defined") unless ( $proj ); # # Take the project name and convert it into a full path # Need to create a uniq project name - allowing for multiple uses # my $project = MakeSrcResolve ( $proj ); $proj = MakeProjectName($project); Error ("Project File Not found: $project") unless ( -f $project ); my $basedir = StripFileExt( $project ); # # Collect user arguments # They are all processed within the toolset # my @tool_options; foreach ( @elements ) { if ( m/^--Debug/ ) { $PROJECTS{$proj}{'Debug'} = 1; } elsif ( m/^--Prod/ ) { $PROJECTS{$proj}{'Prod'} = 1; } elsif ( m/^--(Package|Install)ProgDebug=(.*)/ ) { _PackageFromProject( $1, $proj, $basedir,'Prog', 'D', $2 ); } elsif ( m/^--(Package|Install)Prog(Prod)*=(.*)/ ) { _PackageFromProject( $1, $proj, $basedir, 'Prog', 'P', $3 ); } elsif ( m/^--(Package)LibDebug=(.*)/ ) { _PackageFromProject( $1, $proj, $basedir, 'Lib', 'D', $2 ); } elsif ( m/^--(Package)Lib(Prod)*=(.*)/ ) { _PackageFromProject( $1, $proj, $basedir, 'Lib', 'P', $3 ); } elsif ( m/^--(Package)SharedLibDebug=(.*)/ ) { _PackageFromProject( $1, $proj, $basedir, 'Lib', 'D', $2 ); } elsif ( m/^--(Package)SharedLib(Prod)*=(.*)/ ) { _PackageFromProject( $1, $proj, $basedir, 'Lib', 'P', $3 ); } elsif ( m/^--(Package)Hdr=(.*)/ ) { _PackageFromProject( $1, $proj, $basedir, 'Hdr', undef, $2 ); } elsif ( m/^--(Package)File=(.*)/ ) { _PackageFromProject( $1, $proj, $basedir, 'File', undef, $2 ); } elsif ( m/^--(Package)Tool(Prod)*=(.*)/ ) { _PackageFromProject( $1, $proj, $basedir, 'Tool', 'P', $3 ); } elsif ( m/^--(Package)ToolDebug=(.*)/ ) { _PackageFromProject( $1, $proj, $basedir, 'Tool', 'D', $2 ); } elsif ( m/^--(Package|Install)/ ) { Error("MakeProject. Unknown $1 option: $_"); } else { push @tool_options, $_; } } # # Save the information # $PROJECTS{$proj}{'options'} = \@tool_options; $PROJECTS{$proj}{'name'} = $proj; $PROJECTS{$proj}{'project'} = $project; $PROJECTS{$proj}{'basedir'} = $basedir; UniquePush (\@PROJECTS_ORDER, $proj); # # Validate some of the arguments # Error ("Makeproject. Conflicting options --Debug and --Prod" ) if ( $PROJECTS{$proj}{'Debug'} && $PROJECTS{$proj}{'Prod'} ); } #------------------------------------------------------------------------------- # Function : _PackageFromProject # # Description : Save Packaged data from the project # # Inputs : $tgt - Install or Package # $proj - Name of the project # $base - Base directory of files # $etype - Type of Package (Progs, Libs, ... ) # $type - Debug or Production or both # $items - Item to add. It may be comma seperated # my %PackageToData = ( 'Package' => { 'Hdr' => \%PACKAGE_HDRS, 'Lib' => \%PACKAGE_LIBS, 'Prog' => \%PACKAGE_PROGS, 'File' => \%PACKAGE_FILES, 'Tool' => \%PACKAGE_FILES, '_BASE' => 'PBase', }, 'Install' => { 'Hdr' => \%INSTALL_HDRS, 'Lib' => \%INSTALL_LIBS, 'Prog' => \%INSTALL_PROGS, 'File' => undef, 'Tool' => undef, '_BASE' => 'IBase', }, ); sub _PackageFromProject { my( $tgt, $proj, $base, $etype, $type, $items ) = @_; my $subdir = ''; # # Sanity test # $type = '' unless ( $type ); Error ("INTERNAL. Bad packaging option: $tgt") unless ( exists $PackageToData{$tgt} ); Error ("INTERNAL. Bad packaging option: $etype") unless ( exists $PackageToData{$tgt}{$etype} ); Error ("Unsupported packaging combination: $tgt$etype$type=$items") unless ( defined $PackageToData{$tgt}{$etype} ); # # Determine the index into the 'PackageInfo' structure # This provides the symbolic name for the target package path # for Package or Install # # The key '_BASE' is internal. Used only to provide this information # my $tbase = $PackageToData{$tgt}{'_BASE'}; # # Process options # foreach my $item ( split (/,/, $items ) ) { next unless ( $item =~ m/^--/ ); if ( $item =~ m/^--Subdir=(.*)/ ) { $subdir = '/' . $1; $subdir =~ s~//~/~g; $subdir =~ s~/$~~g; } else { Warning( "MakeProject: Unknown packaging option ignored: $_" ); } } # # Process files # foreach my $item ( split (/,/, $items ) ) { next if ( $item =~ m/^--/ ); my $tdir = $PackageInfo{$etype}{$tbase} . $PackageInfo{$etype}{'Dir'} . $subdir ; my $fname = StripDir( $item ); my $target = $tdir . '/' . $fname; $item = "$base/$item" if ( $base ); # # Do not use $(GBE_TYPE) in the target name # The existing package mechanism does not handle different # production and debug file naming mechanism, whereas the project # must. Convert $(GBE_TYPE) into P or D to ensure uniquness # $item = QuoteForMake($item); $target = QuoteForMake($target); $target =~ s~\$\(GBE_TYPE\)~$type~ if ($type); # # Create a PACKAGE entry suitable for processing by the normal packaging # routines. This is complicated because the Projects do not adhere to # the JATS file name conventions # my %package_entry; $package_entry{'src'} = $item; $package_entry{'dir'} = $tdir; $package_entry{'set'} = 'ALL' if ($tgt eq 'Package'); $package_entry{'type'} = $type if ($type); $PackageToData{$tgt}{$etype}->{$target} = {%package_entry}; } } #------------------------------------------------------------------------------- # Function : MakeAnt # # Description : A nasty directive to create JAR files via ANT # There are several limitations # This is closely related to the MakeProject directive # # # Inputs : Platform - Active platform # buildfile - Name of the build.xml file # Options - A few options # --Jar=file # Generated JAR file(s) # --GeneratedFile=file # Other generated files # Used to flag JNI that must # Occur early # --AutoTest= # Supports unitAutomated unit test # by calling build target # --UnitTest= # Supports unit test # by calling build target # --PackageBase # Provides path to base of all packages # # Returns : # our %JAR_FILES; sub MakeAnt { my( $platforms, $proj, @elements ) = @_; Debug2( "MakeAnt($platforms, $proj, @elements)" ); return if ( ! ActivePlatform($platforms) ); # # Sanity test # Error ("MakeAnt: build.xml name not defined") unless ( $proj ); # # Take the project name and convert it into a full path # my $project; $project = MakeSrcResolve ( $proj ); $proj = MakeProjectName($project); Error ("Build File Not found: $project") unless ( -f $project ); my $basedir = StripFileExt( $project ); # # Collect user arguments # They are all processed within the toolset # my @tool_options; my @generated; my $unit_tests; my $auto_tests; my $package_base; foreach ( @elements ) { if ( m/^--Debug/ ) { $PROJECTS{$proj}{'Debug'} = 1; } elsif ( m/^--Prod/ ) { $PROJECTS{$proj}{'Prod'} = 1; } elsif ( m/^--Jar=(.*)/ ) { my $tgt = $1; $tgt = "$basedir/$tgt" if ( $basedir ); my $fn = StripDir( $1 ); $JAR_FILES{$fn} = $tgt; GenerateSrcFile( 0, $tgt ); } elsif ( m/^--GeneratedFile=(.*)/ ) { my $tgt = $1; $tgt = "$basedir/$tgt" if ( $basedir ); push @generated, $tgt; GenerateSrcFile( 2, $tgt ); } elsif ( m/^--UnitTest=(.*)/ ) { $unit_tests = $1 } elsif ( m/^--AutoTest=(.*)/ ) { $auto_tests = $1 } elsif ( m/^--PackageBase/ ) { $package_base = 1; } elsif ( m/^--/ ) { Error("MakeAnt. Unknown option ignored: $_"); } else { push @tool_options, $_; } } # # Extend option arguments to include the base dir of packages # Create definitions of the form PACKAGE_ # for my $entry (@{$::ScmBuildPkgRules{$ScmPlatform} }) { next unless ( $entry->{'TYPE'} eq 'link' ); my $dir = $entry->{'ROOT'}; my $name = $entry->{'NAME'}; unless ( $package_base ) { $dir .= '/jar'; next unless ( -d $dir ); } push @tool_options, "-DPACKAGE_$name=$dir"; } # # Extend options to include the base dir of the created package # Allows careful use for direct packaging of artifacts # push @tool_options, '-DPACKAGEDIR=$(PWD)/$(PKGDIR)'; # # Save the information # $PROJECTS{$proj}{'options'} = \@tool_options; $PROJECTS{$proj}{'generated'} = \@generated if ( @generated ); $PROJECTS{$proj}{'name'} = $proj; $PROJECTS{$proj}{'project'} = $project; $PROJECTS{$proj}{'basedir'} = $basedir; $PROJECTS{$proj}{'type'} = 'ant'; $PROJECTS{$proj}{'unittest'} = $unit_tests if ( $unit_tests ); $PROJECTS{$proj}{'autotest'} = $auto_tests if ( $auto_tests ); UniquePush (\@PROJECTS_ORDER, $proj); $TESTS_TO_AUTORUN = 1 if ( $auto_tests ); $TESTS_TO_RUN = 1 if ( $unit_tests || $auto_tests ); # # Validate some of the arguments # Error ("MakeAnt. Conflicting options --Debug and --Prod" ) if ( $PROJECTS{$proj}{'Debug'} && $PROJECTS{$proj}{'Prod'} ); } ############################################################################### # # Installation/Packaging util functions # #------------------------------------------------------------------------------- # Function : __TargetDir # # Description : Internal function to process common arguments for # the PackageXxx directives # # Inputs : flags - Indicate how to handle this argument # base - Base directory for this type of package # argument - Argument to process # pdir - Reference to resultant directory # ptype - Reference to resultant type (P or D)(optional) # # Returns : 0 - Agument not consumed # 1 - Argument consumed # 2 - Skip this directive # my $T_TYPE = 0x0001; # Postfix GBE_TYPE my $T_PKG = 0x0002; # Special --Dir handling my $T_MACH = 0x0004; # Allow --Machine too my $T_GBE = 0x0008; # Allow --Gbe too my $T_FILE = 0x0010; # Suffix or prefix subdir sub __TargetDir { my( $flags, $base, $argument, $pdir, $ptype ) = @_; my $dir = ""; my $consumed = 0; # # Generate basic parts # Note Product will default to Platform # my $str_platform = '$(GBE_PLATFORM)'; my $str_product = $ScmProduct ? '$(GBE_PRODUCT)' : '$(GBE_PLATFORM)'; my $str_target = '$(GBE_TARGET)'; my $str_common = '$(GBE_OS_COMMON)'; my $str_common_avail = 0; $str_common_avail = 1 if ( exists( $::BUILDINFO{$ScmPlatform}{OS_COMMON} )); # # Add requested suffix # if ($flags & $T_TYPE) { $str_platform .= '$(GBE_TYPE)'; $str_product .= '$(GBE_TYPE)'; $str_target .= '$(GBE_TYPE)'; $str_common .= '$(GBE_TYPE)'; } # # Process the argument # $_ = $argument; if ( /^--Debug/ ) { # In the Debug build only if ( $ptype ) { $$ptype = "D"; $consumed = 1; } } elsif ( /^--Prod$/ || /^--Production$/ ) { # In the Production build only if ( $ptype ) { $$ptype = "P"; $consumed = 1; } } elsif (/^--Prefix=(.*)/) { # Prefix with subdir $dir = "$base/$1"; } elsif (/^--Subdir=(.*)/) { # same as 'prefix' $dir = "$base/$1"; } elsif (/^--Platform$/) { # Platform installation $dir = "$base/$str_platform"; } elsif (/^--Platform=(.*?),(.*)/) { # prefix and suffix with platform specific subdir $dir = "$base/$1/$str_platform/$2"; } elsif (/^--Platform=(.*)/) { # prefix with platform specific subdir if ($flags & $T_FILE) { $dir = "$base/$1/$str_platform"; } else { $dir = "$base/$str_platform/$1"; } } elsif (/^--Product$/) { # Product installation $dir = "$base/$str_product"; } elsif (/^--Product=(.*?),(.*)/) { # prefix and suffix with product specific subdir $dir = "$base/$1/$str_product/$2"; } elsif (/^--Product=(.*)/) { # prefix with product specific subdir if ($flags & $T_FILE) { $dir = "$base/$1/$str_product"; } else { $dir = "$base/$str_product/$1"; } } elsif (/^--Target$/) { # Target installation $dir = "$base/$str_target"; } elsif (/^--Target=(.*?),(.*)/) { # prefix and suffix with target specific subdir $dir = "$base/$1/$str_target/$2"; } elsif (/^--Target=(.*)/) { # prefix with target specific subdir if ($flags & $T_FILE) { $dir = "$base/$1/$str_target"; } else { $dir = "$base/$str_target/$1"; } } elsif (/^--OsCommon/) { unless ( $str_common_avail ) { Warning("Packaging option --OsCommon not supported on this platform($ScmPlatform). Directive skipped"); $consumed = 2; } elsif (/^--OsCommon$/) { # OS installation $dir = "$base/$str_common"; } elsif (/^--OsCommon=(.*?),(.*)/) { # prefix and suffix with target specific subdir $dir = "$base/$1/$str_common/$2"; } elsif (/^--OsCommon=(.*)/) { # prefix with target specific subdir if ($flags & $T_FILE) { $dir = "$base/$1/$str_common"; } else { $dir = "$base/$str_common/$1"; } } } elsif (/^--Derived=(.*?),(.*?),(.*)/) { # Derived target + prefix + subdir $dir = "$base/$2/$1_$str_platform/$3"; } elsif (/^--Derived=(.*?),(.*)/) { # Derived target + subdir if ($flags & $T_FILE) { $dir = "$base/$2/$1_$str_platform"; } else { $dir = "$base/$1_$str_platform/$2"; } } elsif (/^--Derived=(.*)/) { # Derived target $dir = "$base/$1_$str_platform"; } elsif ($flags & $T_MACH && /^--Machine(([=])(.*))?$/) { # Allow Machine and Machine=xxx specfic target # # Special: Append machine type to user dir # Intended to create tools/bin/win32 and tools/bin/sparc directories my $path = ( defined( $3) ) ? "/$3" : ""; $dir = "$base$path/\$(GBE_HOSTMACH)"; } elsif ($flags & $T_GBE && /^--Gbe(([=])(.*))?$/) { # Allow Gbe and Gbe=xxx specfic target my $path = ( defined( $3) ) ? "/$3" : ""; $dir = "$base/gbe$path"; } elsif (/^--Dir=(.*)/) { # prefix with target specific subdir Error ('Packaging directive with --Dir option does not specify a directory.', 'Possible bad use of option of the form:--Dir=$xxx', 'Note: Use of package.pl and this construct is deprecated') unless ( $1 ); my $udir = $1; # # Remove leading ./ # Check for leading ../ while ( $udir =~ s{^\./}{} ){}; if ( $udir =~ m~^\.\./~ ) { Warning("Packaging directive with --Dir option contains relative path (removed)", "Option: $_"); while ( $udir =~ s{^\.\./}{} ){}; } if ($flags & $T_PKG) { $dir = __PkgDir( $udir ); } else { $dir = "\$(LOCALDIR)/$udir"; } } return ($consumed) if ($dir eq ""); $dir =~ s~//~/~g; $dir =~ s~/$~~; $$pdir = $dir; return (1); } # __PkgDir --- # Convert --Dir Package directives, removing leading subdir if # matching the global $Pbase value. # # Required as PKGDIR has the value 'GBE_ROOT/pkg/$Pbase'. # Required to maintain compatability with older (package.pl) constructs #.. sub __PkgDir { my( $dir ) = @_; my $org = $dir; $dir =~ s~^\Q$::Pbase\E[/]?~~; Debug2( " PkgDir: converted \"$org\" to \"$dir\"" ); $dir = "\$(PKGDIR)/$dir"; return $dir; } # getMajorMinor --- # Just a little help to deal with major/minor stuff for shared libs - # given the name of the library as the argument, split out major and # minor parts and return the basename, i.e name without major and minor # and the pair of major and minor. #.. sub getMajorMinor { my @bits = split ('\.', $_[0]); my $stop; my $major; my $minor; if ( $#bits > 2 ) { $stop = $#bits - 2; $major = $bits[$#bits-1]; $minor = $bits[$#bits]; } elsif ($#bits > 1) { $stop = $#bits-1; $major = $bits[$#bits]; $minor=0; } else { $stop = $#bits; $major = 1; $minor = 0; } my $base = $bits[0]; for ( my $i=1; $i <= $stop; $i++ ) { $base = join ('.', $base, $bits[$i]); } return ($base, $major, $minor); } ############################################################################### # # Installation # sub InstallHdr { my( $platforms, @elements ) = @_; my( $base, $dir, $srcfile, $full, $strip, $package ); my( $len, $name, $basename ); Debug2( "InstallHdr($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); Warning ("InstallHdr: Needs local directory specified in build.pl") unless ( $::ScmLocal ); #.. Arguments # $base = $PackageInfo{'Hdr'}{'IBase'}; # Base of target $dir = $base . $PackageInfo{'Hdr'}{'Dir'}; # Installation path (default) $full = $strip = 0; foreach ( @elements ) { # Standard targets my $rv = __TargetDir(0, $base, $_, \$dir); next if ( $rv == 1 ); return if ( $rv == 2 ); if (/^--Full/) { # using full (resolved) path $full = 1; } elsif (/^--Strip$/) { # Strip path from source files $strip = 1; # Package } elsif (/^--Package$/ || /^--Package=(.*)/) { $package = 1; } elsif (/^--(.*)/) { Message( "InstallHdr: unknown option $_ -- ignored\n" ); } } #.. Files # foreach ( @elements ) { my %package_entry; if ( ! /^--(.*)/ ) { $name = $_; $basename = StripDir( $name ); if ( !($srcfile = $SRCS{ $basename }) ) { $srcfile = $name; } if ( $full ) { my $subdir = StripFileExt($srcfile); $subdir = $1 if ( $subdir =~ m~^$ProjectBase/(.*)~ ); $dir .= '/' . $subdir; $dir =~ s~//~/~g; $dir =~ s~/./~/~g; $dir =~ s~/$~~g; $name = $basename; } $name = $basename if ( $strip ); Debug( "InstallHdr( $dir/$name, src: $srcfile, dest: $dir)" ); $package_entry{'src'} = $srcfile; $package_entry{'dir'} = StripFileExt( "$dir/$name" ); $INSTALL_HDRS{ "$dir/$name" } = {%package_entry}; } } #.. Package # PackageHdr( @_ ) # auto package if ( $package ); } sub InstallLib { my( $platforms, @elements ) = @_; my( $base, $dir, $package ); my( $lib, $strip ); my $org_lib; Debug2( "InstallLib($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); Warning ("InstallLib: Needs local directory specified in build.pl") unless ( $::ScmLocal ); #.. Arguments # $base = $PackageInfo{'Lib'}{'IBase'}; # Base of target $dir = $base . $PackageInfo{'Lib'}{'Dir'}; # Installation path (default) foreach ( @elements ) { # Standard targets my $rv = __TargetDir(0, $base, $_, \$dir); next if ( $rv == 1 ); return if ( $rv == 2 ); if (/^--Package$/ || /^--Package=(.*)/) { $package = 1; } elsif (/^--Strip$/) { # Strip path from source files $strip = 1; } elsif (/^--(.*)/) { Message( "InstallLib: unknown option $_ -- ignored\n" ); } } #.. Files # foreach ( @elements ) { my %package_entry; if ( ! /^--(.*)/ ) { $_ = basename ($_) if ( $strip ); $org_lib = $_; # Original name if ( $ScmTargetHost eq "Unix" ) { $lib = "lib$_"; # Prefix "lib" .... $lib =~ s/^liblib/lib/; # @LIBS already has lib added } else { $lib = $_; } if ( my $libp = $SHLIBS->Get($lib) ) { Debug( "InstallLib( $dir/$lib\$(GBE_TYPE).$::so, " . "src: \$(LIBDIR)/$lib\$(GBE_TYPE).$::so, dest: $dir)" ); # # Create a "placekeeper" entry within $INSTALL_SHLIBS # The exact format of the name of the shared library is # toolset specific. Create an entry to allow the toolset # to extend the packaging information when the shared library # recipe is constructed. # my $ver = $libp->{ VERSION }; my $name = "$dir/$lib.$ver.PlaceKeeper"; $package_entry{'placekeeper'} = 1; $package_entry{'version'} = $ver; $package_entry{'lib'} = $lib; $package_entry{'dir'} = $dir; push @{$SHLIB_INS{$lib}}, $name; $INSTALL_SHLIBS{$name} = {%package_entry}; } # # Clean up the package_entry # Insert common items # %package_entry = (); $package_entry{'lib'} = $lib; $package_entry{'dir'} = $dir; if ( my $libfile = $SRCS{$org_lib} ) { # # Allow the user to package a sourced file as a library # But must be the un-massaged name of the file. # $package_entry{'dst'} = "$dir/$org_lib"; $package_entry{'src'} = $libfile; } elsif ( $LIBS->Get($lib) ) { # # Install a library known to the makefile # my $libp = $LIBS->Get($lib); $package_entry{'dst'} = $dir . '/' . $libp->getFullName(); $package_entry{'src'} = $libp->getPath(); } elsif ( ! $SHLIBS->Get($lib) ) { # # Not a known shared lib # Not a known static lib # Not a 'sourced' file # Assume the a static library has magically appeared # in the standard LIB directory. May have been placed there # by a 'rule' # my $libp = $LIBS->New($lib); $package_entry{'dst'} = $dir . '/' . $libp->getFullName(); $package_entry{'src'} = $libp->getPath(); } # # Add entry to various lists if required # PackageLib_AddEntry ('InstallLib', \%LIB_INS, \%INSTALL_LIBS, \%package_entry ) if ( exists $package_entry{'dst'} ); } } #.. Package # PackageLib( @_ ) # auto package if ( $package ); } sub InstallJar { my( $platforms, @elements ) = @_; my( $base, $dir, $package ); my( $jar ); Debug2( "InstallJar($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); Warning ("InstallJar: Needs local directory specified in build.pl") unless ( $::ScmLocal ); #.. Arguments # $base = $PackageInfo{'Jar'}{'IBase'}; # Base of target $dir = $base . $PackageInfo{'Jar'}{'Dir'}; # Installation path (default) foreach ( @elements ) { # Standard targets my $rv = __TargetDir(0, $base, $_, \$dir); next if ( $rv == 1 ); return if ( $rv == 2 ); if (/^--Package$/ || /^--Package=(.*)/) { $package = 1; } elsif (/^--(.*)/) { Message( "InstallJar: unknown option $_ -- ignored\n" ); } } #.. Files # foreach ( @elements ) { my %package_entry; if ( ! /^--(.*)/ ) { $jar = $_; my $src; my $dest; if ( $JAR_FILES{$jar} ) { $src = $JAR_FILES{$jar}; $dest = $jar; } else { $src = "\$(CLSDIR)/$jar\$(GBE_TYPE).jar"; $dest = "$jar\$(GBE_TYPE).jar"; } Debug( "InstallJar( $dir/$dest, " . "src: $src, dest: $dir)" ); $package_entry{'src'} = $src; $package_entry{'dir'} = $dir; $INSTALL_CLSS{ "$dir/$dest" } = {%package_entry}; } } #.. Package # PackageJar( @_ ) # auto package if ( $package ); } sub InstallProg { my( $platforms, @elements ) = @_; my( $base, $dir, $package ); my( $prog ); Debug2( "InstallProg($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); Warning ("InstallProg: Needs local directory specified in build.pl") unless ( $::ScmLocal ); #.. Arguments # $base = $PackageInfo{'Prog'}{'IBase'}; # Base of target $dir = $base . $PackageInfo{'Prog'}{'Dir'}; # Installation path (default) foreach ( @elements ) { # Standard targets my $rv = __TargetDir($T_TYPE, $base, $_, \$dir); next if ( $rv == 1 ); return if ( $rv == 2 ); if (/^--Package$/ || /^--Package=(.*)/) { $package = 1; } elsif (/^--(.*)/) { Message( "InstallProg: unknown option $_ -- ignored\n" ); } } #.. Files # foreach ( @elements ) { my %package_entry; if ( ! /^--(.*)/ ) { my $ext = ""; $prog = $_; # # If the named target is a program then append the correct # extension. Otherwise assume that the target is either a script # or a some other file - and don't append an extension # $ext = $::exe if ( $PROGS->Get($prog) ); # # A "file" that is specified with a "Src" directive may be # installed as though it were a program # my $progfile; $progfile = "\$(BINDIR)/$prog$ext" unless ( $progfile = $SRCS{$prog} ); Debug( "InstallProg( $dir/$prog$ext, " . "src: $progfile, dest: $dir)" ); push @{$PROG_INS{$prog}}, "$dir/$prog$ext"; $package_entry{'src'} = $progfile; $package_entry{'dir'} = $dir; $INSTALL_PROGS{ "$dir/$prog$ext" } = {%package_entry}; } } #.. Package # PackageProg( @_ ) # auto package if ( $package ); } ############################################################################### # # Packaging # sub PackageDist { my( $name, @elements ) = @_; Debug2( "PackageDist($name, @elements)" ); foreach ( @elements ) { #.. Distribution sets # HashJoin( \%PACKAGE_DIST, $;, $name, "$_" ); #.. Summary of distribution sets # $PACKAGE_SETS{ $_ }{'TAG'} = 1 if ( ! exists $PACKAGE_SETS{ $_ }{'TAG'} ); } } #------------------------------------------------------------------------------- # Function : PackageFile # # Description : Directive to package files # Not to be used to package libraries, executables, headers # as this should be done by specialised directives # # Use to package other files # Can package an entire tree (ugly) # # Inputs : # # sub PackageFile { my( $platforms, @elements ) = @_; my( $base, $dir, $full, $path, $dist, $strip, $exefile, $type ); my( $name, $basename, $len, $srcfile ); my( $dir_tree, @dir_tree_exclude, @dir_tree_include, $strip_base, $strip_dots ); my $recurse = 1; Debug2( "PackageFile($platforms, @elements)" ); return if ( !$ScmPackage ); # Packaging enabled ? return if ( ! ActivePlatform($platforms) ); #.. Arguments # $dist = "ALL"; # Default set (ALL) $base = $PackageInfo{'File'}{'PBase'}; # Base of target $dir = $base . $PackageInfo{'File'}{'Dir'}; # Installation path (default) $full = 0; $strip = 0; $strip_base = 0; $strip_dots = 0; $exefile = 0; foreach ( @elements ) { my $rv = __TargetDir($T_PKG|$T_MACH|$T_GBE|$T_FILE, $base, $_, \$dir, \$type); next if ( $rv == 1 ); return if ( $rv == 2 ); if (/^--Full/) { # Using full (resolved) path $full = 1; } elsif (/^--Set=(.*)/) { # Distribution set $dist = "$1"; } elsif (/^--Package$/) { # Package .. call by InstallFile } elsif (/^--Package=(.*)/) { $dist = "$1"; } elsif (/^--Strip$/) { # Strip path from source files $strip = 1; } elsif (/^--Executable$/) { # Mark the file as executable $exefile = "X"; } elsif ( /^--DirTree=(.*)/ ) { Error("DirTree. Multiple directories not allowed.") if ( $dir_tree ); $dir_tree = $1; Error("DirTree. No path specified") unless ( defined($dir_tree) && $dir_tree ne "" ); # Prevent the user from escaping from the current directory Error("DirTree. Absolute paths are not allowed", "Directory: $dir_tree") if ( $dir_tree =~ m~^/~ || $dir_tree =~ m~^.\:~ ); # # Convert the relative path to one that is truely relative to the current # directory. This may occur when the user uses $ProjectBase # my $abs_dir_tree = AbsPath($dir_tree); $dir_tree = RelPath($abs_dir_tree); # # Ensure that the user is not trying to escape the package # Don't allow the user to attempt to package the entire package either # # Calculate the relative path from $ProjectBase to the target directory # It must not be above the $ProjectBase # if ( $dir_tree =~ m~^\.\.~) { my $dirFromBase = RelPath($abs_dir_tree, AbsPath($ProjectBase)); Error("DirTree cannot extend outside current package.", "Directory: $dirFromBase") if ( $dirFromBase =~ m~\.\.~ ); Error("DirTree cannot package entire package.", "Directory: $dirFromBase") if ( $dirFromBase eq '.' ); } Debug2( "PackageFile. DirTree: $dir_tree" ); Error("DirTree. Directory not found", "Directory: $dir_tree") unless ( -d $dir_tree ); # If packaging a parent directory then force dot_stripping of the base directory # strip_base will have precedence if both are active if ( $dir_tree =~ m~\.\.~ ) { $dir_tree =~ m~(\.\./)+~; $strip_dots = length($1); } } elsif ( /^--FilterOut=(.*)/ ) { push @dir_tree_exclude, $1; } elsif ( /^--FilterIn=(.*)/ ) { push @dir_tree_include, $1; } elsif ( /^--StripDir/ ) { $strip_base = 1; } elsif ( m/^--Recurse/ ) { $recurse = 1; } elsif ( m/^--NoRecurse/ ) { $recurse = 0; } elsif (/^--(.*)/) { Message( "PackageFile: unknown option $_ -- ignored\n" ); } } #.. DirTree expansion # Note: Uses REs, not simple globs # Use JatsLocateFiles to do the hard work if ( $dir_tree ) { my $search = JatsLocateFiles->new('FullPath' ); $search->recurse($recurse); $search->filter_in_re ( $_ ) foreach ( @dir_tree_include ); $search->filter_out_re( $_ ) foreach ( @dir_tree_exclude ); $search->filter_out_re( '/\.svn/' ); @elements = $search->search ( $dir_tree ); if ($strip_base){ $strip_base = length( $dir_tree ) if ( $strip_base ); } elsif ($strip_dots) { $strip_base = $strip_dots; } } #.. Files # foreach ( @elements ) { my %package_entry; $name = $_; # # Trap special files # DPACKAGE - but only if we have a DPackageLibrary directive # in the same makefile. # if ( m~^DPACKAGE$~ && $DPackageDirective ) { $name = 'DPACKAGE.' . $::GBE_MACHTYPE; } # # Allow for named files that must be quoted $name = QuoteForMake( $name ); if ( ! /^--(.*)/ ) { $basename = StripDir( $name ); if ( !($srcfile = $SRCS{ $basename }) ) { $srcfile = $name; } if ( $full ) { my $subdir = StripFileExt($srcfile); $subdir = $1 if ( $subdir =~ m~^$ProjectBase/(.*)~ ); $dir .= '/' . $subdir; $dir =~ s~//~/~g; $dir =~ s~/./~/~g; $dir =~ s~/$~~g; $name = $basename; } $name = $basename if ( $strip ); if ( $strip_base ) { $name = substr $name, $strip_base; $name =~ s~^/~~; } $dir =~ s~//~/~g; $dir =~ s~/$~~; # # Sanity test the source filename # User may have misused an option # if ( ( $srcfile =~ m/=/ ) || ( $srcfile =~ m/^-/ ) || ( $srcfile =~ m~/-~ ) ) { Warning ("PackageFile: Suspect source filename: $srcfile"); } Debug( "PackageFile( $dir/$name, " . "src: $srcfile, dest: $dir, dist: $dist, exe: $exefile )" ); $package_entry{'src'} = $srcfile; $package_entry{'dir'} = StripFileExt( "$dir/$name" ); $package_entry{'set'} = $dist; $package_entry{'exe'} = $exefile if $exefile; $package_entry{'type'} = $type if ( $type ); $PACKAGE_FILES{ "$dir/$name" } = {%package_entry}; } } } sub PackageHdr { my( $platforms, @elements ) = @_; my( $base, $dir, $full, $path, $dist, $strip ); my( $name, $basename, $len, $srcfile ); Debug2( "PackageHdr($platforms, @elements)" ); return if ( !$ScmPackage ); # Packaging enabled ? return if ( ! ActivePlatform($platforms) ); #.. Arguments # $dist = "ALL"; # Default set (ALL) $base = $PackageInfo{'Hdr'}{'PBase'}; # Base of target $dir = $base . $PackageInfo{'Hdr'}{'Dir'}; # Installation path (default) $full = 0; $strip = 0; foreach ( @elements ) { my $rv = __TargetDir($T_PKG, $base, $_, \$dir); next if ( $rv == 1 ); return if ( $rv == 2 ); if (/^--Full/) { # Using full (resolved) path $full = 1; } elsif (/^--Set=(.*)/) { # Distribution set $dist = "$1"; } elsif (/^--Package$/) { # Package .. call by InstallHdr } elsif (/^--Package=(.*)/) { $dist = "$1"; } elsif (/^--Strip$/) { # Strip path from source files $strip = 1; } elsif (/^--(.*)/) { Message( "PackageHdr: unknown option $_ -- ignored\n" ); } } #.. Files # foreach ( @elements ) { my %package_entry; if ( ! /^--(.*)/ ) { $name = QuoteForMake($_); $basename = StripDir( $name ); if ( !($srcfile = $SRCS{ $basename }) ) { $srcfile = $name; } if ( $full ) { my $subdir = StripFileExt($srcfile); $subdir = $1 if ( $subdir =~ m~^$ProjectBase/(.*)~ ); $dir .= '/' . $subdir; $dir =~ s~//~/~g; $dir =~ s~/./~/~g; $dir =~ s~/$~~g; $name = $basename; } $name = $basename if ( $strip ); Debug( "PackageHdr( $dir/$name, " . "src: $srcfile, dest: $dir, dist: $dist )" ); $package_entry{'src'} = $srcfile; $package_entry{'dir'} = StripFileExt( "$dir/$name" ); $package_entry{'set'} = $dist; $PACKAGE_HDRS{ "$dir/$name" } = {%package_entry}; } } } sub PackageLib { my( $platforms, @elements ) = @_; my( $base, $dir, $dist, $type ); my( $lib, $org_lib, %extras, $strip ); Debug2( "PackageLib($platforms, @elements)" ); return if ( !$ScmPackage ); # Packaging enabled ? return if ( ! ActivePlatform($platforms) ); #.. Arguments # $dist = "ALL"; # Default set (ALL) $base = $PackageInfo{'Lib'}{'PBase'}; # Base of target $dir = $base . $PackageInfo{'Lib'}{'Dir'}; # Installation path (default) $type = ""; foreach ( @elements ) { # Standard targets my $rv = __TargetDir($T_PKG, $base, $_, \$dir, \$type); next if ( $rv == 1 ); return if ( $rv == 2 ); if (/^--Set=(.*)/) { # Distribution set(s) $dist = "$1"; } elsif (/^--Package$/) { # Package .. call by PackageLib } elsif (/^--Package=(.*)/) { $dist = "$1"; } elsif (/^--Extras=(.*)/) { # Extras=[none, .. ,all] foreach my $elem ( split( ',', $1 ) ) { Error ("PackageLib: Unknown Extras mode: $elem") unless ( grep m/$elem/, qw(none stub map lint debug all) ); $extras{$elem} = 1; } %extras = () if ( $extras{'all'} ); } elsif (/^--Strip$/) { # Strip path from source files $strip = 1; } elsif (/^--(.*)/) { Message( "PackageLib: unknown option $_ -- ignored\n" ); } } #.. Files # foreach ( @elements ) { my %package_entry; if ( ! /^--(.*)/ ) { $_ = StripDir( $_ ) if ( $strip ); $org_lib = $_; # Original name if ( $ScmTargetHost eq "Unix" ) { $lib = "lib$_"; # Prefix "lib" .... $lib =~ s/^liblib/lib/; # @LIBS already has lib added } else { $lib = $_; } if ( my $libp = $SHLIBS->Get($lib) ) { Debug( "PackageLib( $dir/$lib\$(GBE_TYPE).$::so, " . "src: \$(LIBDIR)/$lib\$(GBE_TYPE).$::so, dest: $dir, dist: $dist, type: $type )" ); # # Create a "placekeeper" entry within $PACKAGE_SHLIBS # The exact format of the name of the shared library is # toolset specific. Create an entry to allow the toolset # to extend the packaging information when the shared library # recipe is constructed. # # my $ver = $libp->{ VERSION }; my $name = "$dir/$lib.$ver.PlaceKeeper"; $package_entry{'placekeeper'} = 1; $package_entry{'version'} = $ver; $package_entry{'lib'} = $lib; $package_entry{'dir'} = $dir; $package_entry{'set'} = $dist; $package_entry{'type'} = $type if ( $type ); $package_entry{'extras'} = {%extras} if ( scalar %extras ); push @{$SHLIB_PKG{$lib}}, $name; $PACKAGE_SHLIBS{$name} = {%package_entry}; } # # Clean up the package_entry # Insert common items # %package_entry = (); $package_entry{'lib'} = $lib; $package_entry{'dir'} = $dir; $package_entry{'set'} = $dist; $package_entry{'extras'} = {%extras} if ( scalar %extras ); $package_entry{'type'} = $type if ( $type ); if ( my $libfile = $SRCS{$org_lib} ) { # # Allow the user to package a sourced file as a library # But must be the un-massaged name of the file. # $package_entry{'dst'} = QuoteForMake("$dir/$org_lib"); $package_entry{'src'} = QuoteForMake($libfile); } elsif ( $LIBS->Get($lib) ) { # # Package up a library known to the makefile # my $libp = $LIBS->Get($lib); $package_entry{'dst'} = $dir . '/' . $libp->getFullName(); $package_entry{'src'} = $libp->getPath(); } elsif ( ! $SHLIBS->Get($lib) ) { # # Not a known shared lib # Not a known static lib # Not a 'sourced' file # Assume the a static library has magically appeared # in the standard LIB directory. May have been placed there # by a 'rule' # my $libp = $LIBS->New($lib); $package_entry{'dst'} = $dir . '/' . $libp->getFullName(); $package_entry{'src'} = $libp->getPath(); } # # Add entry to various lists if required # PackageLib_AddEntry ('PackageLib', \%LIB_PKG, \%PACKAGE_LIBS, \%package_entry ) if ( exists $package_entry{'dst'} ); } } } #------------------------------------------------------------------------------- # Function : PackageLib_AddEntry # # Description : Helper function to add a package entry # to the lists # # Inputs : $directive - Directive name # $pList - Ref to array list to maintain # $pHash - Ref to hash to maintain # $pData - Packaging Data # Must Take a copy. # # Returns : # sub PackageLib_AddEntry { my ($directive, $pList, $pHash, $pData) = @_; my $lib = delete $pData->{'lib'}; my $dst = delete $pData->{'dst'}; Error ("INTERNAL PackageLib_AddEntry: lib or dst not defined") unless ( $lib && $dst ); Debug( "$directive( ",$dst, ", src: " ,$pData->{'src'}, ", dest: ",$pData->{'dir'}, ", dist: ",$pData->{'set'}, ", type: ",$pData->{'type'} || '', " )" ); push @{$pList->{$lib }}, $dst; $pHash->{$dst } = {%$pData}; } sub PackageProg { my( $platforms, @elements ) = @_; my( $base, $dir, $dist, $type ); my( $prog, %extras, $strip ); Debug2( "PackageProg($platforms, @elements)" ); return if ( !$ScmPackage ); # Packaging enabled ? return if ( ! ActivePlatform($platforms) ); #.. Arguments # $dist = "ALL"; # Default set (ALL) $base = $PackageInfo{'Prog'}{'PBase'}; # Base of target $dir = $base . $PackageInfo{'Prog'}{'Dir'}; # Installation path (default) $type = ""; foreach ( @elements ) { # Standard targets my $rv = __TargetDir($T_PKG|$T_TYPE, $base, $_, \$dir, \$type); next if ( $rv == 1 ); return if ( $rv == 2 ); if (/^--Set=(.*)/) { # Distribution set(s) $dist = "$1"; } elsif (/^--Package$/) { # Package .. call by PackageLib } elsif (/^--Package=(.*)/) { $dist = "$1"; } elsif (/^--Tool(([=])(.*))?$/) { # Allow Tool and Tool=xxx specfic target my $path = ( defined( $3) ) ? "/$3" : ""; $dir = "\$(PKGDIR)$path/\$(GBE_HOSTMACH)"; } elsif (/^--Extras=(.*)/) { # Extras=[none, .. ,all] foreach my $elem ( split( ',', $1 ) ) { Error ("PackageLib: Unknown Extras mode: $elem") unless ( grep m/$elem/, qw(none stub map lint debug all) ); $extras{$elem} = 1; } %extras = () if ( $extras{'all'} ); } elsif (/^--Strip$/) { # Strip path from source files $strip = 1; } elsif (/^--(.*)/) { Message( "PackageProg: unknown option $_ -- ignored\n" ); } } #.. Files # foreach ( @elements ) { my %package_entry; if ( m~descpkg~ ) { PackageFile($platforms, @elements); } elsif ( ! /^--(.*)/ ) { $_ = StripDir( $_ ) if ( $strip ); my $ext = ""; $prog = $_; # # If the named target is a program then append the correct # extension. Otherwise assume that the target is either a script # or a some other file - and don't append an extension # # A program may not have any object files, only libraries # $ext = $::exe if ( $PROGS->Get($prog) ); # # A "file" that is specified with a "Src" directive may be # installed as though it were a program # my $progfile; if ( $progfile = $SRCS{$prog} ) { $progfile = QuoteForMake($progfile); $prog = QuoteForMake($prog); } else { $progfile = "\$(BINDIR)/$prog$ext"; } Debug( "PackageProg( $dir/$prog$ext, " . "src: $progfile, dest: $dir, dist: $dist, type: $type )" ); my $target = "$dir/$prog$ext"; push @{$PROG_PKG{$prog}}, $target; $package_entry{'src'} = $progfile; $package_entry{'dir'} = $dir; $package_entry{'set'} = $dist; $package_entry{'extras'}= {%extras} if ( scalar %extras ); $package_entry{'type'} = $type if ( $type ); $PACKAGE_PROGS{$target} = {%package_entry}; } } } sub PackageJar { my( $platforms, @elements ) = @_; my( $base, $dir, $dist, $type ); my( $jar ); Debug2( "PackageJar($platforms, @elements)" ); return if ( !$ScmPackage ); # Packaging enabled ? return if ( ! ActivePlatform($platforms) ); #.. Arguments # $dist = "ALL"; # Default set (ALL) $base = $PackageInfo{'Jar'}{'PBase'}; # Base of target $dir = $base . $PackageInfo{'Jar'}{'Dir'}; # Installation path (default) $type = ""; foreach ( @elements ) { # Standard targets my $rv = __TargetDir($T_PKG, $base, $_, \$dir, \$type); next if ( $rv == 1 ); return if ( $rv == 2 ); if (/^--Set=(.*)/) { # Distribution set(s) $dist = "$1"; } elsif (/^--Package$/) { # Package .. call by InstallJar } elsif (/^--Package=(.*)/) { $dist = "$1"; } elsif (/^--(.*)/) { Message( "PackageJar: unknown option $_ -- ignored\n" ); } } #.. Files # foreach ( @elements ) { my %package_entry; if ( ! /^--(.*)/ ) { $jar = $_; my $src; my $dest; if ( $JAR_FILES{$jar} ) { $src = $JAR_FILES{$jar}; $dest = $jar; } else { $src = "\$(CLSDIR)/$jar\$(GBE_TYPE).jar"; $dest = "$jar\$(GBE_TYPE).jar"; } Debug( "PackageJar( $dir/$dest, " . "src: $src, dest: $dir, dist: $dist, type: $type )" ); $package_entry{'src'} = $src;; $package_entry{'dir'} = $dir; $package_entry{'set'} = $dist; $package_entry{'type'} = $type if ( $type ); $PACKAGE_CLSS{ "$dir/$dest" } = {%package_entry}; } } } #------------------------------------------------------------------------------- # Function : PackageProgAddFiles - Add files to a PackageProg # PackageLibAddFiles - Add files to a PackageLib # PackageShlibAddFiles - Add files to a PackageLib (shared lib) # PackageShlibAddLibFiles - Add files to a PackageLib (shared lib) # Add static library files # # Description : Add files to a Program package or installation # For use by Tool sets to allow additional files to be # packaged with a program. # # The files are only added if the named program is being # packaged and/or installed. # # # Inputs : prog - program identifier # file - A file to be add # args - Additional packageing arguments # # Returns : Nothing # sub PackageProgAddFiles { Debug("PackageProgAddFiles"); PackageAddFiles ( \%PACKAGE_PROGS, \%PACKAGE_PROGS, \%PROG_PKG, @_); PackageAddFiles ( \%INSTALL_PROGS, \%INSTALL_PROGS, \%PROG_INS, @_); } sub PackageLibAddFiles { Debug("PackageLibAddFiles"); PackageAddFiles ( \%PACKAGE_LIBS, \%PACKAGE_LIBS, \%LIB_PKG, @_ ); PackageAddFiles ( \%INSTALL_LIBS, \%INSTALL_LIBS, \%LIB_INS, @_ ); } sub PackageShlibAddFiles { my ($prog, $file, @args) = @_; Debug("PackageShlibAddFiles"); PackageAddFiles ( \%INSTALL_SHLIBS, \%INSTALL_SHLIBS, \%SHLIB_INS, @_ ); PackageAddFiles ( \%PACKAGE_SHLIBS, \%PACKAGE_SHLIBS, \%SHLIB_PKG, @_ ); # # These files become the target of the "make_install_shlib" operation unless: # Conditionally packaged files are not always created # RemoveOnly files are not always generated # my $no_add; foreach ( @args ) { if ( m/^defined=/ or m/^RemoveOnly=/ or /NoTarget=/ ) { $no_add = 1; last; } } push (@SHLIB_TARGETS, $file ) unless $no_add; } sub PackageShlibAddLibFiles { Debug("PackageShlibAddLibFiles"); PackageAddFiles ( \%PACKAGE_SHLIBS, \%PACKAGE_LIBS, \%SHLIB_PKG, @_ , 'Class=lib'); PackageAddFiles ( \%INSTALL_SHLIBS, \%INSTALL_LIBS, \%SHLIB_INS, @_ , 'Class=lib'); } #------------------------------------------------------------------------------- # Function : PackageAddFiles # # Description : Internal function to add files to the data structures that # describe a package or installation # # Use this function to package or install additional files with # the Progs and Libs # # ie: Add a LIB file to be packaged with a Shared Library # ie: Add a MAP file to be packaged with a program # # Inputs : ref_spkg - Reference to the hash that contains the package data # ref_dpkg - Reference to the target package/install hash # Normally the same as ref_dpkg, but does allow # a static library to be added to a dynamic library # package. # ref_list - Reference to a hash that may contain package keys to process # prog - Key for index to above # file - A file to be added # args - Additional packaging arguments # # Returns : # sub PackageAddFiles { my ($ref_spkg, $ref_dpkg, $ref_list, $prog, $file, @args ) = @_; # # Process entry # The files may need to be added to multiple packages # Debug("PackageAddFiles: $file"); return unless ( $ref_list->{$prog} ); # # Parse arguments and extract the "Class=xxx" argument. This may be used # to limit the extra files piggybacked with the base file # All files without a class will be treated as base files # my $class; foreach ( @args ) { next unless ( m~^Class=(.*)$~ ); $class = $1 unless ( $1 eq 'none' ); } Debug("PackageAddFiles: Class: ", $class || 'Default=None'); foreach my $entry_key ( @{$ref_list->{$prog}} ) { Debug("PackageAddFiles: Entry found: $entry_key"); # # Copy of the template entry # my %package_entry = %{$ref_spkg->{$entry_key}}; Error ("INTERNAL: Expected entry in PACKAGE_ hash not found: $entry_key" ) unless ( %package_entry ); # # Do not add the file if the user has limited the extra files added # to the packaging list and the current file is not in the class list # if ( $class && $package_entry{'extras'} ) { next unless ( $package_entry{'extras'}{$class} ); } # # Create new entries for the file # $package_entry{'src'} = $file; foreach ( @args ) { m~^(.*)=(.*)$~; $package_entry{$1} = $2; } # # Clean out useless fields # Must remove the placekeeper marker to allow the entry to be visible # delete $package_entry{'placekeeper'}; delete $package_entry{'version'}; delete $package_entry{'lib'}; # delete $package_entry{'extras'}; # Keep these delete $package_entry{'Class'}; # # Add the entry # # Under some conditions is it possible to attempt to add the same named # file. This will result in a circular dependancy in the makefile # # The condition is when merged libaries with PDBs (WINCE+WIN32) are merged # and the source for the merge is the "local directory. # # my $dst = $package_entry{'dir'} ; ( my $dfile = $file) =~ s~.*/~~; Debug( " added $dst/$dfile = $file" ); $ref_dpkg->{"$dst/$dfile"} = {%package_entry} unless ( "$dst/$dfile" eq "$file" ); } } #------------------------------------------------------------------------------- # Function : PackageProgRemoveFiles # # Description : Flag a Packaged program to be not packaged # This mechanism is used to remove a program from packageing # under conditions where the toolset has generated a different # program. # # The entry is flagged as a placeholder # # Inputs : prog - Program to process # # Returns : Nothing # sub PackageProgRemoveFiles { my ($prog) = @_; Verbose ("PackageProgRemoveFiles: $prog" ); return unless (exists($PROG_PKG{$prog})); # # Must lookup the TAG to locate the required entry # my $tag = $PROG_PKG{$prog}; foreach my $entry ( @$tag ) { Verbose("Do not package: $entry"); if ( exists $PACKAGE_PROGS{$entry} ) { $PACKAGE_PROGS{$entry}{placekeeper} = 'ProgRemoved'; } } } #------------------------------------------------------------------------------- # Function : DPackageLibrary # # Description : Collect information to allow the generation of a DPACKAGE # file. This directive allows the generation of "Library" # directives within the final DPACKAGE # # This directive does generate the DPACKAGE file. # # Inputs : platform - This does not need to be an active platform # it is simply passed to the DPACKAGE builder # # using - The "using" target # # ... - Arguments for the Library directive # # Returns : # sub DPackageLibrary { JatsDPackage::DPackageAdd ( @_ ); $DPackageDirective = 1; } #------------------------------------------------------------------------------- # Function : SetProjectBase # # Description : Allows the user to modify the build's concept of the Base # of the build. By default the base is the same directory as # the build.pl file, but in some contorted environments it # is a great deal simpler to specify a differnt base. # # The use may use the variable $ProjectBase as a path # specifier to locate files and directories # # Both absolute and relative paths are supported # If the initial value of $ProjectBase is relative then # it will be maintained as a relative path. # # Inputs : elements path to base # These may be: # --Up=xx # name # # Returns : Nothing # # # Allow the user to modify the project base variable # sub SetProjectBase { my $rip = 0; my $path = ""; my $is_relative; Debug("ProjectBase Initial: $ProjectBase, @_"); # # Ensure that the ProjectBase is in a "nice" form # 1) No /./ bits # 2) No trailing / # 3) Not equal to . # 4) No training /. # 5) No // # $ProjectBase =~ s~/\./~/~g; $ProjectBase =~ s~/$~~g; $ProjectBase =~ s~^\.$~~g; $ProjectBase =~ s~/\.$~~g; $ProjectBase =~ s~//$~/~g; # # ProjectBase may be absolute or relative # Determine this before we mess with it # $is_relative = ($ProjectBase !~ m~^/~); # # Process each argument # foreach ( @_ ) { if ( /^--Up=([0-9]*)/ ) { $rip = $1; } elsif ( /^--/ ) { Warning( "SetProjectBase - unknown option \"$_\" - ignored" ); } else { $path = $_; } } # # Process the UP requests # If the tail directory is a ".." then up is done by adding another ".." # If the tail directory is not a "..", then up is done by removing it # # If we go past the start of the path then simply add ".." # while ( $rip-- > 0 ) { Debug2("ProjectBase: $ProjectBase, Up: $rip, IsRel: $is_relative"); # # If ending in a /.. or is exactly equal to .. # Then its a dot-dot and the way to go UP is to append another .. # if ( $ProjectBase =~ m~(/\.\.$)|(^\.\.$)~ ) { $ProjectBase .= '/..'; } else { # # Not a dot-dot ending # Attempt to remove the last directory of the form # /xxxxx # Where the leading / is optional # Note: Must have at least one character in the dirname # This prevents leading / from matching - which is needed # unless ($ProjectBase =~ s~/?[^/]+$~~) { # # Removal failed # If a relative path then we can keep on going up, # otherwise we are dead. # Error ("ProjectBase outside project") unless ($is_relative); $ProjectBase = '..'; } # # Ensure that the leading / in an absolute path is not deleted # $ProjectBase = '/' unless ( $is_relative || $ProjectBase ); } } # # Append the user path # $ProjectBase .= '/' . $path if ( $path ); $ProjectBase = '.' unless ( $ProjectBase ); Debug("ProjectBase set to : $ProjectBase"); # # Once upon a time I tried to convert paths that contained spaces into # short (mangled) names. This was not sucessful because: # 1) Clearcase dynamic views do not support name mangling # 2) Samba file system does not appear to support name mangling # # Spaces in paths are not good for MAKE # Now I simple generate a message # Warning( "ProjectBase contains a space: $ProjectBase") if ( $ProjectBase =~ m/ / ); # # Sanity check # Absolute paths can be checked easily # Checking of relative paths does not appear to work # When I tested it chdir, opendir and stat would limit themselves # and drop into the root directory ( under windows ) # # Solution: Check the path does not extend beyond the file tree # my $distance = 1; my $tpath = $ProjectBase; if ( $is_relative && $tpath ne '.' ) { # # Build up the complete pathname by merging it with the # current directory. Then clean it up. # $tpath = $::Cwd . '/' . $ProjectBase; # # Scan the list of diretories and count the distance from the root # This should not be greater than zero for sanity # Note: Get an empty elemement from the split due to # the leading / of the ABS path # $distance = 0; foreach ( split ('/', $tpath) ) { if ( m~\.\.~ ) { $distance--; } else { $distance++; } } } # # Warn if not a valid directory # Warning( "ProjectBase is not a directory: $ProjectBase") if ( $distance <= 0 || ! -d $tpath ); # # $ProjectBase will always be a valid directory, but if its the top # directory (/) and it is added to a path we will get //path # This is not good, so // will be removed later in the AddIncDir and # AddSrcDir commands where $ProjectBase is really used. # # Alternatively we could set $ProjectBase to an empty string, but then # this may be confused with an empty relative directory # Debug("ProjectBase Final : $ProjectBase"); } #------------------------------------------------------------------------------- # Function : DeployPackage # # Description : Generate a deployed package # This is a gateway to a different packaging system # # DeployPackage and PackageXxxxx directives are mutually # exclusive. Only one person can play in the package area. # # Inputs : Platform Specifier # Package Name (Optional) # Options # --Name : Base name of the package. The default is taken # from the build.pl file # --Dir : Package directory # The default is based on the package name # # Returns : # sub DeployPackage { my( $platforms, @elements ) = @_; my $dir; my $name; # # Flag that this build creates a deployable package, even if its not # active on this platform. # $DEPLOYPACKAGE = 1; Debug2( "DeployPackage($platforms, @elements)" ); return if ( ! ActivePlatform($platforms) ); # # Only allow one use of this directive # Error("DeployPackage can only be used once" ) if ( %DEPLOYPACKAGE ); $DEPLOYPACKAGE = 2; # # Ensure that the deployment file is available # my $command_file = $ScmDeploymentPatch ? "deploypatch.pl" : "deployfile.pl"; Error("DeployPackage: $command_file not found") unless (-f "./$command_file"); # # Collect arguments # foreach (@elements ) { if ( m/^--Dir=(.*)/ ) { Error ("DeployPackage: Package directory defined multiple times") if $dir; $dir = $1; } elsif ( m/^--Name=(.*)/ ) { Error ("DeployPackage: Package name defined multiple times") if $name; $name = $1; } elsif ( m/^--/ ) { Warning( "DeployPackage: Unknown option ignored: $_"); } else { Error ("DeployPackage: Package name defined multiple times") if $name; $name = $_; } } $name = $::ScmBuildPackage unless ( $name ); # # Save the deployment data # $dir = lc($name) unless ( $dir ); $DEPLOYPACKAGE{'name'} = $name; $DEPLOYPACKAGE{'dir'} = $dir; $DEPLOYPACKAGE{'cmdfile'} = $command_file; # # Flag that toolset tests should be supressed # The Deploy world does not really use the full makefiles and if the # compilers are not installed will not be able to create deployment # packages # $ScmNoToolsTest = 1; } ############################################################################### ############################################################################### # Private function section. # The following functions are used INTERNALLY by makelib.pl2. ############################################################################### ############################################################################### # A collection of functions to write to the MAKEFILE handle # # MakeHeader - Write a nice section header # MakeNewLine - Print a new line # MakePrint - Print a line ( without trailing \n) # MakeQuote - Escape \ and " character, then print a line # MakePrintList - Print an array # MakeEntry - Complex line printer # MakePadded - Padded line printer (internal) # PadToPosn - Calc space+tabs to tabstop (internal) # MakeEntry3 - Complex Line Printer # MakeDefEntry - Print a definition line (Production + Debug support) # MakeIfDefEntry - Print ifdef entry # MakeIfnDefEntry - Print ifndef entry # MakeIfZeroEntry - Print ifeq entry # ############################################################################### sub MakeHeader { my ($text, @rest) = @_; my $length = length ($text); print MAKEFILE "\n"; print MAKEFILE "#--------- $text ", '-' x (80 - 12 - $length) ,"\n"; print MAKEFILE "# $_\n" foreach ( @rest ) ; print MAKEFILE "#\n"; } sub MakeNewLine # Print a newline to the current 'Makefile' { print MAKEFILE "\n"; } sub MakePrint # Print to the current 'Makefile' { print MAKEFILE @_ if ( defined $_[0] ); } sub MakeQuote # Quote a makefile text line { my( $line ) = @_; $line =~ s/\\/\\\\/g; # quote all '\' characters $line =~ s/"/\\"/g; # Then quote '"' characters $line =~ s/=#/=\\#/g; # Then quote '=#' sequence print MAKEFILE $line; } sub MakePrintList { print MAKEFILE $_ . "\n" foreach (@{$_[0]}); } sub QuoteArray { my $quote = "'"; if ( @_ ) { return ($quote . join("$quote $quote", @_) . $quote); } return ''; } #------------------------------------------------------------------------------- # Function : MakeEntry # # Description : Build a entry based on the element list # Creates text of the form # $(BINDIR)/prog.exe: object1.obj \ # object2.obj # # # Inputs : $prelim - Preamble (one-off) # $postlim - Postamble (one-off) # $prefix - Pefix (to each element of array) # $postfix - Postfix (to each element of array ) # @elements - Array of element to wrap # # Returns : 1 Always # # Notes: # The above description means that the following entry format is # produced: # # ... # # With judicious use of newline and tab characters, a target # and dependency list along with the command(s) to build the # target can be constructed. # sub MakeEntry { my( $prelim, $postlim, $prefix, $postfix, @elements ) = @_; MakePrint $prelim; MakePrint "${prefix}${_}${postfix}" foreach ( @elements ); MakePrint $postlim if ($postlim); return 1; } #------------------------------------------------------------------------------- # Function : MakePadded # # Description : Generate aligned output of the form # Prefix_text Aligned_text # where the aligned text is at a specified TAB boundary # # Inputs : $align - Tab stop (One tab = 8 chars) # $prefix - Text to print before alignment occurs # @line - Remainder of the line # sub MakePadded # Print to the current 'Makefile', tab aligning { my( $align, $prefix, @line ) = @_; my $strlen = length( $prefix ); my $pad = PadToPosn( $strlen, $align * 8 ); print MAKEFILE $prefix . $pad; print MAKEFILE @line; } #------------------------------------------------------------------------------- # Function : PadToPosn # # Description : Given that we are at $startposn return a tab and space # string to place us at $endposn # sub PadToPosn { my ($startposn, $endposn ) = @_; # # Case where we are already too far into the line # return ( ' ' )if ( $endposn <= $startposn ); my $tcount = 0; my $scount = 0; while ( $startposn < $endposn ) { $tcount ++; $startposn = ($startposn >> 3) * 8 + 8; my $delta = $endposn - $startposn; if ( $delta < 8 ) { $scount = $delta; last; } } return ( "\t" x $tcount . ' ' x $scount ); } #------------------------------------------------------------------------------- # Function : MakeEntry3 # # Description : Build a makefile entry based on the element list, tab aligned # Can creat text of the form: # TAG = NAME0 \ TAG : NAME0 \ # NAME1 NAME1 # # # Inputs : $prelim - Preliminary text # $presep - Preliminary seperator # $elem_ref - Either a single name or a reference to # and array of names, or a hash. # # Returns : Writes directly to the Makefile # sub MakeEntry3 { my( $prelim, $presep, $elem_ref ) = @_; # # The prelim may have some "\n" characters at the start # These simplify formatting, but mess up the nice formatting # if ($prelim =~ m~(^\n+)(.*)~ ) { MakePrint $1; $prelim = $2; } # # Print the definition and the sep with nice padding # MakePadded ( 3, $prelim, $presep ); my $leadin = ' '; # # If a HASH reference then use a sorted list of keys from the hash. # if ( ref ($elem_ref) eq "HASH" ) { my @hash_list; @hash_list = sort keys ( %{$elem_ref} ); $elem_ref = \@hash_list; } # # If the list is only one element long, then create a simple form # If the list is not an array ref, then treat it as a single element # if ( ref ($elem_ref) eq "ARRAY" ) { my $line = 0; foreach my $element ( @$elem_ref ) { print MAKEFILE $leadin . $element; $leadin = " \\\n" . PadToPosn(0,24 + length( $presep ) + 1 ) unless ($line++); } } elsif ( defined $elem_ref ) { print MAKEFILE $leadin . $elem_ref; } MakeNewLine(); return 1; } #------------------------------------------------------------------------------- # Function : MakeDefEntry # # Description : Make a definition entry of the form # # TAG = NAME0 \ # NAME1 # # Support a list of definitions that will always be created # as well as a production and a debug list. # # Will always generate the "TAG = " string, even if the list # is empty. # # Will supress the TAG if there is no data if the FIRST opr starts with a '+' # # Inputs : TAG - Text tag to create # FIRST - First assignement opr. = or += # ALL_LIST - A reference to a list of names to assign # or a single name. # PROD_LIST - Optional list to extend the definition with for a production build # DEBUG_LIST - Optional list to extend the definition with for a debug build # # Returns : Nothing # sub MakeDefEntry { my( $tag, $assign, $all, $prod, $debug ) = @_; # # Do not generate anything if the $opr is "+=" and there is no data # to output. ie: Supress empty TAG += statements # return if ( $assign =~ m/\+/ && ( ref($all) && ! defined $all->[0] ) ); # # TAG for all entries # MakeEntry3( $tag, $assign, $all ); # # TAGs for PROD build # TAGs for DEBUG build # if ( defined $prod && defined $prod->[0] ) { print MAKEFILE 'ifeq "$(DEBUG)" "0"' . "\n"; MakeEntry3( $tag, "+=", $prod ); print MAKEFILE 'endif' . "\n"; } if ( defined $debug && defined $debug->[0] ) { print MAKEFILE 'ifeq "$(DEBUG)" "1"' . "\n"; MakeEntry3( $tag, "+=", $debug ); print MAKEFILE 'endif' . "\n"; } } sub MakeIfDefEntry { my( $iftag, @rest ) = @_; print MAKEFILE "ifdef $iftag\n"; MakeDefEntry (@rest); print MAKEFILE "endif\n\n"; } sub MakeIfnDefEntry { my( $iftag, @rest ) = @_; print MAKEFILE "ifndef $iftag\n"; MakeDefEntry (@rest); print MAKEFILE "endif\n\n"; } sub MakeIfZeroEntry { my( $iftag, @rest ) = @_; print MAKEFILE "ifeq (\$($iftag),0)\n"; MakeDefEntry (@rest); print MAKEFILE "endif\n\n"; } #------------------------------------------------------------------------------- # Function : CreateNameList # # Description : Create a list of names by adding a prefix and suffix to a # list of items. This is used to add a directory prefix and a # file suffix to a list of files. # # Inputs : $prefix ie: '$(OBJDIR)/' # $suffix ie: '.obj' # $elem_ref ie: A list of files ( passed be ref ) # If a Hash then its sorted keys is used # # Returns : A ref to the resulting list # sub CreateNameList { my( $prefix, $suffix, $elem_ref ) = @_; my @result; if ( ref ($elem_ref) eq "HASH" ) { my @hash_list; @hash_list = sort keys ( %{$elem_ref} ); $elem_ref = \@hash_list; } foreach ( @$elem_ref ) { push @result, $prefix . $_ . $suffix; } return \@result; } #------------------------------------------------------------------------------- # Function : ListGeneratedProjects # # Description : Return a list of generated/nongenerated projects # Used in conjunction with CreateNameList # # Inputs : $type - TRUE : Generated # FALSE: Not Generated # # Returns : A reference to a list of projects # undef will be retuend if there are no projects # sub ListGeneratedProjects { my ($type) = @_; my @list; foreach my $project ( @PROJECTS_ORDER ) { if ( exists($PROJECTS{$project}->{'generated'}) xor $type ) { push @list, $project; } } return @list ? \@list : undef; } #------------------------------------------------------------------------------- # Function : ListCleanGenerated # # Description : return a list of generated targets that have 'clean' # operations. This is used in conjunction with CreateNameList # # Inputs : None # # Returns : A list of project indexes, that can be cleaned # sub ListCleanGenerated { my @list; foreach my $i ( @GENERATE_FILES ) { push @list, $i->{'index'} if ( $i->{'clean'} ); } return \@list; } #------------------------------------------------------------------------------- # Function : MakeResolve # # Description : Internal Function # Locate a source file by examining a list of directories # # Don't use directly # Use MakeSrcResolve or MakeSrcResolveExtended # # Inputs : $dirs - Ref to an array of directories to scan # $source - File to locate # # Returns : Resolved path to the file # Will warn if multiple instances of the file are found # sub MakeResolve { my( $dirs, $source ) = @_; my( $first, $count ); # # If the path contains a '$' then its assumed to be # a variable name in the path. Just assume that it exists # return $source if ( $source =~ m#\$# ); # # If the path is absolute or contains a leading ., then don't search # Warn if it can't be found # if ( $source =~ m#^(/|\.)# ) { Warning( "Unable to resolve '$source' path" ) unless -f $source; return $source; } #.. search local path first # $count = 0; $first = ""; $first = "$source" # was ./$source if (-r "$source"); #.. search directory paths # foreach my $dir (@$dirs) { next if ( $dir eq '.' ); my $temp = "$dir/$source"; # was ./$dir/$source Debug2( "MakeResolve: Looking in: $temp" ); if (-r "$temp") { if ($first eq "") { $first = $temp; } else { Warning( "Duplicate '$source' image - '$temp'" ); $count++; } } Debug3( "MakeResolve: $count, $temp" ); } if ($first eq "") { $first = $source; Warning( "Unable to resolve '$source' path" ); } else { Warning( " using '$first'" ) if ($count); } return $first; } #------------------------------------------------------------------------------- # Function : MakeSrcResolve # # Description : Locate a source file by examining the list of source # directories. There are a few frills # # Look for a source file in # 1) %::BUILD_KNOWNFILES # 2) %SRCS # 3) Dirs specified by the array @SRCSDIRS # # Inputs : Name of a file to resolve # # Returns : Resolved path. # Input file - if not found at all # sub MakeSrcResolve { my ($name) = @_; my $file; if ( exists ( $::BUILD_KNOWNFILES{$name} ) ) { # # The Known Files list is relative to ScmRoot # This must be included in the full path # $file = $ScmRoot . '/' . $::BUILD_KNOWNFILES{$name}; } elsif ( exists $SRCS{$name} ) { $file = $SRCS{$name}; } else { $file = MakeResolve( \@SRCDIRS, @_ ) } return $file; } # MakeSrcResolveExtended # from_global = 0 : Search user specified directories # = 1 : Search LinkPkgArchive list # our @PkgSrcDirList; sub MakeSrcResolveExtended { my ( $from_global, $file ) = @_; # # Simple Case. Resolve source from known source directories # # return MakeSrcResolve( $file ) unless ( $from_global ); # # Not so simple Case # Resolve the source from the imported packages # # Create a list of directores to search, but only the first time # - Interface directories - from BuildPkgArchive # - LnkPkgArchive directories # Using target,product,platform include directories # unless ( @PkgSrcDirList ) { for my $entry (@{$::ScmBuildPkgRules{$ScmPlatform} }) { next if ( $entry->{'TYPE'} eq 'build' ); # Ignore BuildPkgArchives for (@{$entry->{'PINCDIRS'}}, @{$entry->{'THXDIRS'}}, '' ) { my $dir = $entry->{'ROOT'} . "/" . $_ ; $dir =~ s~//~/~g; $dir =~ s~/$~~; push ( @PkgSrcDirList, $dir ); } } } return MakeResolve( \@PkgSrcDirList, $file ); } #------------------------------------------------------------------------------- # Function : GetPackageRoot # # Description : Determine the root directory for a given package # This routine is intended for non-standard JATS scripts that # access package contents directly # # Note: This routine does not attempt to handle multiple # instances of a package ( sysbasetypes ). # # Inputs : $pname - Name of the package # # Returns : # sub GetPackageRoot { my( $pname ) = @_; Debug( "GetPackageRoot(@_)" ); my $result = undef; my $pkg = GetPackageEntry( $pname ); if ( $pkg ) { $result = $pkg->{'ROOT'}; Debug( "GetPackageRoot: $result" ); } return $result; } #------------------------------------------------------------------------------- # Function : ActiveProject # # Description : Determine if the specified project is currenly 'active' # # Inputs : $project - one or more project names separated # by either a comma or a colon # # Returns : TRUE if the project is active # sub ActiveProject { my ($project) = @_; foreach ( split( '\s*[:,]\s*', $project ) ) { return 1 if ( $_ eq $::ScmBuildProject ); } return 0; } #------------------------------------------------------------------------------- # Function : ActiveDefine # # Description : Determine if the specified definition is currenly 'active' # # Inputs : $defs - one or more variable names separated # by either a comma or a colon # # Returns : TRUE if any of the definitions are known # sub ActiveDefine { my ($defs) = @_; no strict 'refs'; foreach ( split( '\s*[:,]\s*', $defs ) ) { return 1 if ( defined( $$_ ) || ( @$_ ) ); } use strict 'refs'; return 0; } #------------------------------------------------------------------------------- # Function : ActiveMachType # # Description : Determine if the specified MachType is currenly 'active' # # Inputs : $mtype - one or more machine names separated # by either a comma or a colon # # Returns : TRUE if any of the current MachType is in the list # sub ActiveMachType { my ($mtype) = @_; foreach ( split( '\s*[:,]\s*', $mtype ) ) { return 1 if ( uc($_) eq uc($::GBE_MACHTYPE) ); } return 0; } #------------------------------------------------------------------------------- # Function : ActivePlatform # # Description : Determine if the specified platform is currently 'active' # This is used by all user directives in order to determine # if the directive should be ignored for the current platform # # Inputs : $platform_spec - A platform specifier # This is a bit complex. # # Format of platform_spec. One or more of # PlatformName # AliasName # TargetName,--Target # Special Options (Must all be True) # --Project=ProjectName[:ProjectName]+ # --Defined=SomeValue[:SomeValue]+ # --MachType=SomeValue[:SomeValue]+ # Each can be prefixed with a '!' to negate the test # # Valid options are: # --Target - indicates that the platform is a 'target' # # Returns : TRUE if the platform spec is satisfied # sub ActivePlatform { my( $platform_spec ) = @_; my( @platforms, $scmplatform, $platform ); my( %arguments, @args, $filter ); my @plist; my ($match_count, $count_invert, $count_vert) = (0,0,0); # # Short circuit check # '*' is used so often that it pays to check it first # if ( $platform_spec eq '*' ) { Debug3( " ActivePlatform(@_) = TRUE" ); return 1; } # # Platform specifier may be a comma seperated list # ie: WIN32,MOS,XXX # Extract non-platform arguments # Process to yield a dummy platform of '0' or '1' - these will be seen later # foreach ( split( '\s*,\s*', $platform_spec ) ) { my ($result, $not); if ( m~^(!?)--Project=(.+)~ ) { $not = $1; $result = ActiveProject($2); } elsif ( m~^(!?)--Defined=(.+)~ ) { $not = $1; $result = ActiveDefine($2); } elsif ( m~^(!?)--MachType=(.+)~ ) { $not = $1; $result = ActiveMachType($2); } else { # # Must be a platform argument # Add to a list # push @platforms, $_; next; } # # Continue processing non-platform arguments # Each one must be TRUE, allowing for negation. # $result = $result ? 1 : 0; $result = ! $result if ( $not ); return 0 unless ( $result ); } # # If we have no platforms then the test was purely non-platform arguments. # # if ($platform_spec ne '' && ! @platforms) { return 1; } # Platform specified may be an Alias # Perform alias expansion # @platforms = ExpandPlatforms( @platforms ); # aliasing Debug3( " ActivePlatform(@_)" ); # Debug0( " Platforms(@platforms)", "PLIST(@plist)" ); #.. Arguments # At this point we have a list of platforms and arguments # Build up a hash of arguments for each platform being parsed # Multiple arguments can follow a platform name # Arguments apply to the preceeding platform name # $platform = undef; foreach ( @platforms ) { if ( /^--Target/ ) { # Arguments if ( $platform ) { $arguments{$platform}{'Target'} = 1; } else { Warning ("No Platform preceding platform option: $_"); } } elsif ( /^--Only(Prod)|(Debug)/ || /--board=/ ) { # Known arguments # Bit of a kludge. Must be a better way } elsif ( /^--/ ) { Warning ("Unknown platform option: $_"); } else { # Target $platform = $_; push @plist, $platform; } } #.. Scan the expression # $scmplatform = uc( $ScmPlatform ); # current platform foreach ( @plist ) { $platform = uc( Trim( $_ ) ); # trim white and convert case # # Determine filter comparison # Either a Platform or a Target # if ( $arguments{$platform}{'Target'} ) { $filter = uc( $ScmTarget ); } else { $filter = $scmplatform; # filter specification } Debug3( " Platform=$platform, Filter=$filter" ); # # Examine platform names # Allow negation of name (!), but all the names must be negated # as a mix does not make sense. # ie: !P1,!P2,!P3 - All except P1,P2 or P3 # P1,P2,P2 - Only P1,P2,P3 # my $invert = 0; if ( substr($platform, 0, 1) eq '!' ) { $count_invert++; $platform = substr($platform, 1); } else { $count_vert++; } $match_count++ if ( $platform eq '' || $platform eq '*' || $platform eq '1' || $platform eq $filter ); } # # Sanity test # Force failure on bad sanity # if ( $count_vert && $count_invert ) { Warning( "Platform expression makes no sense. Mixed use of '!' operator", "Expression: @_" ); return 0; } # # Test for pass # If not using '!', then any match passes : P1 or P2 or P3 # If are using '!', then any match is bad : !P1 and !P2 and !P3 == !(P1 or P2 or P3) # if ( ( $count_vert && $match_count ) || ( $count_invert && ( not $match_count) ) ) { Debug3( " ActivePlatform(@_ == $ScmPlatform) = TRUE" ); return 1; } Debug3( " ActivePlatform(@_ == $ScmPlatform) = FALSE" ); return 0; } #------------------------------------------------------------------------------- # Function : If # # Description : Function for embedding arguments in directives # To be used within another directive # ie: # AnyDirective ('*', arg1, arg2, ... # If (SomePlatform, arg1, .. ,argn)) # # Inputs : $platform - Active Platform speciifier # @args - Args # # Returns : @args or nothing # sub If { my $platform = shift; return @_ if ( ActivePlatform( $platform )); return; } #------------------------------------------------------------------------------- # Function : RegisterMakefileGenerate # # Description : Register a function to be called at the start of the # makefile generation process # # Inputs : $fname - Name of the function # $args - Function Arguments # # Returns : Nothing # our @MF_Generators; sub RegisterMakefileGenerate { my ($fref) = @_; my $rtype = ref($fref) || 'not a reference'; Error ("RegisterMakefileGenerate called incorrectly", "First argument MUST be a code reference", "It is a $rtype" ) unless ( $rtype eq 'CODE' ); # # Save the arguments by reference in an array # The array will be processed later # push @MF_Generators, \@_; } #------------------------------------------------------------------------------- # Function : RegisterSrcHook # # Description : Register a function to be called when a source file is # declared # # Inputs : $ext - Extension of interest # '*' will be used by all # $fname - Name of the function # $args - Function Arguments # # Returns : Nothing # sub RegisterSrcHook { my $ext = shift; my ($fref) = @_; my $rtype = ref($fref) || 'not a reference'; Error ("RegisterSrcHook called incorrectly", "Second argument MUST be a code reference", "It is a $rtype" ) unless ( $rtype eq 'CODE' ); # # Save the arguments by reference in an array # The array will be processed later # push @{$MF_RegisterSrcHooks{$ext}}, \@_; } #------------------------------------------------------------------------------- # Function : MakefileHeader # # Description: : Generate a "standard" makefile header. # #.. sub MakefileHeader { my ($file, $desc, $by, @trailing) = @_; my ($diff); $diff = 0 if (($diff = ((80-5) - length($desc))) < 0); $desc .= ' ' . ('-' x $diff); print $file <Preprocess(); # # If we have supressed the Toolset use, then we need to sanity test # the use of the toolset # if ( $ScmNoToolsTest ) { ReportError ("Building programs not supported with --NoToolset") if ( @PROGS || @TESTPROGS ); ReportError ("Building libraries not supported with --NoToolset") if ( @LIBS || @MLIBS || @SHLIBS ); ReportError ("Building projects not supported with --NoToolset") if ( %PROJECTS ); ErrorDoExit(); } # # Auto package the 'descpkg' file # If this makefile packages any files, then it can also package the descpkg file # The descpkg will be piggybacked into all makefiles that do a package # if ( %PACKAGE_FILES || %PACKAGE_HDRS || %PACKAGE_CLSS || %PACKAGE_LIBS || %PACKAGE_SHLIBS || %PACKAGE_PROGS ) { Src ('*', 'descpkg') unless ($SRCS{ descpkg }); PackageFile ('*', 'descpkg'); } # # Some toolsets NEED a relative root # Note: At the moment ScmRoot is relative anyway, thus this code # does nothing # my $gbe_root = $::ScmRoot; if ( $::UseRelativeRoot ) { $gbe_root = RelPath( $::ScmRoot ); } # # Now start to create the makefile # ::ToolsetFile ($Makefile); open( MAKEFILE, '>', $Makefile ) || Error( "Cannot create $Makefile" ); ::MakefileHeader( *MAKEFILE, 'Auto-generated Platform Dependent Makefile', "$ScmMakelib (version $ScmVersion)", "# Copyright (c) VIX TECHNOLOGY (AUST) LTD", '#', "# Located in $::Cwd", "# Platform $::ScmPlatform", '#' . ('-' x 79), ); # # Ensure that some essential variables are set # print MAKEFILE <{$tag} ) { $pSeen->{$tag} = 1; push @{$pList}, $data; } } } # # Search paths for include files and libraries # Currently symbolic paths are created, but there is very little need for this # sub MakePaths { my( $root ) = @_; my @pathlist; my %seen; # # Interface and Local directories are now treated quite # diffently. The Local is under our control. # if ( $root =~ /INTERFACE/ ) { # eg. SCA_WIN32 and SCA_WIN32 and SCA_SOLARIS # SCA WIN32_i386 SPARC # WIN32 SCA SCA # . WIN32 SOLARIS # . . #.. # # For Interface Path List use the 'PARTS' as calculated within # the build file. Its good enough for everthing else in JATS # foreach ( @{$::BUILDINFO{$ScmPlatform}{PARTS}} ) { PushPath( \@pathlist, \%seen, "$root/$_", $_ ); } } else { # # Local directory # The user can (should) only install stuff into a few known # locations. # foreach ( $ScmPlatform, $ScmProduct, $ScmTarget ) { PushPath( \@pathlist, \%seen, "$root/$_", $_ ); } } # # Add OS Common Directory ### May need more testing before being added # #if ( exists($::BUILDINFO{$ScmPlatform}{OS_COMMON}) ) #{ # my $os_common = $::BUILDINFO{$ScmPlatform}{OS_COMMON}; # PushPath( \@pathlist, \%seen, "$root/$os_common", $os_common ); #} # # Add the root directory too # push @pathlist, "$root"; return \@pathlist; } #------------------------------------------------------------------------------- # # MakeHeader ("Include Search Paths", "Package Include Paths for header files and libraries" ); MakeDefEntry( 'PINCDIRS', '=', '# includes'); MakeDefEntry( 'PLIBDIRS', '=', '# libraries'); for my $package (@{$::ScmBuildPkgRules{$ScmPlatform} }) { # # Skip the pseudo package that encapsulates the interface # directory. Currently the makefiles do this in a different # manner - to be resolved # # Just comment out the lines so that the data is visible # Its a hint to make use of the data # my $prefix = ''; $prefix = '# ' if ( $package->{'TYPE'} eq 'interface' ); $prefix = '# ' if ( $package->{'TYPE'} eq 'build' ); my $name = $package->{'NAME'} . '/' . $package->{'VERSION'}; my $base = $package->{'ROOT'}; my @doc; push (@doc, "From: $base"); push (@doc, 'BuildPkgArchive via Interface' )if $package->{'TYPE'} eq 'build' ; push (@doc, 'Is Interface' ) if $package->{'TYPE'} eq 'interface' ; MakeHeader ("Source: $name", @doc); # # List include and library directories # Note: Need the True Path for windows. # Some makefile functions (wildcard) only work as expected # if the case of the pathname is correct. Really only a problem # with badly formed legecy packages where the Windows user # guessed at the package format. # # The conversion to a TruePath is done when ScmBuildPkgRules # is created. Create one, use many time. # # for my $type (qw (PINCDIRS PLIBDIRS) ) { for my $path ( @{$package->{$type}} ) { MakeDefEntry ( "$prefix$type", "+=", $base . $path); } } } #------------------------------------------------------------------------------- # # MakeHeader ("Include Search Paths", "Local Include Paths", " LINKDIRS - Local include search path (short)", " INCDIRS - Include search path (complete)", " NODEPDIRS - ", " SRCDIRS - ", " LIBDIRS - Library search path", ); # Include search path # # user-local # local # interface # BuildPkgArchive # LinkPkgArchive # user-global # MakeDefEntry ( "\nLINCDIRS", "= ", \@L_INCDIRS ); # .. Local MakeDefEntry ( "LINCDIRS", "+=", MakePaths( '$(INCDIR_LOCAL)' )); # .. Sandbox interface MakeDefEntry ( "LINCDIRS", "+=", MakePaths( '$(INCDIR_INTERFACE)' )); # .. Sandbox interface MakeDefEntry ( "LINCDIRS", "+=", \@G_INCDIRS ); # .. Global MakeDefEntry ( "INCDIRS", "= ", '$(LINCDIRS)' ); # Local MakeDefEntry ( "INCDIRS", "+=", '$(PINCDIRS)' ); # Package MakeDefEntry ( "LINCDIRS", "+=", \@S_INCDIRS ); # System # Source search path MakeDefEntry( "\nNODEPDIRS", "=", \@NODEPDIRS ); MakeDefEntry( "\nSRCDIRS","= " , [ @L_SRCDIRS, @G_SRCDIRS ] ); # Local MakeDefEntry ( "SRCDIRS", "+=" , '$(PINCDIRS)' ); # Package MakeDefEntry ( "SRCDIRS", "+=" , \@S_INCDIRS ); # System # Library search path # # user-local # local # interface # BuildPkgArchive # LinkPkgArchive # user-global MakeDefEntry( "\nLIBDIRS", "= ", '$(LIBDIR)' ); # User Local MakeDefEntry( "LIBDIRS", "+=", \@L_LIBDIRS ); # Local MakeDefEntry( "LIBDIRS", "+=", MakePaths( '$(LIBDIR_LOCAL)' )); # Sandbox/interface MakeDefEntry( "LIBDIRS", "+=", MakePaths( '$(LIBDIR_INTERFACE)' )); MakeDefEntry( "LIBDIRS", "+=", \@G_LIBDIRS ); # Global MakeDefEntry( "LIBDIRS", "+=", '$(PLIBDIRS)' ); # Package MakeDefEntry( "LIBDIRS", "+=", \@S_LIBDIRS ); # System #------------------------------------------------------------------------------- # # Subdir creation and deletion # Creation is done on the fly # Deletion is done AFTER the toolset functions have been invoked to create the # build artifacts so that the toolsets can create directories too MakeHeader ("Subdir creation"); CreateMkdirRules(); MkdirRule( '$(OBJDIR)', 'OBJDIR', '--Extra=depend,depend.err' ); # Object build directory MkdirRule( '$(OBJDIR)/'.$_ ) foreach (@SHLIBS); # Shared library build directory MkdirRule( '$(LIBDIR)', 'LIBDIR' ); # Library directory MkdirRule( '$(BINDIR)', 'BINDIR' ); # Binary directory # # Create a directory for library merge tool to work within # MkdirRule( "\$(MLIBDIR)", 'MLIBDIR', '--Path=$(GBE_PLATFORM).MRG', '--RemoveAll' ) if (@MLIBS); #------------------------------------------------------------------------------- # Generate rules and recipes to create all the toolset specific parts # This is done fairly early to allow the toolsets to extend various # definitions that may be used later in the makefile construction # MakeHeader ("Construct Programs"); foreach my $i ( @PROGS ) { my $pProg = $PROGS->Get($i); my $pArgs = $pProg->getItems('ARGS'); my $pObjs = $pProg->getItems('OBJS'); my $pLibs = $pProg->getItems('LIBS'); # # Create a list of program object files # push @PROGOBJS, @$pObjs; MakePrint( "#---- (${i})\n\n" ); if ( $ScmToolsetProgDependancies ) { # # Original style Prog Interface # Write some dependency information here and some in the toolset # Problems: # 1) Errors in library dependency generation will be # reported after all the object files have been created # Thus the error message and the make-stop are seperated # by many,many lines of output. This makes it difficult # to see the error. # # 2) Lack of Flexability # MakeEntry( "\$(BINDIR)/$i$::exe: ", "", "\\\n\t\t", ".$::o ", @$pObjs ); } else { # # New Style Prog Interface # The toolset does it all # # Flag the progam packaging as a placeholder. # The toolset will replace/update it. # PackageProgRemoveFiles( $i ); } $if->LD ( $i, $pArgs, $pObjs, $pLibs ); $if->LDLINT( $i, $pArgs, $pObjs, $pLibs ); } #------------------------------------------------------------------------------- # # MakeHeader ("Construct Test Programs"); foreach my $i ( @TESTPROGS ) { my $pProg = $TESTPROGS->Get($i); my $pArgs = $pProg->getItems('ARGS'); my $pObjs = $pProg->getItems('OBJS'); my $pLibs = $pProg->getItems('LIBS'); # # Create a list of program object files # push @TESTPROGOBJS, @$pObjs; MakePrint( "#---- (${i})\n\n" ); if ( $ScmToolsetProgDependancies ) { MakeEntry( "\$(BINDIR)/$i$::exe: ", "", "\\\n\t\t", ".$::o ", @$pObjs ); } else { PackageProgRemoveFiles( $i ); } $if->LD ( $i, $pArgs, $pObjs, $pLibs ); $if->LDLINT( $i, $pArgs, $pObjs, $pLibs ); } #------------------------------------------------------------------------------- # # MakeHeader ("Transfer Scripts to BINDIR"); foreach my $i ( sort ( values %SCRIPTS )) { my $tname = "\$(BINDIR)/" . StripDir( $i ); MakePrint( "$i:\t\tmakefile.pl\n" . "\t\$(XX_PRE)if [ ! -f \"$i\" ]; then echo Script [$i] not found; exit 2; fi\n\n" ); # # Create a rule to copy the script into the BIN directory # Mark the script as executable - It can't hurt and its there # to be run as part of a test. # MakePrint "$tname:\t\$(GBE_BINDIR) $i\n" . "\t\$(XX_PRE)\$(cp) -f $i $tname\n" . "\t\$(XX_PRE)\$(chmod) -f +wx $tname\n\n" } #------------------------------------------------------------------------------- # # MakeHeader ("Construct Libraries"); foreach my $i ( @LIBS ) { my $pLib = $LIBS->Get($i); my $pArgs = $pLib->getItems('ARGS'); my $pObjs = $pLib->getItems('OBJS'); unless ( $ScmToolsetNillLibSrc ) { Error ("Library has no component objects: $i") if ( scalar @$pObjs <= 0 ); } MakePrint "#---- (${i})\n\n"; $if->AR( $i, $pArgs, $pObjs, $pLib); $if->ARLINT( $i, $pArgs, $pObjs, $pLib ); } #------------------------------------------------------------------------------- # # MakeHeader ("Construct Merged Libraries"); sub MlibEntry { my( $mlib, $plib, $pLibs ) = @_; my @flib; MakePrint '$(LIBDIR)/' . GenLibName($mlib) . ":"; foreach my $lib ( @$pLibs ) { # # Each library name may contains one embedded option which # specifies the source directory # libname[,--Option | BaseSubdir] # my ($slib, $sdir) = split( ',', $lib ); my $mode; # # By default the librares are pulled from LOCAL unless the # library is built in this directory, in which case it will # be used. # $sdir = ( $LIBS->Get($slib) ) ? '--Here' : '--Local' unless ( $sdir ); # # --Interface - Pull library from the interface directory # --Local - Pull library from the local directory # --SubDir=xxxx - Pull library from specified subdirectory # --Here - Pull from local directory if built locally # otherwise - Pull library from specified subdirectory # if ($sdir eq '--Interface') { $sdir = '$(LIBDIR_INTERFACE)/$(GBE_PLATFORM)'; } elsif ($sdir eq '--InterfacePlain') { $sdir = '$(LIBDIR_INTERFACE)/$(GBE_PLATFORM)'; $mode = 1; } elsif ( $sdir eq '--Local') { $sdir = $PackageInfo{'Lib'}{'IBase'} . # Base of Installed libs $PackageInfo{'Lib'}{'Dir'}; # Default subdir } elsif ( $sdir =~ m~^--SubDir=(.*)~ ) { $sdir = $1 . '/$(LIBDIR)'; } elsif ( $sdir eq '--Here') { $sdir = '$(LIBDIR)'; } else { $sdir .= '/$(LIBDIR)'; } MakePrint " \\\n\t\t${sdir}/" . GenLibName($slib, $mode); push @flib, "${sdir}/${slib}"; } return \@flib; } foreach my $i ( @MLIBS ) { my $pLib = $MLIBS->Get($i); my $pArgs = $pLib->getItems('ARGS'); my $pLibs = $pLib->getItems('LIBS'); MakePrint "#---- (${i})\n\n"; unless ( defined &ToolsetARMerge ) { Warning( "Merging of libraries not supported in this toolset yet" ); Warning( "MergeLibrary: \"$i\" will not be created" ); } else { # # Create the dependency rule # Target library : source library list # Recipe - generated by the toolset # foreach ( @$pArgs ) { Warning( "Ignoring unknown argument to MergeLibrary. $_" ); } $pLibs = MlibEntry( $i, $pLib, $pLibs ); $if->ARMerge( $i, $pArgs, $pLibs, $pLib ); } } #------------------------------------------------------------------------------- # # MakeHeader ("Construct Shared Libraries"); foreach my $i ( @SHLIBS ) { my $pShlib = $SHLIBS->Get($i); my $pArgs = $pShlib->getItems('ARGS'); my $pObjs = $pShlib->getItems('OBJS'); my $pLibs = $pShlib->getItems('LIBS'); my $version = $pShlib->{VERSION}; $if->SHLD ( $i, $pArgs, $pObjs, $pLibs, $version ); $if->SHLDLINT( $i, $pArgs, $pObjs, $pLibs, $version ); } #------------------------------------------------------------------------------- # Construct Objects # For each object within OBJSOURCE construct the following: # # $(OBJDIR)/object-name: source-name [makefile] # Toolset ... # # # MakeHeader ("Construct Objects"); foreach my $i ( sort keys %OBJSOURCE ) { my( $src, $sname, $ext, $type, @args ); $src = $OBJSOURCE{ $i }; $sname = StripDir( $src ); $ext = StripFile( $src ); $ext = lc($ext) if ( $::ScmHost ne "Unix" ); $type = ($ScmSourceTypes{ $ext } || '') unless (( $type = $SRC_TYPE{ $sname }) ); # # Object source is an object file # No need the generate the object, just create makefile rule # [ddp] Not too sure how we get here # if ( $ext eq ".$::o" ) { MakePrint "$src:"; MakePrint " \$(SCM_MAKEFILE)"; MakeNewLine(); next; } # # Need to create object file # @args = split( /$;/, $SRC_ARGS{ StripDir( $sname ) } ) if $SRC_ARGS{ $sname }; push( @args, "--Shared" ) if ( exists $SHOBJ_LIB{$i} ); # # Convert relative paths to absolute paths if required by the # toolset. Some compilers need ABS paths to generate nice debug # information. # $src = AbsPath($src) if ( $UseAbsObjects ); # # Extract any user specified dependancies # These will be added to the dependency list # my @dlist; @dlist = split( /$;/, $SRC_DEPEND{$sname} ) if ( exists $SRC_DEPEND{$sname} ); # # Create the dependency part of the object rule # The source file MUST be the first dependent recipes # may assume that $< is the name source file # MakeEntry( "\$(OBJDIR)/$i.$::o: $src \$(SCM_MAKEFILE)", "", " \\\n\t", "", @dlist ); if ( $type eq ".c" ) { $if->CC( $src, $i, \@args ); } elsif ( $type eq ".cc" ) { $if->CXX( $src, $i, \@args ); } elsif ( $type eq ".asm" ) { $if->AS( $src, $i, \@args ); } else { $if->EXT( $src, $i, \@args ) || Warning( "Don't know how to build '$ext' images' for $src, $i" ); MakeNewLine(); } } #------------------------------------------------------------------------------- # Construct Projects # Construct toolset specific projects # MakeHeader ("Construct Projects"); while ( my($project, $entry) = each %PROJECTS) { $if->PROJECT( $entry ); } #------------------------------------------------------------------------------- # Automated tests # MakeHeader ("Automated tests"); my $idx = 0; my @copy_set = (); foreach my $pEntry ( @TESTS_TO_RUN ) { # Foreach test $idx++; $pEntry->{'index'} = $idx; $pEntry->{'test_name'} = "run_test_$idx"; $pEntry->{'utfname'} = $pEntry->{'test_name'} unless defined $pEntry->{'utfname'}; # # If the test is being run within a 'FrameWork' then the underlying # toolset must instantiate the frame work. # # This may change. Perhaps frameworks shouldn't be a part of the # toolset. Perhaps they should be standalone. May change # if ( $pEntry->{framework} ) { $if->TESTFRAMEWORK( $pEntry ); } # # Create a rule to run the test # my $tdir_alias = $pEntry->{'testdir'}; my $tdir = '$(' . $tdir_alias . ')'; my $test_name = $pEntry->{'test_name'}; push @TESTPROJECT_TO_URUN, $test_name unless ($pEntry->{'auto'} ); push @TESTPROJECT_TO_ARUN, $test_name if ($pEntry->{'auto'} ); my $tprog = $tdir . '/' . StripDir( $pEntry->{'prog'} ); my $me = MakeEntry::New( *MAKEFILE, $test_name, '--Phony' ); # # Export GBE_UTFNAME for the duration of the test # $me->AddDefn('export GBE_UTFNAME', $pEntry->{'utfname'}); $me->AddDefn('export GBE_UTFUID', '$(MAKEFILEUID)' . '_' . $pEntry->{'index'}); $me->AddDependancy( "\$(GBE_$tdir_alias)" ); $me->AddDependancy( "\$(INTERFACEDIR)/set_$::ScmPlatform.sh" ); $me->AddDependancy( $tprog ) if $pEntry->{'copyprog'}; $me->AddDependancy( @{ $pEntry->{'copyin' } } ); $me->AddDependancy( map { $tdir . '/' . StripDir($_) } @{ $pEntry->{'copyonce' } } ); $me->AddDependancy( @{ $pEntry->{'preq'} } ); $me->RecipePrefix ('$(XX_PRE)'); $me->RecipeComment( "------ Running test [$idx] ..." ); # # Extend the PATH seen by the script to include the local/bin directory # Allows programs and tests that have been created elsewhere in the component # to be accessed within the script. # $me->AddShellRecipe ( ". \$(INTERFACEDIR)/set_$::ScmPlatform.sh" ); # # Copy in the files that we need # foreach my $file ( @{$pEntry->{'copyin'}} ) { my $dst = $tdir . '/' . StripDir( $file ); UniquePush( \@COPYIN, $dst ); UniquePush( \@copy_set, $file ); $me->AddShellRecipe ( "\$(cp) -f $file $dst" ); $me->AddShellRecipe ( "\$(chmod) -f +wx $dst" ); } # # Insert any FrameWork Recipe bits # $me->AddShellRecipe ( @{$pEntry->{'ShellRecipe'}} ); # # Insert command # If we could run a UTF filter, then ignore the commands return code # $me->AddShellRecipe ( "cd $tdir" ); $me->AddShellRecipe ( ["GBE_TYPE=\$(GBE_TYPE)", "GBE_HOST=\$(GBE_HOST)", "GBE_ROOT=\$(GBE_ROOT_ABS)", "PATH=.\\$::ScmPathSep\$(BINDIR_LOCAL_PATH)\\$::ScmPathSep\$\$PATH", $pEntry->{'command'}, @{$pEntry->{'args'}}, $pEntry->{'utfformat' } ? '|| true' : '' ] ); if ($pEntry->{'utfformat' }) { # # Create the basic command line for 'jats_runutf' # my @cmdline; push @cmdline, '--'; push @cmdline, '$(VERBOSE_OPT)'; push @cmdline, "-filter=$pEntry->{utfformat}"; push @cmdline, '-root=$(GBE_ROOT_ABS)' ; push @cmdline, "-dir=$tdir"; push @cmdline, '-target=$(GBE_PLATFORM)'; push @cmdline, '-pkgdir=$(PKGDIR)'; push @cmdline, '-local=$(LOCALDIR)'; push @cmdline, '-interface=$(INTERFACEDIR)'; foreach my $entry (@{$pEntry->{'utfargs' }}) { push @cmdline, '-arg=' . $entry; } # # Insert commands to post process the test results according to the specified formatter # $me->NewSection (); $me->SectionDef ('UTF_POSTPROCESS'); $me->RecipePrefix ('$(XX_PRE)'); $me->AddRecipe ( "\$(GBE_PERL) -Mjats_runutf -e processUtf " . join(" \\\n\t\t\t", @cmdline) ); } $me->Print(); # # Create entries to handle the copy-once files # foreach my $file ( @{ $pEntry->{'copyonce' } } ) { my $tname = $tdir . '/' . StripDir($file); my $me = MakeEntry::New( *MAKEFILE, $tname ); $me->AddDependancy( $file ); $me->AddRecipe ( "\$(call CopyFile,CopyIn,$tname,$file,$tdir,)" ); $me->Print(); UniquePush( \@COPYIN, $tname ); UniquePush( \@copy_set, $file ); } } # # Generate sanity test for each copyin script # Simply to provide a nice error message for generated scripts # that do not exist at run-time # test_copy_in: foreach my $i ( @copy_set ) { next if ( $SCRIPTS{$i} ); foreach ( @SHLIB_TARGETS ) { next test_copy_in if ( $i eq $_ ); } MakePrint( "\n$i:\t\tmakefile.pl\n" . "\t\@if [ ! -f \"$i\" ]; then echo ERROR: CopyIn Script [$i] not found; exit 2; fi\n" ); } #------------------------------------------------------------------------------- # Deploy rules # MakeHeader ("Deploy Rules"); print MAKEFILE <{'index'}; # # If predelete is enabled, then create a list of files to delete # if ( $i->{'predelete'} ) { MakeDefEntry( "generate_gen_$gen_tag", "=", $i->{'gen'} ); MakePrint("\n") } # # Generate the basic generate rule and recipe # together with the prerequisites # MakeEntry ( "", ":", "", " ", @{$i->{'gen'}} ); unless ( $i->{'clean'} && $i->{'shell'} ) { MakeEntry ( "", "", " \\\n\t\t", "", @{$i->{'preq'}} ); MakeEntry ( "", "", " \\\n\t\t", "", "phony_generate" ) if $i->{'preq_sus'}; MakeEntry ( "", "", " \\\n\t\t", "", "\$(SCM_MAKEFILE)" ); MakePrint ("\n\t" . "\@\$(echo) [$i->{'text'}] generating.." ); if ( $i->{'predelete'} ) { MakePrint ("\n\t" . "\$(XX_PRE)\$(call RmFiles,generate_gen_$gen_tag)" ); } MakePrint ("\n\t" . "\$(XX_PRE)\$(call generate_$gen_tag,)" ); } # # Generate 'clean' rules and recipes # if ( $i->{'clean'} ) { MakePrint ("\n\nPHONY: clean_generate_$gen_tag" ); MakePrint ("\nclean_generate_$gen_tag:" ); MakePrint ("\n\t" . "\$(XX_PRE)-\$(call generate_$gen_tag,$i->{'clean'})" ); } # # Define a function to contain the body of the generation call # The first argument will be a 'clean' argument # MakePrint ("\n\ndefine generate_$gen_tag" ); if ( $i->{'shell'} ) { MakeEntry ("\n\t(" , "\\\n\t)\n", " \\\n\t", ";" , @{$i->{'toolargs'}} ); } else { MakeEntry ("\n\t" . $i->{'tool'} . ' $1', "\n", " \\\n\t\t", "" , @{$i->{'toolargs'}} ); } MakePrint ("endef\n\n" ); } #------------------------------------------------------------------------------- # Toolset Post Processing # Allow the toolset to perform any post processing, before we finally write # out any definitions. # # We will not interprete any more user directives, but new stuff may get added # # MakeHeader ("Toolset Post Processing"); $if->Postprocess(); ################################################################################ # All interactions with the toolset are now complete # All lists are now complete # # Can now create internal definitions # ################################################################################ # # Would be nice if this would work # Unfortunatelty we still need $if for the CCDEPENDS and CTAGS work # These must be defined AFTER the definitions # # Ideally we should construct our makefile in sections # and then we can order the sections when we write them out # #$if = 0; # Ensure the MakeIf class is not called # If this file is modified #------------------------------------------------------------------------------- # Sources # MakeHeader ( "Sources"); MakeDefEntry( "CSRCS", "=", \@CSRCS ); MakeDefEntry( "CXXSRCS", "=", \@CXXSRCS ); MakeDefEntry( "ASSRCS", "=", \@ASSRCS ); #------------------------------------------------------------------------------- # Generated, Installed and Packaged components # MakeHeader ("Generated, Installed and Packaged components"); MakeDefEntry( "INITS", "=", \@INITS ) if ( @INITS ); MakeDefEntry( "GENERATED", "=", \@GENERATED ) if ( @GENERATED ); MakeDefEntry( "GENERATED_NOTSRC","=", \@GENERATED_NOTSRC ) if ( @GENERATED_NOTSRC ); MakeDefEntry( "GENERATEDCLEAN", "=", CreateNameList( 'clean_generate_', '', ListCleanGenerated() )); MakeDefEntry( "INSTALL_HDRS", "=", \%INSTALL_HDRS ) if ( %INSTALL_HDRS ); MakeDefEntry( "INSTALL_CLSS", "=", \%INSTALL_CLSS ) if ( %INSTALL_CLSS ); MakeDefEntry( "OBJS", "=", CreateNameList( '$(OBJDIR)/', ".$::o", \@OBJS) ); MakeDefEntry( "SHOBJS", "=", CreateNameList( '$(OBJDIR)/', ".$::o", \%SHOBJ_LIB )); MakeDefEntry( "PROGOBJS", "=", CreateNameList( '', ".$::o", \@PROGOBJS )); MakeDefEntry( "TESTPROGOBJS", "=", CreateNameList( '', ".$::o", \@TESTPROGOBJS )); MakeDefEntry( "LIBS", "=", $LIBS->AllTargets() ) if ($::a); MakeDefEntry( "MLIBS", "=", $MLIBS->AllTargets() ) if ($::a); MakeDefEntry( "SHNAMES", "=", \@SHLIBS ); MakeDefEntry( "SHDIRS", "=", CreateNameList( '$(OBJDIR)/', "", \@SHLIBS )); MakeDefEntry( "SHLIBS", "=", \@SHLIB_TARGETS ); MakeDefEntry( "SCRIPTS", "=", CreateNameList( '$(BINDIR)/', "", \%SCRIPTS )); MakeDefEntry( "COPYIN", "=", \@COPYIN ); MakeDefEntry( "PROGS", "=", $PROGS->AllTargets() ); MakeDefEntry( "PROGS_EXTRA", "=", \@PROGS_EXTRA ); MakeDefEntry( "TESTPROGS", "=", $TESTPROGS->AllTargets()); MakeDefEntry( "LINTLIBS", "=", CreateNameList( 'lib_', '_lint', \@LINTLIBS )); MakeDefEntry( "LINTSHLIBS", "=", CreateNameList( 'shlib_', '_lint', \@LINTSHLIBS )); MakeDefEntry( "LINTPROGS", "=", CreateNameList( 'prog_', '_lint', \@PROGS )); MakeDefEntry( "LINTPROGS", "+=", CreateNameList( 'prog_', '_lint', \@TESTPROGS )); MakeDefEntry( "PROJECTS", "=", CreateNameList( 'Project_', '', ListGeneratedProjects(1) )); MakeDefEntry( "PROJECTSGEN", "=", CreateNameList( 'Project_', '', ListGeneratedProjects(0) )); MakeDefEntry( "PROJECTSCLEAN", "=", CreateNameList( 'ProjectClean_', '', \%PROJECTS )); MakeDefEntry( "UNITTESTS", "=", \@TESTPROJECT_TO_URUN ); MakeDefEntry( "AUTOUNITTESTS", "=", \@TESTPROJECT_TO_ARUN ); MakeDefEntry( "AUTOUNITTESTS_PRE", "=", \@TOOLSET_UTF_PRE ); MakeDefEntry( "AUTOUNITTESTS_POST", "=", \@TOOLSET_UTF_POST ); MakeDefEntry( "AUTOUNITTESTS_COLLATE","=", \@TOOLSET_UTF_COLLATE ); MakeHeader ("Toolset components"); MakeDefEntry( "USERGENERATED", "=", \@USERGENERATED ) if ( @USERGENERATED ); MakeDefEntry( "TOOLSETGENERATED", "=", \@TOOLSETGENERATED ) if ( @TOOLSETGENERATED ); MakeDefEntry( "TOOLSETOBJS", "=", \@TOOLSETOBJS ) if ( @TOOLSETOBJS ); MakeDefEntry( "TOOLSETLIBS", "=", \@TOOLSETLIBS ) if ( @TOOLSETLIBS ); MakeDefEntry( "TOOLSETPROGS", "=", \@TOOLSETPROGS ) if ( @TOOLSETPROGS ); MakeDefEntry( "TOOLSETDIRS", "=", \@TOOLSETDIRS ) if ( @TOOLSETDIRS ); MakeDefEntry( "TOOLSETDIRTREES", "=", \@TOOLSETDIRTREES ) if ( @TOOLSETDIRTREES ); #--------- Determine compiler flag groups to use ---------------------------- # # Allows the compiler options to be controlled for both the debug and # the production builds. Allows control over # 1) Optimisations # 2) Debug Information # MakeHeader ("Determine compiler flag groups to use"); print MAKEFILE <CTAGS() if (@CSRCS || @CXXSRCS); #------------------------------------------------------------------------------- # Depend # If we are build C or C++ source files then create rules and recipes # to invoke a dependency generator. # # NODEPEND is used to disable, at make-time, the dependency generation # and inclusion process. # # MakeHeader ("Depend"); if ($::o && (@CSRCS || @CXXSRCS)) { $ScmDependTags = 1; print MAKEFILE <CCDepend( "\$(OBJDIR)/depend", "\$(CSRCS)" ) if ( @CSRCS ); $if->CXXDepend( "\$(OBJDIR)/depend", "\$(CXXSRCS)" ) if ( @CXXSRCS ); MakePrint "\t-\@\$(touch) -f \$(OBJDIR)/depend\n"; print MAKEFILE <{$element}{'placekeeper'} ); # # Prepend any prerequisites (once) # if ( $prereq ) { MakePrint " \\\n\t${prereq}"; $prereq = 0; } MakePrint " \\\n\t${element}"; } MakePrint "\n\n"; } InstallTarget( "install_hdr", \%INSTALL_HDRS ); InstallTarget( "install_lib", \%INSTALL_LIBS, 'make_mlib' ); InstallTarget( "make_install_shlib",\%INSTALL_SHLIBS, '', "@shlibdep" ); InstallTarget( "install_prog", \%INSTALL_PROGS, 'make_script' ); InstallTarget( "install_class", \%INSTALL_CLSS ); InstallTarget( "package_files", \%PACKAGE_FILES ); InstallTarget( "package_hdr", \%PACKAGE_HDRS ); InstallTarget( "package_lib", \%PACKAGE_LIBS ); InstallTarget( "package_shlib", \%PACKAGE_SHLIBS ); InstallTarget( "package_prog", \%PACKAGE_PROGS, 'make_script' ); InstallTarget( "package_class", \%PACKAGE_CLSS ); #------------------------------------------------------------------------------- # Installations MakeHeader ("Installations"); PackageRule ( \&InstallCmd, \%INSTALL_HDRS ); PackageRule ( \&InstallCmd, \%INSTALL_CLSS ); PackageRule ( \&InstallCmd, \%INSTALL_LIBS ); PackageRule ( \&InstallCmd, \%INSTALL_SHLIBS ); PackageRule ( \&InstallCmd, \%INSTALL_PROGS ); #------------------------------------------------------------------------------- # Packaging # MakeHeader ("Packaging"); PackageRule ( \&PackageCmd, \%PACKAGE_FILES ); PackageRule ( \&PackageCmd, \%PACKAGE_HDRS ); PackageRule ( \&PackageCmd, \%PACKAGE_CLSS ); PackageRule ( \&PackageCmd, \%PACKAGE_LIBS ); PackageRule ( \&PackageCmd, \%PACKAGE_SHLIBS ); PackageRule ( \&PackageCmd, \%PACKAGE_PROGS ); #------------------------------------------------------------------------------- # Uninstall/unpackaging # MakeHeader ("Uninstall/unpackaging"); UnpackageRule( "uninstall_hdr", \&UninstallCmd, \%INSTALL_HDRS ); UnpackageRule( "uninstall_lib", \&UninstallCmd, \%INSTALL_LIBS ); UnpackageRule( "uninstall_shlib", \&UninstallCmd, \%INSTALL_SHLIBS ); UnpackageRule( "uninstall_prog", \&UninstallCmd, \%INSTALL_PROGS ); UnpackageRule( "uninstall_class", \&UninstallCmd, \%INSTALL_CLSS ); UnpackageRule( "unpackage_files", \&UnpackageCmd, \%PACKAGE_FILES ); UnpackageRule( "unpackage_hdr", \&UnpackageCmd, \%PACKAGE_HDRS ); UnpackageRule( "unpackage_lib", \&UnpackageCmd, \%PACKAGE_LIBS ); UnpackageRule( "unpackage_shlib", \&UnpackageCmd, \%PACKAGE_SHLIBS ); UnpackageRule( "unpackage_prog", \&UnpackageCmd, \%PACKAGE_PROGS ); UnpackageRule( "unpackage_class", \&UnpackageCmd, \%PACKAGE_CLSS ); #------------------------------------------------------------------------------- # Distribution Sets # MakeHeader ("Distribution Sets"); PackageSetRules(); #------------------------------------------------------------------------------- # # Subdir deletion # This is done AFTER the toolset functions have been invoked to create the # build artifacts so that the toolsets can create directories too # # Note: Toolset directories are deleted first # Note: User Directories are deleted in the reverse order of creation # # Add them into the directory data structure # foreach my $path ( @TOOLSETDIRS ) { MkdirRule( $path, '', '--NoCreate' ); } foreach my $path ( @TOOLSETDIRTREES ) { MkdirRule( $path, '', '--NoCreate' , '--RemoveAll'); } MakeHeader ("Subdir deletion"); RmdirRules(); MakeNewLine(); #--------- Toolset Rules ------------------------------------------------------- MakeHeader ("Toolset Rules"); MakePrintList ( \@TOOLSETRULES ); #--------- Maketags ------------------------------------------------------------ Maketag( "make_init", @INITS ); Maketag( "make_dir", @mkdirdep ); Maketag( "generate", @generatedep || @projectgendep || @USERGENERATED || ($ScmToolsetGenerate != 0) ); Maketag( "depend", $ScmDependTags != 0 ); Maketag( "make_lib", @libdep ); Maketag( "make_mlib", @mlibdep ); Maketag( "make_install_shlib", %INSTALL_SHLIBS || @shlibdep); Maketag( "make_script", @scriptdep ); Maketag( "make_prog", @progdep || @projectdep ); Maketag( "make_test", @testprogdep ); Maketag( "exec_tests", $TESTS_TO_RUN || @TESTPROJECT_TO_URUN || $TESTS_TO_AUTORUN || @TESTPROJECT_TO_ARUN ); Maketag( "exec_unit_tests", $TESTS_TO_AUTORUN || @TESTPROJECT_TO_ARUN ); Maketag( "process_tests", @TOOLSET_UTF_PRE || @TOOLSET_UTF_POST || @TOOLSET_UTF_COLLATE); Maketag( "install_hdr", %INSTALL_HDRS ); Maketag( "install_class", %INSTALL_CLSS ); Maketag( "install_lib", %INSTALL_LIBS ); Maketag( "install_prog", %INSTALL_PROGS ); Maketag( "deploy", %DEPLOYPACKAGE ); Maketag( "package", %PACKAGE_FILES || %PACKAGE_HDRS || %PACKAGE_CLSS || %PACKAGE_LIBS || %PACKAGE_SHLIBS || %PACKAGE_PROGS ); # # Display tags in the MAKEFILE # Not used here - just for show # MakeHeader ("Maketags"); foreach my $tag ( sort keys %MakeTags ) { MakePadded( 3, "# $tag:", '1', "\n"); } #------------------------------------------------------------------------------- # End of Makefile # MakeHeader ("End of Makefile"); close( MAKEFILE ); # # Save all platform information # Done after the makefile is written as toolsets can extend the data # WriteParsedConfig(); # # Write out any accumulated DPACKAGE data # JatsDPackage::DPackageSave(); return 0; } #------------------------------------------------------------------------------- # Function : QuoteForMake # # Description : Escape/Quote a pathname for make # Allow files with a $ in the name # Allow files with a space in the name # Allow files with a comma in the name # Allow for paths that have make-varible prefixes # $(GBE_...)/ # as these may be generated internally # # Must also allow $(GBE_TYPE) in the remainder # # Inputs : uarg - Arg to quote # # Returns : Quoted arg # sub QuoteForMake() { my ($uarg) = @_; # # Split into two # $(xxx)/ - Makefile variables # Remainder - Stuff to quote # $uarg =~ m~^((\$\(.*?\)/)*)(.*)~; my $prefix = defined $1 ? $1 : ''; my $arg = defined $3 ? $3 : ''; $arg =~ s~\$(?!\(GBE_)~\$\$~g; # $, not followed by (GBE_ - id not $(GBE_ $arg =~ s~ ~\\ ~g; $arg =~ s~,~\$(comma)~g; $arg =~ s~%~\\%~g; return $prefix . $arg; } #------------------------------------------------------------------------------- # Function : Maketag # # Description : Create Makefile tags to speed up recursive makes # # Inputs : tag_name # dep # # Returns : # sub Maketag { my( $tag, $dep ) = @_; $MakeTags{$tag} = 1 if ( defined($dep) && $dep ); } #------------------------------------------------------------------------------- # Function to create and delete directories within the build system # # To stop make regenerating directory dependent targets each time the # directory content is modified, rule should only be dependent on a internally # created alias file 'gbedir', which represents the time a dir was created not # last modified. # # Must use tags like GBE_BINDIR, GBE_LIBDIR and GBE_OBJDIR to ensure that the # directories are created correctly. # my %MkdirRuleData; my @MkdirRuleOrder; my $MkdirRulePrinting = 0; my $MkdirRuleGbeFile = ( $::ScmHost eq "Unix" ) ? ".gbedir" : "_gbedir"; #------------------------------------------------------------------------------- # Function : MkdirRule # # Description : Create Rules and Recipes to create a directory at make-time # Mark the information for such that the directories will # be deleted in a 'clean' # # Can be called before we start writing the makefile # Such entries will be retained and dumped at a known time # # Inputs : $subdir - Symbolic name of the subdir $(OBJDIR) # $alias - Optional script alias for the dir 'OBJDIR' --> GBE_OBJDIR # Options: # --Path=path Optional value of $subdir '$(GBE_PLATFORM)$(GBE_TYPE).OBJ' # --RemoveAll Remove all files on clean # --Extra=file[,file] Additiona files to remove # --NoCreate Do not Create the Directory, just delete it # # Returns : Nothing # sub MkdirRule { my( $subdir, $alias, @opts ) = @_; # # Create data entry once # $alias =~ s~^GBE_~~ if $alias; unless ( $MkdirRuleData{$subdir} ) { my %data; # # Parse options # foreach ( @opts ) { if ( /^--Path=(.+)/ ) { $data{path} = $1; } elsif ( /^--RemoveAll/ ) { $data{remove_all} = 1; } elsif ( /^--NoCreate/ ) { $data{noCreate} = 1; } elsif ( /^--Extra=(.+)/ ) { @{$data{extraFiles}} = split(/,/, $1); } else { Error ("MkdirRule: Unknown option: $_"); } } $data{alias} = $alias if ( $alias ); $MkdirRuleData{$subdir} = \%data; push @MkdirRuleOrder, $subdir; } # # Save or print # return unless ( $MkdirRulePrinting ); return if ( $MkdirRuleData{$subdir}{noCreate} ); # # Create a definition of the physical directory # my $path = $MkdirRuleData{$subdir}{path}; MakePadded (2, $alias, ":= $path\n") if ( $path && $alias ); # Create an alias to be used within rules # The defined aliase will be prefixed with 'GBE_' # MakePadded (2, "GBE_$alias", ":= $subdir/$MkdirRuleGbeFile\n") if ( $alias ); # # Create a recipe to create the directory # This is not as simple as it sounds # The touch is required. # Had 'timestamp' issues on solaris'. The 'echo' did not appear # to be enough. Perhaps the output was not flushed # MakePadded (2, "$subdir", ": $subdir/$MkdirRuleGbeFile\n"); MakePrint "$subdir/$MkdirRuleGbeFile:\n". "\t\$(XX_PRE)if [ ! -d $subdir ]; then \$(mkdir) -p $subdir; fi; \\\n". "\t\$(echo) '# DO NOT REMOVE.' > \$@; \\\n". "\t\$(touch) \$@\n\n"; } #------------------------------------------------------------------------------- # Function : RmdirRules # # Description : Create the body of a recipe to delete the directories that # have been created. # # Use JatsFileUtil rather than shell script # Faster under windows (and others) # Solved long pathname issues # Simpler to use and control # # Inputs : Uses $MkdirRuleData # # Returns : Nothing. # Prints to the makefile # sub RmdirRules { MakePrint( ".PHONY:\tunmake_dir\n" ); MakePrint( "unmake_dir:\n" ); # # Determine the list of directories to delete # Sort such that subdirs are deleted first # my $txt = 'Removing directories'; foreach my $subdir ( reverse sort keys %MkdirRuleData ) { my @args = $subdir; push (@args, $MkdirRuleGbeFile, 'core', '*.bak', '*.tmp', '*.err') unless $MkdirRuleData{$subdir}{remove_all}; push (@args, @{$MkdirRuleData{$subdir}{extraFiles}}) if ( $MkdirRuleData{$subdir}{extraFiles} ); my $mode = $MkdirRuleData{$subdir}{remove_all} ? 'T0' : 'D0'; MakePrint ("\t-\$(AA_PRE)JatsFileUtil ", QuoteArray( $mode, $txt, @args ), "\n"); $txt = ''; } } #------------------------------------------------------------------------------- # Function : CreateMkdirRules # # Description : Create Rules to make dirs at runtime # This function is called to instantiate those entries # That have been requested before the makefile has has # started to be created. # # Once this function has been called all new MkdirRule calls # will result in the recipes being created in-line. # # Inputs : Nothing # # Returns : Even Less # # sub CreateMkdirRules { $MkdirRulePrinting = 1; foreach my $subdir ( @MkdirRuleOrder ) { my $data = $MkdirRuleData{$subdir}; MkdirRule($subdir, $data->{alias}, $data->{path} ); } } #------------------------------------------------------------------------------- # Function : PackageRule # # Description : Generate rules and recipes to "install" and "package" files # # Inputs : codecmd - A code reference to the actual installer routine # hashp - A reference to a INSTALL or PACKAGE hash # # hashp is a reference to a hash # The key is the full path of the install target # The value is (another) hash that describes the install options # # Valid keys are: # src - Path of the source file [Mandatory] # dir - Target directory [Mandatory] # # defined - Copy the file only if value is defined # Exists - Copy the file only if it exists # exe - Mark the file as executable # Mode - File modes. Default is -w # placekeeper - Marks SHARED library placekeepers # set - Distribution sets # type - Copy the file in DEBUG or PROD mode # Valid values are "D" or "P" # version - Shared library version information # RemoveOnly - Do not install the file. Entries are # created to allow the removal of the file # NoTarget - Reserved - Implemented elsewhere # # Returns : # sub PackageRule { my ($codecmd, $hashp) = @_; foreach my $dest ( keys %{$hashp} ) { my $entry = $hashp->{$dest}; # # Skip placekeepers # next if ( $entry->{'placekeeper'} ); # # Some entries are not installed via this mechanism, but can be removed # if they exist. Mark these as PHONY to keep targets happy # if ( $entry->{'RemoveOnly'} ) { MakePrint ".PHONY:\t$dest\n"; MakePrint "$dest:\n\n"; next; } my $fname = $entry->{'src'}; my $fmode = $entry->{'Mode'}; $fmode .= "+x" if ( $entry->{'exe'} ); # # User conditionional # Mark both the source and the target as PHONY if the condition is not met # This will ensure that the target need not be built. # my $udef = $entry->{'defined'}; if ( $udef ) { MakePrint "ifndef $udef \n"; MakePrint ".PHONY:\t\t$dest\n"; MakePrint ".PHONY:\t\t$fname\n"; MakePrint "$dest:\n"; MakePrint "else\n" } # # File exists # Only package the file if it has been generated. ie: .exe.manifest # my $fexist = $entry->{'Exists'}; if ($fexist) { MakePrint "ifeq (\"\$(wildcard $fname)\",\"\")\n"; MakePrint ".PHONY:\t\t$dest\n"; MakePrint "$dest:\n"; MakePrint "else\n" } # # Conditional installation for DEBUG/PRODUCTION # my $type = $entry->{'type'}; if ( $type ) { if ( $type eq "D" ) { MakePrint 'ifeq "$(DEBUG)" "0"'."\n"; } elsif ( $type eq "P" ) { MakePrint 'ifneq "$(DEBUG)" "0"'."\n"; } else { Error("INTERNAL: Unexpected packaging type: $type"); } MakePrint ".PHONY:\t\t$dest\n"; MakePrint "$dest:\n"; MakePrint "else\n" } # # The body of the copy # MakePadded( 4, "$dest:" ); MakePrint "\t$fname\n"; MakePrint $codecmd->( $dest, $fname, $fmode ); MakeNewLine(); # # Unwind conditionals # MakePrint "endif\n" if ( $type ); MakePrint "endif\n" if ( $fexist ); MakePrint "endif\n" if ( $udef ); # # Distribution sets # my $dist = $entry->{'set'}; if ( $dist ) { foreach my $set ( split( ',', $dist ) ) { push @{$PACKAGE_SETS{$set}{LIST}}, $dest; } MakeNewLine(); } } } #------------------------------------------------------------------------------- # Function : PackageSetRules # # Description : Generate the packageset rules # These appear to be a now-defuct feature # # By default all packaged files are a part of package_setALL # # Inputs : None # Takes data from %PACKAGE_SET # # Returns : Nothing # sub PackageSetRules { foreach my $set ( sort keys %PACKAGE_SETS ) { my $me = MakeEntry::New( *MAKEFILE, "package_set$set", '--Phony' ); $me->AddDependancy( @{$PACKAGE_SETS{$set}{LIST}} ); $me->Print(); } } #------------------------------------------------------------------------------- # Function : UnPackageRule # # Description : Generate rules and recipes to "uninstall" and "unpackage" files # # Inputs : target - Name of the target # codecmd - A code reference to the actual installer routine # hashp - A reference to a INSTALL or PACKAGE hash # # Returns : # sub UnpackageRule { my ($target, $codecmd, $hashp) = @_; MakePrint ".PHONY:\t\t"."$target\n"; MakePrint "$target:\t"; foreach my $dest ( sort keys %{$hashp} ) { my $entry = $hashp->{$dest}; # # Skip placekeepers # next if ( $entry->{'placekeeper'} ); MakePrint "\n" . $codecmd->($dest); } MakePrint "\n\n"; } # # Internal macro interface, see RULE.STD for definitions: # sub RmFilesCmd { my ( $list ) = @_; return "\$(call RmFiles,$list)"; } sub InstallCmd { my( $dest, $file, $fmode ) = @_; $fmode = "-w" # default, read-only if ( !defined( $fmode ) || $fmode eq "" ); return "\t\$(call InstallFile,$dest,$file,$fmode)"; } sub UninstallCmd { my( $file ) = @_; return "\t\$(call UninstallFile,$file)"; } sub PackageCmd { my( $dest, $file, $fmode ) = @_; $fmode = "-w" # default, read-only if ( !defined( $fmode ) || $fmode eq "" ); return "\t\$(call PackageFile,$dest,$file,$fmode)"; } sub UnpackageCmd { my( $file ) = @_; return "\t\$(call UnpackageFile,$file)"; } 1;