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