Subversion Repositories DevTools

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
# -*- mode: perl; tabs: 8; indent-width: 4; show-tabs: yes; -*-
2
# Copyright (C) 1998-2004 ERG Transit Systems, All rights reserved
3
#
4
# Module name   : Makelib.pl
5
# Module type   : Makefile system
6
#
7
# Description:
8
#       This modules builds the primary makefile for each directory,
9
#       used in conjunction with Makelib.pl2.  The produced makefile
10
#       acts as a frontend to platform builds.
11
#
12
# Notes:
13
#
14
#                       *** DO NOT DETAB ***
15
#
16
#       Beware the use of space v's tab characters within the
17
#       makefile generation sessions.
18
#
19
#.........................................................................#
20
 
255 dpurdie 21
require 5.006_001;
227 dpurdie 22
use strict;
23
use warnings;
261 dpurdie 24
use Getopt::Long;
263 dpurdie 25
use JatsError;
227 dpurdie 26
use JatsEnv;
27
use JatsMakeInfo qw(:create);
28
 
29
our $MakelibVersion         = "2.33";           # makelib.pl version
30
 
31
our $ScmRoot                = "";
32
our $ScmSrcDir              = "";
33
our $ScmMakelib             = "";
34
our @ScmDepends             = ();
35
our $ScmExpert              = 0;
36
our $ScmAll                 = 0;
37
our $ProjectBase            = "";               # Base of the user's project
38
our $ScmInterface           = "interface";
39
 
40
our @SUBDIRS                = ();
41
our @PLATFORMS              = ();
42
our %PLATFORMARGS           = ();
43
our @DEFINES                = "";
44
our @RULES                  = ();
45
 
46
our %PACKAGE_DIST           = ();
47
 
48
our $ROOTMAKEFILE           = 0;
49
our $PLATFORMINCLUDED       = 0;
50
our @BUILDPLATFORMS         = ();
51
 
263 dpurdie 52
our $GBE_CONFIG;
53
our $GBE_TOOLS;
54
our $GBE_PERL;
227 dpurdie 55
 
56
#.. Running under 'buildlib.pl' ?
57
#
58
unless ( $::ScmBuildlib )
59
{
60
    MakeLibInit();
61
}
62
 
63
sub MakeLibInit
64
{
261 dpurdie 65
    #.. Test environment
66
    #
227 dpurdie 67
    EnvImport( "GBE_BIN" );
68
    EnvImport( "GBE_PERL" );
69
    EnvImport( "GBE_TOOLS" );
70
    EnvImport( "GBE_CONFIG" );
71
    EnvImport( "GBE_MACHTYPE" );
72
 
261 dpurdie 73
    #.. Common stuff
74
    #
227 dpurdie 75
    require "$::GBE_TOOLS/common.pl";
76
 
77
    CommonInit( "makelib " );
78
    Debug( "version:   $MakelibVersion" );
79
 
261 dpurdie 80
    #.. Parse command line
81
    #       makefile.pl  rootdir Makelib.pl [options ...]
82
    #
83
    Verbose ("Command Line: @ARGV");
84
    my $opt_help = 0;
85
    my $result = GetOptions (
86
                "help+"         => \$opt_help,
87
                "interface=s"   => \$::ScmInterface,
88
                );
89
 
90
    MLUsage() if ( $opt_help || !$result );
91
 
92
    #
93
    #   Needs 2 command line arguments
94
    #
227 dpurdie 95
    $::ScmRoot    = StripDrive( ${ARGV[0]} );
279 dpurdie 96
    $::ScmRoot    = '/./' if ( $::ScmRoot eq '/'  );    # Prevent leading '//'
227 dpurdie 97
    $::ProjectBase= $::ScmRoot;
98
    $::ScmMakelib = ${ARGV[1]};
99
 
100
    Debug( "ARGV:      @ARGV" );
101
    Debug( "Root:      $::ScmRoot" );
102
    Debug( "Makelib:   $::ScmMakelib" );
103
 
261 dpurdie 104
    #.. Get the stuff from the platform definition file
105
    #
227 dpurdie 106
    ConfigLoad();
107
 
261 dpurdie 108
    #.. Get the stuff from the package definition file
109
    #
227 dpurdie 110
    require "$::ScmRoot/package.pl"
111
        if ( -f "$::ScmRoot/package.pl" );
112
}
113
 
