Rev 227 | Rev 255 | Go to most recent revision | 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.6.1;use strict;use warnings;use JatsEnv;use JatsMakeInfo qw(:create);our $MakelibVersion = "2.33"; # makelib.pl versionour $Generated = 10; # Generated error code ..our $ScmRoot = "";our $ScmSrcDir = "";our $ScmMakelib = "";our @ScmDepends = ();our $ScmForce = 0;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 = ();#.. Running under 'buildlib.pl' ?#unless ( $::ScmBuildlib ){MakeLibInit();}sub MakeLibInit{my ( $argc ); # argument count#.. 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 ...]#$::ScmRoot = StripDrive( ${ARGV[0]} );$::ProjectBase= $::ScmRoot;$::ScmMakelib = ${ARGV[1]};Debug( "ARGV: @ARGV" );Debug( "Root: $::ScmRoot" );Debug( "Makelib: $::ScmMakelib" );$argc = 2;while ($_ = $ARGV[ $argc++ ]){last if ( ! /^-/ ); # end of optionsDebug( "option: $_" );/^-h$|^-help$|^--help$|^--usage$|^-?$/ && MLUsage();# long argumentsif (/^--interface=(.*)/) {$::ScmInterface = $1;Debug( "Interface: $::ScmInterface" );next;}if (/^--expert(.*)/) {if ($1) {$::ScmExpert = $1;} else {$::ScmExpert = 1;}Debug( "Expert: $::ScmExpert" );next;}if (/^--all/) {$::ScmAll = 1;Debug( "All: $::ScmAll" );next;}last if (/^--$/); # end of argumentsif (/^-D(.*)/) {if ($1) {$::ScmDebug = $1;} else {$::ScmDebug = $ARGV[0];$argc++;}Debug( "ScmDebug: $::ScmDebug" );next;}next if (/^-V/ && ($::ScmVerbose = 1));next if (/^-F/ && ($::ScmForce = 1));if (/^-.*/) { # unknown optionMLUsage( "unknown option '$_'\n\n" );}}#.. 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{my( $msg ) = @_;print "makefile $msg\n\n"if ( $msg ne "" );print "Usage: perl makefile.pl <ROOTDIR> <Makelib.pl> [options]\nValid options:\t-V Verbose mode.\t-D<debug-level> Set the debug level.\t except all SCM support files are removed (eg CVS).\n";exit(42);}#-------------------------------------------------------------------------------# 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 ){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( SCRIPT, $script ) ||Error( "cannot open '$script'" );while (<SCRIPT>) {$_ =~ s/\s*(\n|$)//; # kill trailing whitespace & nlpush( @::DEFINES, $_ );}push( @::ScmDepends, "$script" ); # makefile dependenciesclose( SCRIPT );}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( SCRIPT, $script ) ||Error( "cannot open '$script'" );while (<SCRIPT>) {$_ =~ s/\s*(\n|$)//; # kill trailing whitespace & nlpush( @::RULES, $_ );}push( @::ScmDepends, "$script" ); # makefile dependenciesclose( SCRIPT );}#-------------------------------------------------------------------------------# 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 ( $dir, $exitVal, $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 GB_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 && defined( %::ScmBuildPlatforms ) ){ # targetsforeach my $key (keys %::ScmBuildPlatforms) {push( @platforms, $key )if (! defined( %::ScmBuildProducts ) ||! scalar $::ScmBuildProducts{ $key } );}}## Expand the '*' into a list of platforms that are 'products'#if ( $products && defined( %::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 );if ($::ROOTMAKEFILE == 1) {print STDERR "WARNING: problem generating Makefile ($exitVal)\n"unless (($exitVal == $::Generated) || ($exitVal == 0));return ($exitVal);}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## Returns : $exitVal#sub Generate{my( $noplatforms ) = @_;my( $exitVal ) = 0;#.. Dont build platforms within root directory#$noplatforms = 1if ($::ROOTMAKEFILE == 1);#.. Build 'makefile'#$exitVal = GeneratePlatforms()unless ($noplatforms );GenerateMakefile( $noplatforms );$exitVal = $::Generated if ( $exitVal == 0);return $exitVal;}## Note:# Currently this function will create all .mk files in the current directory## Note: Cleanup of unused .mk files is done when the WriteCommonInfo is# processed. Makefiles that are no lonker used will be purged#sub GeneratePlatforms{my( $exitVal, $exitVal2 ) = 0;foreach my $platform ( @::PLATFORMS ){my( $Cmdline ) = "$::GBE_PERL $0 $::ScmRoot " . $::ScmMakelib . "2" . " $platform";$Cmdline .= " --interface=$::ScmInterface"if ( $::ScmInterface ne "" );## Insert platform local arguments#if ($::PLATFORMARGS{ $platform }){my( @args ) = split( /$;/, $::PLATFORMARGS{ $platform } );foreach my $arg (@args) {$Cmdline .= " --arg=$arg";}}$Cmdline .= " --expert"if ( $::ScmExpert );$Cmdline .= " --all"if ( $::ScmAll );$exitVal2 = System( $Cmdline );Warning ("Problem generating $platform.mk in $::Cwd ($exitVal2)")unless (($exitVal2 == $::Generated) || ($exitVal2 == 0));if ($exitVal == 0) {$exitVal = $exitVal2;} elsif ($exitVal == $::Generated) {$exitVal = $exitVal2if ($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;