Rev 2449 | Blame | Compare with Previous | Last modification | View Log | RSS feed
# -*- mode: perl; tabs: 8; indent-width: 4; show-tabs: yes; -*-# Copyright (C) 1998-2004 ERG Transit Systems, All rights reserved## Module name : Makelib.pl# Module type : Makefile system## Description:# This modules builds the primary makefile for each directory,# used in conjunction with Makelib.pl2. The produced makefile# acts as a frontend to platform builds.## Notes:## *** DO NOT DETAB ***## Beware the use of space v's tab characters within the# makefile generation sessions.##.........................................................................#require 5.006_001;use strict;use warnings;use Getopt::Long;use JatsError;use JatsEnv;use JatsMakeInfo qw(:create);our $MakelibVersion = "2.33"; # makelib.pl versionour $ScmRoot;our $ScmSrcDir = "";our $ScmMakelib = "";our @ScmDepends = ();our $ScmExpert = 0;our $ScmAll = 0;our $ProjectBase = ""; # Base of the user's projectour $ScmInterface = "interface";our @SUBDIRS = ();our @PLATFORMS = ();our %PLATFORMARGS = ();our @DEFINES = "";our @RULES = ();our %PACKAGE_DIST = ();our $ROOTMAKEFILE = 0;our $PLATFORMINCLUDED = 0;our @BUILDPLATFORMS = ();our $GBE_CONFIG;our $GBE_TOOLS;our $GBE_PERL;#.. Running under 'buildlib.pl' ?# This will be the 'require .../makelib.pl' that is present in# the build.pl files. Yes, its overly complicated, but it was# the way it was done.#unless ( $::ScmBuildlib ){MakeLibInit();}sub MakeLibInit{#.. Test environment#EnvImport( "GBE_BIN" );EnvImport( "GBE_PERL" );EnvImport( "GBE_TOOLS" );EnvImport( "GBE_CONFIG" );EnvImport( "GBE_MACHTYPE" );#.. Common stuff#require "$::GBE_TOOLS/common.pl";CommonInit( "makelib " );Debug( "version: $MakelibVersion" );#.. Parse command line# makefile.pl rootdir Makelib.pl [options ...]#Verbose ("Command Line: @ARGV");my $opt_help = 0;my $result = GetOptions ("help+" => \$opt_help,"interface=s" => \$::ScmInterface,);MLUsage() if ( $opt_help || !$result );## Needs 2 command line arguments#$::ScmRoot = StripDrive( ${ARGV[0]} );$::ScmRoot = '/./' if ( $::ScmRoot eq '/' ); # Prevent leading '//'$::ProjectBase= $::ScmRoot;$::ScmMakelib = ${ARGV[1]};Debug( "ARGV: @ARGV" );Debug( "Root: $::ScmRoot" );Debug( "Makelib: $::ScmMakelib" );#.. Get the stuff from the platform definition file#ConfigLoad();#.. Get the stuff from the package definition file#require "$::ScmRoot/package.pl"if ( -f "$::ScmRoot/package.pl" );}# MLUsage ---# Makelib command line usage.#..sub MLUsage{Error ( "Usage: perl makefile.pl <ROOTDIR> <makelib.pl> [options ...]","Valid options:"," --help Display Help"," --interface=name Set interface directory",);}#-------------------------------------------------------------------------------# Function : SubDir## Description : Recurse into the specified sub directories# This is one of the very few directives in a 'makefile.pl'# that is processed by this script - all the others are# processed by makelib.pl2.## This directive MUST appear before the 'Platform' directive## Inputs : List of sub directories to visit## Returns : Nothing#sub SubDir{my( @NewDirs );Debug( "SubDir(@_)" );Error ("Directive 'SubDir' not allowed in this context") if ( $::ScmBuildlib );## Support different constructs:# 'dir1 dir2'# 'dir1','dir2'#@NewDirs = map { split /\s+/ } @_;@NewDirs = grep { defined $_ } @NewDirs;foreach my $ThisDir ( @NewDirs ){$ThisDir =~ s~/+$~~;Warning ("SubDir contains a '\\' character: $ThisDir" )if ( $ThisDir =~ m~\\~);if ( grep /^$ThisDir$/, @::SUBDIRS ){Warning( "Duplicate SubDir '$ThisDir' -- ignored." );next;}if ( ! ( -e $ThisDir and -d $ThisDir ) ){Error( "SubDir(): Subdirectory not found: '$ThisDir'","Current directory: $::Cwd" );}if ( ! -f $ThisDir . '/makefile.pl' ){Error( "SubDir(): makefile.pl not found in subdirectory: '$ThisDir'","Current directory: $::Cwd" );}push(@::SUBDIRS, $ThisDir);}}#-------------------------------------------------------------------------------# Function : RootMakefile## Description : This function is called from buildlib.pl prior to the# generation of the root makefile. The Root Makefile is# different to the others in this it does not have any platform# specific makefiles associated with it. It is simply used to# invoke the makefile in the 'src' subdirectory## Inputs : None## Returns : Nothing#sub RootMakefile{Error ("Directive 'RootMakefile' not allowed in this context") if ( $::ScmBuildlib );$::ROOTMAKEFILE = 1;}sub PackageDist{my( $name, @elements ) = @_;Error ("Directive 'PackageDist' not allowed in this context") if ( $::ScmBuildlib );foreach ( @elements ) {HashJoin( \%::PACKAGE_DIST, $;, $name, "$_" );}}sub Define{Error ("Directive 'Define' not allowed in this context") if ( $::ScmBuildlib );push( @::DEFINES, @_ );}sub Defines{my( $path, $script ) = @_;my( $line );Error ("Directive 'Defines' not allowed in this context") if ( $::ScmBuildlib );$script = Exists( $path, $script, "Defines" );open( my $fh, '<' ,$script ) ||Error( "cannot open '$script'" );while (<$fh>) {$_ =~ s/\s*(\n|$)//; # kill trailing whitespace & nlpush( @::DEFINES, $_ );}push( @::ScmDepends, "$script" ); # makefile dependenciesclose( $fh );}sub Rule{Error ("Directive 'Rule' not allowed in this context") if ( $::ScmBuildlib );push( @::RULES, @_ );}sub Rules{Error ("Directive 'Rules' not allowed in this context") if ( $::ScmBuildlib );my( $path, $script ) = @_;my( $line );$script = Exists( $path, $script, "Rules" );open( my $fh, '<' ,$script ) ||Error( "cannot open '$script'" );while (<$fh>) {$_ =~ s/\s*(\n|$)//; # kill trailing whitespace & nlpush( @::RULES, $_ );}push( @::ScmDepends, "$script" ); # makefile dependenciesclose( $fh );}#-------------------------------------------------------------------------------# Function : Platform## Description : Within "makelib.pl" the Platform directive is processed# in such a manner as to trigger the verification and# generation of Makefile and xxxxx.mk files in the tree# below the makefile.pl## Inputs : A list of platforms for which the body of the makefile.pl# are to be processed + GBE_PLATFORM## "*", ... - A wildcard for all platforms# Options include# --Targets (Not Products)# --Products# --NoPlatformBuilds# !xxx - Exclude a platform## "--NoPlatformBuilds"## "xxx"[, "yyy"]* - A list of platforms and aliases## "!xxx" - A platform to be excluded## Returns :#sub Platform{my ( @platforms ) = @_;my $exitVal;my $platform;my $noplatforms = 0;my $defplatforms = 0;my %platforms;Debug( "Platform(@_)" );Error ("Directive 'Platform' not allowed in this context") if ( $::ScmBuildlib );## Import user platform specification (GBE_PLATFORM)# Note: This is also used by the Makefile and it cannot be# alias expanded or sanitised. Its not really really useful## Only use GBE_PLATFORM if the user has not specified to build ALL makefiles.#my %filter;my $filter_present = 0;if ( $::ScmAll == 0 ){$filter{GENERIC} = 1;foreach ( split( ' ', $ENV{ "GBE_PLATFORM" } || '' ) ){$filter{$_} = 1;$filter_present = 1;}}## Expand out the directive platform list (and optional arguments)# Expand wildcards and aliases## Handle format# Platform ('*', '--Options', [!Name]);# Platform ('--NoPlatformBuilds' );# Platform ('Name', '--Options', [!Name] );# Platform ('!Name' );##if ( $platforms[0] && $platforms[0] eq '*' ){my( $targets, $products ); # optionsmy( @args );my( @exclude );$targets = $products = 0;foreach ( @platforms ) {next if ( /^\*$/);next if ( /^--Targets$/ && ($targets = 1));next if ( /^--Products$/ && ($products = 1));next if ( /^--NoPlatformBuilds/ && ($noplatforms = 1));next if ( /^--/ && push @args, $_ );next if ( /^!/ && push @exclude, $_ );Warning( "Platform: unknown option $_ -- ignored\n" );}## Determine the list of platforms to expand the '*' into# The result may be modified by optional arguments# --Targets # Expands to all targets# --Products # Expands to all products# OtherWise # Expands to all build platforms#@platforms = (); # zap list# 'all' platformspush( @platforms, @::DEFBUILDPLATFORMS )unless ( $targets | $products );## Expand the '*' into a list of platforms that are NOT products#if ( $targets && ( %::ScmBuildPlatforms ) ){ # targetsforeach my $key (keys %::ScmBuildPlatforms) {push( @platforms, $key )if (! ( %::ScmBuildProducts ) ||! scalar $::ScmBuildProducts{ $key } );}}## Expand the '*' into a list of platforms that are 'products'#if ( $products && ( %::ScmBuildProducts ) ){ # productsforeach my $key (keys %::ScmBuildProducts) {push( @platforms, $key );}}## Distribute arguments over all expanded platforms#if ( @args ){my @baseplatforms;foreach ( @platforms ){push @baseplatforms, $_, @args;}@platforms = @baseplatforms;}## Place the excluded platforms at the end of the list#push @platforms, ExpandPlatforms( @exclude );}elsif ( scalar @platforms == 1 && $platforms[0] eq "--NoPlatformBuilds" ){ # short-cut$noplatforms = 1;@platforms = @::DEFBUILDPLATFORMS;}else{ # aliasing@platforms = ExpandPlatforms( @platforms );## Process excluded platform lists# Migrate excluded platforms to the end of the list#my (@include, @exclude);foreach ( @platforms ){next if ( m/^!/ && push @exclude, $_ );push @include, $_;}## If no included platforms have been found then assume that the# list is an exclusion list and seed the platform list with# a set of defualt platforms - like '*'##@include = @::DEFBUILDPLATFORMS unless @include;@platforms = ( @include, @exclude );}$platform = ""; # current platform## Process the directives expanded list of platforms### Generate a HASH of lowercase known platform names# This will be used to quickly validate platform names#my %lc_platforms;foreach ( @::BUILDPLATFORMS ){$lc_platforms{ lc($_) } = $_;}FILTER:foreach $_ ( @platforms ){if ( ! /^--(.*)/ ){$_ =~ s/^\s*//g; # leading white space$_ =~ s/\s*(\n|$)//; # trailing white space## Remove specific platforms from the list#$defplatforms = 1;if ( m/!(.*)/ ){Verbose( "Excluded platform removed: $1" );delete $platforms{$1};next FILTER;}if ( exists $platforms{$_} ){Warning( "duplicate platform '$_' -- ignored." );$platform = "";next FILTER;}## validate 'platform'# Allow the user to have a bad case match ( Fred == fred == FRED )#my $lc_platform = lc($_);unless ( exists( $lc_platforms{$lc_platform} ) ){Warning( "Platform '$_' not contained within BuildPlatforms -- ignored." )unless ( exists( $::ScmBuildPlatforms{$_} ));$platform = "";next FILTER;}$lc_platform = $lc_platforms{$lc_platform};if ( $_ ne $lc_platform ){Warning( "Mixed case usage of platform '$_' -- corrected." );$_ = $lc_platform;}# filter 'platform'if ( $filter_present ){if ( ! exists $filter{$_} ){Verbose( "GBE_PLATFORM override $_ -- ignored" );$platform = "";next FILTER;}}## Platform not filtered out - must be using it.#Verbose( "Platform ... $_" );$platforms{$_} = 1; # Add to platform list$platform = $_; # new platform}elsif ( /^--NoPlatformBuilds$/ ){$noplatforms = 1;}elsif ( $platform ne "" ){ # other argumentsVerbose( " .. $_" );HashUniqueJoin( \%::PLATFORMARGS, $; , $platform, $1 ) ||Warning( "Duplicate argument '$platform=$_' -- ignored." );}}## Sort the platforms to ensure that the ordering is consistient throughout#@::PLATFORMS = sort keys %platforms;## Trap makefiles that don't have any platforms# The user 'should' mark these as --NoPlatformBuilds#unless ( @::PLATFORMS ){Warning( "No platform definitions." )unless( $noplatforms || $defplatforms);$noplatforms = 1;}#.. Common rules#my( $file ) = Require( "$::GBE_CONFIG", "Rules", "Common rules " );push( @::ScmDepends, "$file" );#.. Generate primary makefile#$exitVal = Generate( $noplatforms || $::ROOTMAKEFILE );if ($::ROOTMAKEFILE == 1){Warning ("WARNING: Problem generating Makefile ($exitVal)")if ($exitVal);return ($exitVal);}## Not creating the ROOT Makefile# Exit the phase. All the work has been done#Debug( "Platform ExitVal: $exitVal" );exit( $exitVal );}################################################################################ Private function section.# The following functions are used INTERNALLY by makelib.pl.#################################################################################-------------------------------------------------------------------------------# Function : Generate## Description : Build makefiles ...# Creates the command data files and the per-target .MK files## Inputs : $noplatforms - 1 if this makefile does not have# any platforms. Also set for ROOTMAKEFILE## Returns : $exitVal#sub Generate{my( $noplatforms ) = @_;my $exitVal = 0;#.. Build all the per-taget makefiles, if any exist#$exitVal = GeneratePlatforms()unless ($noplatforms );#.. Build the common Makefile.gbe#GenerateMakefile( $noplatforms );return $exitVal;}#-------------------------------------------------------------------------------# Function : GeneratePlatforms## Description : Generate all the per-taget makefiles# Create all .mk files in the current directory## Cleanup of unused .mk files is done when the WriteCommonInfo# is processed. Makefiles that are no longer used will be purged## Inputs : None## Returns : exitVal#sub GeneratePlatforms{my $exitVal = 0;my $exitVal2;foreach my $platform ( @::PLATFORMS ){my @CmdLine;push @CmdLine, $::GBE_PERL, $0, $::ScmRoot;push @CmdLine, $::ScmMakelib . "2";push @CmdLine, $platform;push @CmdLine, "--interface=$::ScmInterface" if ( $::ScmInterface ne "" );## Insert platform local arguments#if ($::PLATFORMARGS{ $platform }){foreach my $arg (split( /$;/, $::PLATFORMARGS{ $platform } )) {push @CmdLine,"--arg=$arg";}}## Invoke the command# Don't use a Shell. Don't need the overhead#$exitVal2 = System( '--NoShell', @CmdLine );## Warn on any error# Overall return code will be set if any platform fails, but we# will process all of them first#if ( $exitVal2 ){Warning ("Problem generating $platform.mk in $::Cwd ($exitVal2)");$exitVal = $exitVal2;}}return $exitVal;}#-------------------------------------------------------------------------------# Function : GenerateMakefile## Description : Generate the non-platform specific makefile# Once upon a time this was a real makefile# Now its a simple file and a database as this offeres greater# flexability.## The file, "Makefile.gbe" contains enough info to get to the# interface directory.## The "database" is held in the interface directory as# Makfile.cfg - index to locate Makefile_nn.cfg# Makefile_nn.cfg - data from a makefile.pl## Inputs : $noplatforms - 1: no platform makefiles in this dir## Returns : Nothing#sub GenerateMakefile{my( $noplatforms ) = @_;my %platform_info;## Determine targets that are limited to either debug or production builds# This information may come from several places#foreach my $key ( @PLATFORMS ){my @args;UniquePush (\@args, split ( /$;/, $::PLATFORMARGS{$key} ))if ($::PLATFORMARGS{$key});UniquePush (\@args, map { s/^--//; $_} split ( /$;/, $::ScmBuildPlatforms{$key} ))if($::ScmBuildPlatforms{$key});my $hasOnlyProd = grep /^OnlyProd/, @args;my $hasOnlyDebug = grep /^OnlyDebug/, @args;if ( $hasOnlyProd && $hasOnlyDebug ){Warning ("Target \"$key\" has both OnlyDebug and OnlyProd options","Both options ignored. Target will be built" );$hasOnlyProd = $hasOnlyDebug = 0;}$platform_info{$key}{prod} = 1 unless $hasOnlyDebug;$platform_info{$key}{debug} = 1 unless $hasOnlyProd;}## Update common information in the Makefile_x.cfg file#WriteCommonInfo( \@::SUBDIRS, \%platform_info, $noplatforms, $::ROOTMAKEFILE );##.. Maintain local information in Makefile.gbe# This allows us to locate the interface directory#Message "[Control] $::Cwd";CreateMakeInfo();}1;