114
#   MLUsage ---
115
#       Makelib command line usage.
116
#..
117
 
118
sub MLUsage
119
{
261 dpurdie 120
    Error ( "Usage: perl makefile.pl <ROOTDIR> <makelib.pl> [options ...]",
121
            "Valid options:",
122
            "   --help            Display Help",
123
            "   --interface=name  Set interface directory",
124
            );
227 dpurdie 125
}
126
 
127
#-------------------------------------------------------------------------------
128
# Function        : SubDir
129
#
130
# Description     : Recurse into the specified sub directories
131
#                   This is one of the very few directives in a 'makefile.pl'
132
#                   that is processed by this script - all the others are
133
#                   processed by makelib.pl2.
134
#
135
#                   This directive MUST appear before the 'Platform' directive
136
#
137
# Inputs          : List of sub directories to visit
138
#
139
# Returns         : Nothing
140
#
141
sub SubDir
142
{
143
    my( @NewDirs );
144
    Debug( "SubDir(@_)" );
145
    Error ("Directive 'SubDir' not allowed in this context") if ( $::ScmBuildlib  );
146
 
147
    #
148
    #   Support different constructs:
149
    #       'dir1 dir2'
150
    #       'dir1','dir2'
151
    #
152
    @NewDirs = map { split /\s+/ } @_;
153
    @NewDirs = grep { defined $_ } @NewDirs;
154
 
155
    foreach my $ThisDir ( @NewDirs )
156
    {
157
        Warning ("SubDir contains a '\\' character: $ThisDir" )
158
            if ( $ThisDir =~ m~\\~);
159
 
160
        if ( grep /^$ThisDir$/, @::SUBDIRS )
161
        {
162
            Warning( "Duplicate SubDir '$ThisDir' -- ignored." );
163
            next;
164
        }
165
        if ( ! ( -e $ThisDir and -d $ThisDir ) )
166
        {
167
            Error( "SubDir(): Subdirectory not found: '$ThisDir'",
168
                   "Current directory: $::Cwd" );
169
        }
170
        if ( ! -f $ThisDir . '/makefile.pl' )
171
        {
172
            Error( "SubDir(): makefile.pl not found in subdirectory: '$ThisDir'",
173
                   "Current directory: $::Cwd" );
174
        }
175
 
176
        push(@::SUBDIRS, $ThisDir);
177
    }
178
}
179
 
180
 
181
#-------------------------------------------------------------------------------
182
# Function        : RootMakefile
183
#
184
# Description     : This function is called from buildlib.pl prior to the
185
#                   generation of the root makefile. The Root Makefile is
186
#                   different to the others in this it does not have any platform
187
#                   specific makefiles associated with it. It is simply used to
188
#                   invoke the makefile in the 'src' subdirectory
189
#
190
# Inputs          : None
191
#
192
# Returns         : Nothing
193
#
194
sub RootMakefile
195
{
196
    Error ("Directive 'RootMakefile' not allowed in this context") if ( $::ScmBuildlib  );
197
    $::ROOTMAKEFILE = 1;
198
}
199
 
200
 
201
sub PackageDist
202
{
203
    my( $name, @elements ) = @_;
204
    Error ("Directive 'PackageDist' not allowed in this context") if ( $::ScmBuildlib  );
205
 
206
    foreach ( @elements ) {
207
        HashJoin( \%::PACKAGE_DIST, $;, $name, "$_" );
208
    }
209
}
210
 
211
 
212
sub Define
213
{
214
    Error ("Directive 'Define' not allowed in this context") if ( $::ScmBuildlib  );
215
    push( @::DEFINES, @_ );
216
}
217
 
218
 
219
sub Defines
220
{
221
    my( $path, $script ) = @_;
222
    my( $line );
223
    Error ("Directive 'Defines' not allowed in this context") if ( $::ScmBuildlib  );
224
 
225
    $script = Exists( $path, $script, "Defines" );
285 dpurdie 226
    open( my $fh, '<' ,$script ) ||
227 dpurdie 227
        Error( "cannot open '$script'" );
285 dpurdie 228
    while (<$fh>) {
227 dpurdie 229
        $_ =~ s/\s*(\n|$)//;                    # kill trailing whitespace & nl
230
        push( @::DEFINES, $_ );
231
    }
232
    push( @::ScmDepends, "$script" );           # makefile dependencies
285 dpurdie 233
    close( $fh );
227 dpurdie 234
}
235
 
236
 
237
sub Rule
238
{
239
    Error ("Directive 'Rule' not allowed in this context") if ( $::ScmBuildlib  );
240
    push( @::RULES, @_ );
241
}
242
 
243
 
244
sub Rules
245
{
246
    Error ("Directive 'Rules' not allowed in this context") if ( $::ScmBuildlib  );
247
    my( $path, $script ) = @_;
248
    my( $line );
249
 
250
    $script = Exists( $path, $script, "Rules" );
285 dpurdie 251
    open( my $fh, '<' ,$script ) ||
227 dpurdie 252
        Error( "cannot open '$script'" );
285 dpurdie 253
    while (<$fh>) {
227 dpurdie 254
        $_ =~ s/\s*(\n|$)//;                    # kill trailing whitespace & nl
255
        push( @::RULES, $_ );
256
    }
257
    push( @::ScmDepends, "$script" );           # makefile dependencies
285 dpurdie 258
    close( $fh );
227 dpurdie 259
}
260
 
261
#-------------------------------------------------------------------------------
262
# Function        : Platform
263
#
264
# Description     : Within "makelib.pl" the Platform directive is processed
265
#                   in such a manner as to trigger the verification and
266
#                   generation of Makefile and xxxxx.mk files in the tree
267
#                   below the makefile.pl
268
#
269
# Inputs          : A list of platforms for which the body of the makefile.pl
270
#                   are to be processed + GBE_PLATFORM
271
#
272
#                   "*", ...            - A wildcard for all platforms
273
#                                         Options include
274
#                                           --Targets (Not Products)
275
#                                           --Products
276
#                                           --NoPlatformBuilds
277
#                                           !xxx - Exclude a platform
278
#
279
#                   "--NoPlatformBuilds"
280
#
281
#                   "xxx"[, "yyy"]*     - A list of platforms and aliases
282
#
283
#                   "!xxx"              - A platform to be excluded
284
#
285
# Returns         :
286
#
287
sub Platform
288
{
289
    my ( @platforms ) = @_;
285 dpurdie 290
    my $exitVal;
291
    my $platform;
227 dpurdie 292
    my $noplatforms = 0;
293
    my $defplatforms = 0;
294
    my %platforms;
295
 
296
    Debug( "Platform(@_)" );
297
    Error ("Directive 'Platform' not allowed in this context") if ( $::ScmBuildlib  );
298
 
299
    #
300
    #   Import user platform specification (GBE_PLATFORM)
301
    #   Note: This is also used by the Makefile and it cannot be
302
    #         alias expanded or sanitised. Its not really really useful
303
    #
304
    #   Only use GB_PLATFORM if the user has not specified to build ALL
305
    #   makefiles.
306
    #
307
    my %filter;
308
    my $filter_present = 0;
309
 
310
    if (  $::ScmAll == 0  )
311
    {
239 dpurdie 312
        $filter{GENERIC} = 1;
227 dpurdie 313
        foreach ( split( ' ', $ENV{ "GBE_PLATFORM" } || '' ) )
314
        {
315
            $filter{$_} = 1;
316
            $filter_present = 1;
317
        }
318
    }
319
 
320
    #
321
    #   Expand out the directive platform list (and optional arguments)
322
    #   Expand wildcards and aliases
323
    #
324
    #   Handle format
325
    #       Platform ('*', '--Options', [!Name]);
326
    #       Platform ('--NoPlatformBuilds' );
327
    #       Platform ('Name', '--Options', [!Name] );
328
    #       Platform ('!Name' );
329
    #
330
    #
331
    if ( $platforms[0] && $platforms[0] eq '*' )
332
    {
333
        my( $targets, $products );              # options
334
        my( @args );
335
        my( @exclude );
336
 
337
        $targets = $products = 0;
338
 
285 dpurdie 339
        foreach ( @platforms ) {
227 dpurdie 340
            next if ( /^\*$/);
341
            next if ( /^--Targets$/ && ($targets = 1));
342
            next if ( /^--Products$/ && ($products = 1));
343
            next if ( /^--NoPlatformBuilds/ && ($noplatforms = 1));
344
            next if ( /^--/ && push @args, $_ );
345
            next if ( /^!/  && push @exclude, $_ );
346
            Warning( "Platform: unknown option $_ -- ignored\n" );
347
        }
348
 
349
        #
350
        #   Determine the list of platforms to expand the '*' into
351
        #   The result may be modified by optional arguments
352
        #       --Targets           # Expands to all targets
353
        #       --Products          # Expands to all products
354
        #       OtherWise           # Expands to all build platforms
355
        #
356
        @platforms = ();                        # zap list
357
 
358
                                                # 'all' platforms
359
        push( @platforms, @::DEFBUILDPLATFORMS )
360
            unless ( $targets | $products );
361
 
362
        #
363
        #   Expand the '*' into a list of platforms that are NOT products
364
        #
365
        if ( $targets && defined( %::ScmBuildPlatforms ) )
366
        {                                       # targets
367
            foreach my $key (keys %::ScmBuildPlatforms) {
368
                push( @platforms, $key )
369
                    if (! defined( %::ScmBuildProducts ) ||
370
                            ! scalar $::ScmBuildProducts{ $key } );
371
            }
372
        }
373
 
374
        #
375
        #   Expand the '*' into a list of platforms that are 'products'
376
        #
377
        if ( $products && defined( %::ScmBuildProducts ) )
378
        {                                       # products
379
            foreach my $key (keys %::ScmBuildProducts) {
380
                push( @platforms, $key );
381
            }
382
        }
383
 
384
        #
385
        #   Distribute arguments over all expanded platforms
386
        #
387
        if ( @args )
388
        {
389
            my @baseplatforms;
390
            foreach  ( @platforms )
391
            {
392
                push @baseplatforms, $_, @args;
393
            }
394
            @platforms = @baseplatforms;
395
        }
396
 
397
        #
398
        #   Place the excluded platforms at the end of the list
399
        #
400
        push @platforms, ExpandPlatforms( @exclude );
401
 
402
    }
403
    elsif ( scalar @platforms == 1 && $platforms[0] eq "--NoPlatformBuilds" )
404
    {                                           # short-cut
405
        $noplatforms = 1;
406
        @platforms = @::DEFBUILDPLATFORMS;
407
    }
408
    else
409
    {                                           # aliasing
410
        @platforms = ExpandPlatforms( @platforms );
411
        #
412
        #   Process excluded platform lists
413
        #   Migrate excluded platforms to the end of the list
414
        #
415
        my (@include, @exclude);
416
 
417
        foreach ( @platforms )
418
        {
419
            next if ( m/^!/ && push @exclude, $_ );
420
            push @include, $_;
421
        }
422
 
423
        #
424
        #   If no included platforms have been found then assume that the
425
        #   list is an exclusion list and seed the platform list with
426
        #   a set of defualt platforms - like '*'
427
        #
428
        #
429
        @include = @::DEFBUILDPLATFORMS unless @include;
430
        @platforms = ( @include, @exclude );
431
    }
432
 
433
    $platform = "";                             # current platform
434
 
435
    #
436
    #   Process the directives expanded list of platforms
437
    #
438
 
439
    #
440
    #   Generate a HASH of lowercase known platform names
441
    #   This will be used to quickly validate platform names
442
    #
443
    my %lc_platforms;
444
    foreach  ( @::BUILDPLATFORMS )
445
    {
446
        $lc_platforms{ lc($_) } = $_;
447
    }
448
 
449
FILTER:
450
    foreach $_ ( @platforms )
451
    {
452
        if ( ! /^--(.*)/ )
453
        {
454
            $_ =~ s/^\s*//g;                    # leading white space
455
            $_ =~ s/\s*(\n|$)//;                # trailing white space
456
 
457
 
458
            #
459
            #   Remove specific platforms from the list
460
            #
461
            $defplatforms = 1;
462
            if ( m/!(.*)/ )
463
            {
464
                Verbose( "Excluded platform removed: $1" );
465
                delete $platforms{$1};
466
                next FILTER;
467
            }
468
 
469
 
470
            if ( exists $platforms{$_}  )
471
            {
472
                Warning( "duplicate platform '$_' -- ignored." );
473
                $platform = "";
474
                next FILTER;
475
            }
476
 
477
            #
478
            #   validate 'platform'
479
            #   Allow the user to have a bad case match ( Fred == fred == FRED )
480
            #
481
            my $lc_platform = lc($_);
482
            unless ( exists( $lc_platforms{$lc_platform}  ) )
483
            {
484
                Warning( "Platform '$_' not contained within BuildPlatforms -- ignored." )
485
                    unless ( exists( $::ScmBuildPlatforms{$_} ));
486
                $platform = "";
487
                next FILTER;
488
            }
489
 
490
            $lc_platform = $lc_platforms{$lc_platform};
491
            if ( $_ ne $lc_platform )
492
            {
493
                Warning( "Mixed case usage of platform '$_' -- corrected." );
494
                $_ = $lc_platform;
495
            }
496
 
497
                                                # filter 'platform'
498
            if ( $filter_present  )
499
            {
500
                if ( ! exists $filter{$_} )
501
                {
502
                    Verbose( "GBE_PLATFORM override $_ -- ignored" );
503
                    $platform = "";
504
                    next FILTER;
505
                }
506
            }
507
 
508
            #
509
            #   Platform not filtered out - must be using it.
510
            #
511
            Verbose( "Platform ... $_" );
512
            $platforms{$_} = 1;                 # Add to platform list
513
            $platform = $_;                     # new platform
514
        }
515
 
516
        elsif ( /^--NoPlatformBuilds$/ )
517
        {
518
            $noplatforms = 1;
519
        }
520
 
521
        elsif ( $platform ne "" )
522
        {                                       # other arguments
523
            Verbose( "          .. $_" );
524
 
525
            HashUniqueJoin( \%::PLATFORMARGS, $; , $platform, $1 ) ||
526
                Warning( "Duplicate argument '$platform=$_' -- ignored." );
527
        }
528
    }
529
    #
530
    #   Sort the platforms to ensure that the ordering is consistient throughout
531
    #
532
    @::PLATFORMS = sort keys %platforms;
533
 
534
    #
535
    #   Trap makefiles that don't have any platforms
536
    #   The user 'should' mark these as --NoPlatformBuilds
537
    #
538
    unless ( @::PLATFORMS )
539
    {
540
        Warning( "No platform definitions." )
541
            unless( $noplatforms || $defplatforms);
542
        $noplatforms = 1;
543
    }
544
 
545
#.. Common rules
546
#
547
    my( $file ) = Require( "$::GBE_CONFIG", "Rules", "Common rules " );
548
    push( @::ScmDepends, "$file" );
549
 
550
 
551
#.. Generate primary makefile
552
#
553
    $exitVal = Generate( $noplatforms );
554
    if ($::ROOTMAKEFILE == 1) {
555
        print STDERR "WARNING: problem generating Makefile ($exitVal)\n"
261 dpurdie 556
            if ($exitVal);
227 dpurdie 557
        return ($exitVal);
558
    }
559
 
560
    Debug( "Platform ExitVal:   $exitVal" );
561
    exit( $exitVal );
562
}
563
 
564
###############################################################################
565
# Private function section.
566
#       The following functions are used INTERNALLY by makelib.pl.
567
#
568
###############################################################################
569
 
570
#-------------------------------------------------------------------------------
571
# Function        : Generate
572
#
573
# Description     : Build makefiles ...
574
#                   Creates the command data files and the per-target .MK files
575
#
576
# Inputs          : $noplatforms    - 1 if this makefile does not have
577
#                                       any platforms
578
#
579
# Returns         : $exitVal
580
#
581
sub Generate
582
{
583
    my( $noplatforms ) = @_;
584
    my( $exitVal ) = 0;
585
 
586
    #.. Dont build platforms within root directory
587
    #
588
    $noplatforms = 1
589
        if ($::ROOTMAKEFILE == 1);
590
 
591
    #.. Build 'makefile'
592
    #
593
    $exitVal = GeneratePlatforms()
594
        unless ($noplatforms );
595
 
596
    GenerateMakefile( $noplatforms );
597
    return $exitVal;
598
}
599
 
600
 
601
#
602
#   Note:
603
#   Currently this function will create all .mk files in the current directory
604
#
605
#   Note: Cleanup of unused .mk files is done when the WriteCommonInfo  is
606
#         processed. Makefiles that are no lonker used will be purged
607
#
608
sub GeneratePlatforms
609
{
610
    my( $exitVal, $exitVal2 ) = 0;
611
 
612
    foreach my $platform ( @::PLATFORMS )
613
    {
261 dpurdie 614
        my @CmdLine;
615
        push @CmdLine, $::GBE_PERL, $0, $::ScmRoot;
616
        push @CmdLine, $::ScmMakelib . "2";
617
        push @CmdLine, $platform;
618
        push @CmdLine, "--interface=$::ScmInterface" if ( $::ScmInterface ne "" );
227 dpurdie 619
 
620
        #
621
        #   Insert platform local arguments
622
        #
623
        if ($::PLATFORMARGS{ $platform })
624
        {
261 dpurdie 625
            foreach my $arg (split( /$;/, $::PLATFORMARGS{ $platform } )) {
626
                push @CmdLine,"--arg=$arg";
227 dpurdie 627
            }
628
        }
629
 
261 dpurdie 630
        #
631
        #   Invoke the command
632
        #   Don't use a Shell. Don't need the overhead
633
        #
634
        $exitVal2 = System( '--NoShell', @CmdLine );
227 dpurdie 635
 
261 dpurdie 636
        #
637
        #   Warn on any error
638
        #   Overall return code will be set if any platform fails
639
        #
640
        if ( $exitVal2 )
641
        {
642
            Warning ("Problem generating $platform.mk in $::Cwd ($exitVal2)");
227 dpurdie 643
            $exitVal = $exitVal2;
644
        }
645
    }
646
 
647
    return $exitVal;
648
}
649
 
650
#-------------------------------------------------------------------------------
651
# Function        : GenerateMakefile
652
#
653
# Description     : Generate the non-platform specific makefile
654
#                   Once upon a time this was a real makefile
655
#                   Now its a simple file and a database as this offeres greater
656
#                   flexability.
657
#
658
#                   The file, "Makefile.gbe" contains enough info to get to the
659
#                   interface directory.
660
#
661
#                   The "database" is held in the interface directory as
662
#                       Makfile.cfg     - index to locate Makefile_nn.cfg
663
#                       Makefile_nn.cfg - data from a makefile.pl
664
#
665
# Inputs          : $noplatforms        - 1: no platform makefiles in this dir
666
#
667
# Returns         : Nothing
668
#
669
sub GenerateMakefile
670
{
671
    my( $noplatforms ) = @_;
672
    my %platform_info;
673
 
674
    #
675
    #   Determine targets that are limited to either debug or production builds
676
    #   This information may come from several places
677
    #
678
    foreach my $key ( @PLATFORMS )
679
    {
680
        my @args;
681
        UniquePush (\@args, split ( /$;/, $::PLATFORMARGS{$key} ))
682
            if ($::PLATFORMARGS{$key});
683
 
684
        UniquePush (\@args, map { s/^--//; $_} split ( /$;/, $::ScmBuildPlatforms{$key} ))
685
            if($::ScmBuildPlatforms{$key});
686
 
687
        my $hasOnlyProd  =  grep /^OnlyProd/, @args;
688
        my $hasOnlyDebug =  grep /^OnlyDebug/, @args;
689
 
690
        if ( $hasOnlyProd && $hasOnlyDebug )
691
        {
692
            Warning ("Target \"$key\" has both OnlyDebug and OnlyProd options",
693
                     "Both options ignored. Target will be built" );
694
 
695
            $hasOnlyProd = $hasOnlyDebug = 0;
696
        }
697
        $platform_info{$key}{prod}  = 1 unless $hasOnlyDebug;
698
        $platform_info{$key}{debug} = 1 unless $hasOnlyProd;
699
    }
700
 
701
    #
702
    #   Update common information in the Makefile_x.cfg file
703
    #
704
    WriteCommonInfo( \@::SUBDIRS, \%platform_info, $noplatforms, $::ROOTMAKEFILE );
705
 
706
    #
707
    #.. Maintain local information in Makefile.gbe
708
    #   This allows us to locate the interface directory
709
    #
710
    Message "[Control] $::Cwd";
711
    CreateMakeInfo();
712
}
713
 
714
1;
715