Subversion Repositories DevTools

Rev

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

Rev Author Line No. Line
227 dpurdie 1
########################################################################
2
# Copyright (C) 2007 ERG Limited, All rights reserved
3
#
4
# Module name   : jats.sh
5
# Module type   : Makefile system
6
# Compiler(s)   : n/a
7
# Environment(s): jats
8
#
9
# Description   : Top level Makefile supervisor
10
#                 The function of this program is to call the target makefiles
11
#                 in a sutable manner.
12
#
13
# History       : Once upon a time this was done with another makefile and
14
#                 some really ugle shell, embedded within the makefile
15
#                 This had a number of limitations, including
16
#                   - Limited to makefile command line syntax
17
#                   - Overly complex makefile
18
#                   - Slow execution
19
#
20
# Usage:
21
#
22
#......................................................................#
23
 
255 dpurdie 24
require 5.006_001;
227 dpurdie 25
use strict;
26
use warnings;
27
 
28
use Pod::Usage;                             # For help support
29
use JatsError;
30
use JatsSystem;
31
use JatsEnv;
32
use JatsMakeInfo;
33
use ReadBuildConfig qw(:All);
34
use Getopt::Long;
35
use FileUtils;
36
use ArrayHashUtils;
37
use JatsDPackage;
38
 
39
my $VERSION = "1.0.0";                      # Update this
40
 
41
#
42
#   Options
43
#
44
my $opt_debug   = $ENV{'GBE_DEBUG'};        # Allow global debug
45
my $opt_verbose = $ENV{'GBE_VERBOSE'};      # Allow global verbose
46
my $opt_help = 0;
47
my $opt_manual = 0;
48
my $opt_silent = 1;
49
my $opt_default;
50
 
51
#
52
#   Global variables
53
#
54
our %cf_info;
55
our $GBE_TOOLS;                             # Base of tools
56
our $GBE_ABT;                               # Optional ABT indication
57
our $GBE_PLATFORM;                          # Optional GBE_PLATFORM
58
our $ScmInterface;                          # Current interface directory
59
our $ScmRoot;                               # Build root
60
 
61
my $Tags;                                   # Reference to hash of tags
62
my %defs;                                   # Make definitions
63
my @defs;                                   # Make definitions
64
my @targets;                                # Make targets
65
my $makelib;                                # Path to makelib.pl
66
my $update = 0;                             # Makefiles updated
67
my $build_error = 0;                        # Build Error encountered
68
my $tlen = 4;                               # Max length of targets
69
my %common_cmd;                             # Short circuit some commands
70
my %gbe_platform;                           # Nice form of GBE_PLATFORM
71
 
72
#
73
#   Known commands
74
#   A hash of known commands an information about the commands
75
#       common      - Command is not executed for Debug and Production
76
#                     Flags: 1 - Command not executed for debug+prod
77
#                     Flags: 2 - Command is only executed ONCE per platform
78
#       local       - The command is handled locally.
79
#       nomakecheck - Don't check for makefile being out of date
80
#       IFLAG       - iteration flag,
81
#                       0   No external dependencies.
82
#                       1   Source dependency list.
83
#                       2   Shared library dependency list.
84
#                       3   Application dependency list.
85
#   Note:   gmake attempts to build includes prior to invoking other
86
#   rules when the source is non-existent, which can/do fail as a
87
#   result of the prereq rules not being executed, the IFLAG inhabits
88
#   includes during these prereq rules.
89
#
90
my %composite_commands = (
91
    #
92
    #   Composite commands
93
    #
94
    'all'                   => [ 'install', 'package', 'deploy' ] ,
95
    'install'               => [ 'build' ] ,
96
    'build'                 => [ 'mkdepends', 'generate', 'install_hdr', 'depend',
97
                                 'make_lib', 'install_lib', 'make_install_shlib',
98
                                 'make_prog', 'make_test', 'install_prog', 'install_class' ] ,
99
    'lint'                  => [ 'mkdepends', 'generate_debug', 'install_hdr_debug',
100
                                 'lint_lib', 'lint_shlib', 'lint_prog' ] ,
101
    'ctags'                 => [ 'mkdepends', 'generate_debug', 'ctags_debug' ] ,
102
    'test'                  => [ 'make_test' ] ,
103
    'mkdepends'             => [ 'makefiles', 'make_init'] ,
104
 
105
    );
106
 
107
my %commands = (
108
    #
109
    #   Basic commands / phases of the build process
110
    #   This should be a list of all known makefile commands
111
    #
112
    'make_usage'            => { 'tag' => 0, 'local' => \&DoHelp },
113
    'help'                  => { 'tag' => 0, 'local' => \&DoHelp },
114
    'rebuild'               => { 'tag' => 0, 'local' => \&TestMakeFiles },
115
    'makefiles'             => { 'tag' => 0, 'local' => \&TestMakeFiles },
116
    'unmakefiles'           => { 'tag' => 0, 'local' => \&UnMakeFiles },
117
    'make_init'             => { 'tag' => 1, 'nomakecheck' => 1, 'common' => 3, },
118
    'make_dir'              => { 'tag' => 1, 'nomakecheck' => 1 },
119
    'generate'              => { 'tag' => 1 },
120
    'install_hdr'           => { 'tag' => 1 },
121
    'depend'                => { 'tag' => 1, 'unless' => 'NODEPEND' },
122
    'make_lib'              => { 'tag' => 1, 'IFLAG' => 1 },
123
    'install_lib'           => { 'tag' => 1, 'IFLAG' => 1 },
124
    'make_install_shlib'    => { 'tag' => 1, 'IFLAG' => 2 },
125
    'make_prog'             => { 'tag' => 1, 'IFLAG' => 3 },
126
    'make_test'             => { 'tag' => 1, 'IFLAG' => 3 },
127
    'install_prog'          => { 'tag' => 1, 'IFLAG' => 3 },
128
    'install_class'         => { 'tag' => 1 },
129
    'package'               => { 'tag' => 1, 'IFLAG' => 3 },
130
    'deploy'                => { 'tag' => 1, 'IFLAG' => 1 },
131
 
132
    #
133
    #   Basic commands, that appear to be unused in composite commands
134
    #   Not too sure why
135
    #
136
    'make_script'           => { 'commands' => [], 'tag' => 1 },
137
 
138
    #
139
    #   Lint related targets
140
    #
141
    'lint_lib',             => { 'tag' => 'make_lib'            , 'IFLAG' => 1 },
142
    'lint_shlib',           => { 'tag' => 'make_install_shlib'  , 'IFLAG' => 2 },
143
    'lint_prog'             => { 'tag' => 'make_prog'           , 'IFLAG' => 3 },
144
 
145
    #
146
    #   Following commands should NOT be a part of a composite command
147
    #
148
    'run_tests'             => { 'nomakecheck' => 1, 'tag' => 1, 'IFLAG' => 3},
149
    'run_unit_tests'        => { 'nomakecheck' => 1, 'tag' => 1, 'IFLAG' => 3},
150
 
151
    'clean'                 => { 'nomakecheck' => 1},
152
    'clobber'               => { 'nomakecheck' => 1},
153
    'rmlitter'              => { 'nomakecheck' => 1},
154
    'unbuild'               => { 'nomakecheck' => 1},
155
    'undepend'              => { 'nomakecheck' => 1},
156
    'ungenerate'            => { 'nomakecheck' => 1},
157
    'uninstall'             => { 'nomakecheck' => 1},
158
    'uninstall_class'       => { 'nomakecheck' => 1},
159
    'uninstall_hdr'         => { 'nomakecheck' => 1},
160
    'uninstall_lib'         => { 'nomakecheck' => 1},
161
    'uninstall_prog'        => { 'nomakecheck' => 1},
162
    'unmake_lib'            => { 'nomakecheck' => 1},
163
    'unmake_prog'           => { 'nomakecheck' => 1},
164
    'unmake_script'         => { 'nomakecheck' => 1},
165
    'unmake_test'           => { 'nomakecheck' => 1},
166
    'unobj'                 => { 'nomakecheck' => 1},
167
    'unpackage'             => { 'nomakecheck' => 1},
168
    'make_undir'            => { 'nomakecheck' => 1},
169
 
170
    );
171
#    DebugDumpData ("Commands", \%commands);
172
 
173
#-------------------------------------------------------------------------------
174
# Function        : Mainline Entry Point
175
#
176
# Description     :
177
#
178
# Inputs          :
179
#
180
 
181
#
182
#   Parse the user options
183
#
184
my $result = GetOptions (
185
                "help+"         => \$opt_help,              # flag, multiple use allowed
186
                "manual"        => \$opt_manual,            # flag, multiple use allowed
187
                "verbose+"      => \$opt_verbose,           # flag, multiple use allowed
188
                "d|debug+"      => \$opt_debug,             # flag, multiple use allowed
189
                "default=s"     => \$opt_default,           # string
190
                );
191
 
192
                #
193
                #   UPDATE THE DOCUMENTATION AT THE END OF THIS FILE !!!
194
                #
195
 
196
#
197
#   Process help and manual options
198
#
199
pod2usage(-verbose => 0, -message => "Version: $VERSION")  if ($opt_help == 1  || ! $result);
200
pod2usage(-verbose => 1)  if ($opt_help == 2 );
201
pod2usage(-verbose => 2)  if ($opt_manual || ($opt_help > 2));
202
 
203
#
204
#   Configure the error reporting process now that we have the user options
205
#
206
ErrorConfig( 'name'    => 'Make',
207
             'verbose' => $opt_verbose,
208
             'debug'   => $opt_debug );
209
 
210
#
211
#   Configure the 'System' process so that it will terminate on any error
212
#   and not report the complete command line
213
#
214
SystemConfig ( 'ExitOnError' => 2);
215
 
216
#
217
#   Extract non-option commands
218
#   Much of this is for backward compatability
219
#   Support the following arguments
220
#       XXXXX=yyyy      Makefile Definition
221
#       -XXXX           Flags passed to make
222
#       XXXX            Raw target name
223
#
224
foreach my $arg ( @ARGV )
225
{
226
    Verbose2 ("Arg: $arg");
227
    if ( $arg =~ m~(\w+)=(.+)~ ) {
228
        $defs{uc($1)} = $2;
229
 
230
    } elsif ( $arg =~ m/^-/ ) {
231
        push @defs, $arg;
232
 
233
    } else {
234
        push @targets, $arg;
235
 
236
    }
237
}
238
 
239
#
240
#   Misc. Initialisation
241
#
242
InitFileUtils();
243
 
244
EnvImport( 'GBE_TOOLS' );
245
$makelib = "$GBE_TOOLS/makelib.pl";
246
Error ("Cannot locate JATS file: $makelib") unless ( -f $makelib );
247
 
248
EnvImportOptional ( 'GBE_ABT' );
249
EnvImportOptional ( 'GBE_PLATFORM' );
250
 
251
#
252
#   Validate user options
253
#
254
@targets = ($opt_default)
255
    if ( $opt_default && ! @targets);
256
Error ("No make targets specified" ) unless ( @targets );
257
 
258
#
259
#   Build the @defs array
260
#   This is a set of definitions passed to the makefiles
261
#   It is created on the fly to allow command line options to modify the definitions
262
#
263
foreach  ( sort keys %defs )
264
{
265
    next if ( m~^DEBUG$~i );                    # Naughty user
266
    push @defs, "$_=$defs{$_}";                 # Makefile defs are XXXX=aaaa
267
}
268
 
269
#
270
#   Look for OPTIONS=*args* and disable the silent make operation
271
#   This will allow the make to output messages
272
#
273
if (( exists $defs{OPTIONS} && $defs{OPTIONS} =~ m/args/ ) || $opt_verbose || $opt_debug )
274
{
275
    $opt_silent = 0;
276
}
277
 
278
#
279
#   Process GBE_PLATFORM and create a usable hash
280
#
281
if ( $GBE_PLATFORM )
282
{
283
    Verbose2 ("GBE_PLATFORM: $GBE_PLATFORM");
284
    $gbe_platform{$_} = 1 foreach ( split( / /, $GBE_PLATFORM ) );
239 dpurdie 285
    $gbe_platform{GENERIC} = 1;
227 dpurdie 286
}
287
 
288
#
289
#   Read in the local Makefile.gbe file
290
#   This will provide a path to the interface directory
291
#
292
ReadMakeInfo();
293
 
294
#
295
#   Read in the global build variables
296
#
297
ReadBuildConfig( "$::ScmRoot/$::ScmInterface" );
298
 
299
#
300
#   Determine the max size of the target name
301
#   Makes a nice display
302
#
303
foreach ( @BUILDPLATFORMS )
304
{
305
    my $len = length ($_);
306
    $tlen = $len if ( $len > $tlen );
307
}
308
 
309
#-------------------------------------------------------------------------------
310
#   Scan the list of targets and perform 'alias' expansion
311
#   targets are of the form
312
#       platform_command_type
313
#       Where:
314
#           type        may be _prod or _debug or not present
315
#           command     may be either a composite command or a simple command
316
#           platform    may be a platform or an alias
317
#
318
#
319
foreach my $arg ( @targets )
320
{
321
    my $suffix = '';
322
    my @commands;
323
    my @platforms;
324
 
325
    #
326
    #   Remove the only available suffix ( _debug | _prod )
327
    #   Also detect commnads of debug or prod
328
    #
329
    if ( $arg =~ m~(.+)_(debug)$~ ) {
330
        $suffix = $2;
331
        $arg = $1;
332
    } elsif ( $arg =~ m~(.+)_(prod)$~ ) {
333
        $suffix = $2;
334
        $arg = $1;
335
    } elsif ( $arg eq 'debug' ) {
336
        $suffix = $arg;
337
        $arg = '';
338
    } elsif ( $arg eq 'prod' ) {
339
        $suffix = $arg;
340
        $arg = '';
341
    }
342
 
343
    #
344
    #   Remove valid make commands from the remaining argument
345
    #   Have a hash of all known makefile commands
346
    #
347
    foreach my $cmd ( keys %composite_commands, keys %commands )
348
    {
349
        if ( $arg eq $cmd )
350
        {
351
            @commands = ExpandComposite($cmd);
352
            $arg = '';
353
        }
354
        elsif ( $arg =~ m~(.+)_($cmd)$~ )
355
        {
356
            $arg = $1;
357
            @commands = ExpandComposite($2);
358
            last;
359
        }
360
    }
361
 
362
    #
363
    #   See of we can extract an platform alias from the command
364
    #   Rip into target + command
365
    #
366
    if ( $arg )
367
    {
368
        if ( exists $ScmBuildAliases{$arg}  )
369
        {
370
            @platforms =  split (/ /, $ScmBuildAliases{$arg} );
371
        }
372
        elsif ( exists $ScmBuildPlatforms{$arg} )
373
        {
374
            @platforms = ($arg);
375
        }
376
        else
377
        {
378
            if ( @commands )
379
            {
380
                Error ("Unknown target: $arg");
381
            }
382
            else
383
            {
384
                Message ("Unknown command. Passed to make: $arg");
385
                @commands = $arg;
386
            }
387
        }
388
    }
389
 
390
    #
391
    #   Insert defaults
392
    #
393
    @platforms = @DEFBUILDPLATFORMS
394
        unless ( @platforms );
395
 
396
    @commands = ExpandComposite('all')
397
        unless ( @commands);
398
 
399
    #
400
    #   Perform GBE_PLATFORM filtering
401
    #
239 dpurdie 402
    @platforms = grep ( exists $gbe_platform{$_} , @platforms )
403
        if ( %gbe_platform );
227 dpurdie 404
 
405
    Error ("No platforms to be processed. Check GBE_PLATFORM")
406
        unless ( @platforms );
407
 
408
    #
409
    #   Iterate commands over platforms
410
    #
411
    foreach my $cmd ( @commands )
412
    {
413
        PerformCommand (\@platforms, $cmd, $suffix );
414
    }
415
}
416
 
417
#
418
#   All done
419
#
420
exit (0);
421
 
422
#-------------------------------------------------------------------------------
423
# Function        : PerformCommand
424
#
425
# Description     : Perform the make on a platform command
426
#
427
# Inputs          : $platform_ref   - platforms to process
428
#                   $cmd            - simple make command
429
#                   $suffix         - debug,prod,none
430
#
431
# Returns         : 
432
#
433
sub PerformCommand
434
{
435
    my ($platform_ref, $cmd, $suffix) = @_;
436
    my $do_prod = 1;
437
    my $do_debug = 1;
438
 
439
    #
440
    #   Clean up some ugly commands from lint and ctags
441
    #
442
    if ( $cmd =~ m~(.+)_(debug)$~ )
443
    {
444
        $cmd = $1;
445
        $suffix = $2;
446
    }
447
    Verbose2 ("Processing Target: $cmd, $suffix");
448
 
449
    #
450
    #   Limit command to production or debug
451
    #
452
    if ( $suffix eq 'debug' ) {
453
        $do_prod  = 0;
454
    } elsif ( $suffix eq 'prod' ) {
455
        $do_debug  = 0;
456
    }
457
 
458
    #
459
    #   Some commands can be suppressed by options
460
    #
461
    if ( exists $commands{$cmd}{'unless'}  )
462
    {
463
        my $check = $commands{$cmd}{'unless'};
464
        if ( exists $defs{$check} && $defs{$check} )
465
        {
466
            Verbose2 ("Skipping: $cmd because $check");
467
            return;
468
        }
469
    }
470
 
471
    #
472
    #   Interecpt commands that are handled locally
473
    #
474
    if ( exists $commands{$cmd}{'local'} )
475
    {
476
        $commands{$cmd}{'local'}( $cmd, $platform_ref );
477
        return;
478
    }
479
 
480
    #
481
    #   Process and recurse directory tree
482
    #
483
    Verbose ("Processing: $cmd, @{$platform_ref}");
484
 
485
    #
486
    #   Determine the local tag name and state
487
    #   The tag is used to short circuit makefile commands in recursive makes
488
    #   It greatly reduces the work required
489
    #
490
    #   Many commands have a tag that is the same as the command, but a few don't
491
    #
492
    my $tag = 0;
493
    if ( exists $commands{$cmd}{tag}  )
494
    {
495
        $tag = $commands{$cmd}{tag};
496
        $tag = $cmd if ( $tag eq '1' );
497
    }
498
 
499
    if ( $commands{$cmd}{'common'} )
500
    {
501
        InvokeSubMake( $::Cwd, $platform_ref ,$cmd, $tag, 1 );
502
    }
503
    else
504
    {
505
        InvokeSubMake( $::Cwd, $platform_ref ,$cmd, $tag, 1 ) if $do_debug;
506
        InvokeSubMake( $::Cwd, $platform_ref ,$cmd, $tag, 0 ) if $do_prod;
507
    }
508
}
509
 
510
#-------------------------------------------------------------------------------
511
# Function        : InvokeSubMake
512
#
513
# Description     : Build recursion
514
#                   Determine if there is anything to be done for the current
515
#                   target within a specified directory
516
#
517
# Inputs          : $dir
518
#                   $pref                   - Ref to an array of platforms
519
#                   $cmd
520
#                   $tag
521
#                   $do_prod
522
#
523
# Returns         : 
524
#
525
sub InvokeSubMake
526
{
527
    my ( $dir, $pref, $cmd, $tag, $do_debug) = @_;
528
 
529
    #
530
    #   Ensure the current directory is known to the Tags database
531
    #
532
    $Tags = ReadMaketags( $dir );
533
    Error ("Directory not in tag database", $dir ) unless ( $Tags );
534
 
535
    #
536
    #   Process commands in the current directory
537
    #   If the command is tagged, then we may be able to skip it
538
    #   Otherwise we need to do it
539
    #
540
    InvokeMake( $dir, $pref, $cmd, $tag, $do_debug );
541
 
542
    #
543
    #   Recurse into any subdirectories that may be a part of the build
544
    #
545
    unless ( exists $defs{NORECURSE} && $defs{NORECURSE}  )
546
    {
547
        #
548
        #   Process subdirectories for the current command
549
        #   If the command is tagged, then we may be able to skip it
550
        #   Otherwise we need to do it
551
        #
552
        foreach my $subdir (@{$Tags->{$dir}{subdirs}} )
553
        {
554
 
555
            InvokeSubMake( CleanDirName( "$dir/$subdir"), $pref, $cmd, $tag, $do_debug );
556
        }
557
    }
558
}
559
 
560
#-------------------------------------------------------------------------------
561
# Function        : InvokeMake
562
#
563
# Description     : Actually invoke the make for debug and production as required
564
#
565
# Inputs          : $dir
566
#                   $pref
567
#                   $cmd
568
#                   $tag
569
#                   $do_debug
570
#
571
#
572
# Returns         : 
573
#
574
sub InvokeMake
575
{
576
    my ( $dir, $pref, $cmd, $tag, $do_debug ) = @_;
577
    my $text = 'C';
578
 
579
    #
580
    #   Ensure that the current directory actually has makefile targets
581
    #   Not all makefile.pl create .mk files
582
    #       1) Top-level makefile.pl
583
    #       2) Makefiles that use --NoPlatformBuilds
584
    #
585
    if ( $Tags->{$dir}{root} )
586
    {
587
        Verbose2 "Root directory has no makefiles: $dir";
588
        return;
589
    }
590
 
591
    if ( $Tags->{$dir}{noplatforms} )
592
    {
593
        Verbose2 "No make targets in $dir";
594
        return;
595
    }
596
 
597
    #
598
    #   Process each platform
599
    #
600
    foreach my $target ( @{$pref} )
601
    {
602
        unless ( exists $Tags->{$dir}{platforms}{$target} )
603
        {
604
            Verbose2 "No make targets in $dir, for $target";
605
            next;
606
        }
607
 
608
        #
609
        #   Do we need to do any thing for this target / tag
610
        #
611
        if ( $tag )
612
        {
613
            unless ( exists $Tags->{$dir}{full}{$target}{'%MakeTags'}{$tag} )
614
            {
615
                Verbose2 ("Skipping $cmd in $dir");
616
                next;
617
            }
618
        }
619
 
620
        #
621
        #   common mode == 2
622
        #   Only process target ONCE for each platform
623
        #   Don't care when its used
624
        #
625
        if ( exists $commands{$cmd}{common} && $commands{$cmd}{common} & 2 )
626
        {
627
            if ( $common_cmd{$cmd}{$target} )
628
            {
629
                Verbose2 "Already performed $cmd for $target";
630
                next;
631
            }
632
            $common_cmd{$cmd}{$target} = 1;
633
        }
634
 
635
        #
636
        #   Is the build limited to only debug or production
637
        #
638
        unless ( exists $commands{$cmd}{common} )
639
        {
640
            if ( $do_debug == 0 ) {
641
                next unless ( $Tags->{$dir}{platforms}{$target}{prod} );
642
                $text = 'P';
643
 
644
            } elsif  ( $do_debug == 1 ) {
645
                next unless ( $Tags->{$dir}{platforms}{$target}{debug} );
646
                $text = 'D';
647
            }
648
        }
649
 
650
        #
651
        #   Final sanity test
652
        #   Must have the makefile. We should have detected this error before now
653
        #
654
        Error ("Makefile not found - $target") unless ( -f "$dir/$target.mk" );
655
 
656
        #
657
        #   Build up the make command line
658
        #   Examine command specfic flags
659
        #
660
        my @args = @defs;
661
        push @args, "IFLAG=$commands{$cmd}{IFLAG}" if ( exists $commands{$cmd}{IFLAG} );
662
        push @args, "NOSCMDEPEND=1" if ( exists $commands{$cmd}{nomakecheck} );
663
 
664
        my $cdir = CleanDirName ($dir);
665
        my @make_command;
666
        push @make_command, "$ENV{GBE_BIN}/xmake";
667
        push @make_command, "-s" if $opt_silent;
668
        push @make_command, "--no-print-directory";
669
        push @make_command, "-C", $cdir unless ( $cdir eq $::Cwd );
670
        push @make_command, "-f", "$target.mk";
671
        push @make_command, @args;
672
        push @make_command, 'make_dir' unless exists $commands{$cmd}{nomakecheck} ;
673
        push @make_command, $cmd;
674
 
675
        Message ( sprintf ("[$text] %-${tlen}s, $cmd, $cdir", $target ));
676
        System (@make_command, "DEBUG=$do_debug" );
677
    }
678
}
679
 
680
#-------------------------------------------------------------------------------
681
# Function        : TestMakeFiles
682
#
683
# Description     : Test all the makefile dependent files to see if any of the
684
#                   makefiles need to be rebuilt.
685
#
686
#                   Walk the directory tree looking for makefiles to be
687
#                   rebuilt.
688
#
689
# Inputs          : $cmd            - Current command
690
#                   $pref           - Ref to an array of platforms
691
#
692
# Returns         : 
693
#
694
sub TestMakeFiles
695
{
696
    my ( $cmd, $pref ) = @_;
697
    Verbose ("Test makefile dependencies");
698
 
699
    #
700
    #   Read in the Tag file for the current directory
701
    #   This will provide the current list of subdirectories
702
    #
703
    #   Process Test the current makefiles, then test any subdirs
704
    #
705
    TestSubMake( $cmd, $Cwd, $pref );
706
 
707
    #
708
    #   Report build errors
709
    #   Done after all makefiles have been processed
710
    #
711
    if ( $build_error )
712
    {
713
        Error ("Error creating makefiles. Errors previously reported");
714
    }
715
 
716
    #
717
    #   Post makefile build processing
718
    #   Only required if a files have been re-built
719
    #
720
    if ( $update )
721
    {
722
        #
723
        #   Sanity test the makefile structure
724
        #   Ensure that makefiles have only one parent.
725
        #
726
        TestParents();
727
 
728
        #
729
        #   Sanity test the installed and packaged files
730
        #   Generate warnings and errors if a file if packaged from two
731
        #   locations
732
        #
733
        TestPackages();
734
 
735
        #
736
        #   Generate DPACKAGE file if required
737
        #
738
        JatsDPackage::DPackageGenerate( $::ScmRoot, $::ScmInterface  );
739
    }
740
 
741
    #
742
    #
743
    #   Check that the build.pl file is not newer that the tags file
744
    #   This will not be an error, but it will be a nagging warning
745
    #
746
    my @build_warn;
747
    my $bstamp = -M "$::ScmRoot/$ScmBuildSrc";
748
    my $tstamp = -M "$::ScmRoot/Makefile.gbe";
749
 
750
    push @build_warn, "Missing: Makefile.gbe" unless ( defined $tstamp );
751
    push @build_warn, "Modified build file ($ScmBuildSrc)" if ( $tstamp && $bstamp < $tstamp );
752
    if ( @build_warn )
753
    {
754
        Warning ("The build file is newer than the generated files",
755
                 "The project needs to be built for these changes to be included",
756
                 @build_warn );
757
    }
758
}
759
 
760
#-------------------------------------------------------------------------------
761
# Function        : TestSubMake
762
#
763
# Description     : Test the makefiles in the current directory
764
#                   Recurse into subdirectories
765
#
766
# Inputs          : $cmd            - Current command
767
#                   $dir            - Directory to test
768
#                   $pref           - Ref to an array of platforms
769
#
770
# Returns         : 
771
#
772
sub TestSubMake
773
{
774
    my ($cmd, $dir, $pref ) = @_;
775
 
776
    $Tags = ReadMaketags( $dir, 1 );
777
    unless ( $Tags )
778
    {
779
        Verbose2 ("TestSubMake :Directory not in tag database", $dir );
780
        MakeBuild( $dir);
781
    }
782
    else
783
    {
784
        TestMake( $cmd, $dir, $pref );
785
    }
786
 
787
    #
788
    #   Recurse into any subdirectories that may be a part of the build
789
    #
790
    unless ( exists $defs{NORECURSE} && $defs{NORECURSE}  )
791
    {
792
        foreach my $subdir (@{$Tags->{$dir}{subdirs}} )
793
        {
794
            TestSubMake( $cmd, CleanDirName( "$dir/$subdir"), $pref );
795
        }
796
    }
797
}
798
 
799
#-------------------------------------------------------------------------------
800
# Function        : TestMake
801
#
802
# Description     : Test the makefiles component files
803
#                   and determine if the makefile(s) need to be rebuilt
804
#
805
# Inputs          : $cmd            - Current command
806
#                   $dir            - Directory to test
807
#                   $pref           - Ref to an array of platforms
808
#
809
# Returns         : 
810
#
811
sub TestMake
812
{
813
    my ( $cmd, $dir, $pref ) = @_;
814
    my @must_rebuild;
815
 
816
    if ( $cmd eq 'rebuild' )
817
    {
818
        MakeBuild( $dir);
819
        return;
820
    }
821
 
822
    #
823
    #   Check that the local makefile.pl is not newer than
824
    #   the Tags file. Not all makefile.pl's create .mk files
825
    #
826
    my $mstamp = -M "$dir/makefile.pl";
827
    my $tstamp = -M "$dir/Makefile.gbe";
828
 
829
    unless ( defined $tstamp )
830
    {
831
        UniquePush (\@must_rebuild, "Missing: $dir/Makefile.gbe");
832
    }
833
 
834
    if ( $mstamp && $mstamp < $tstamp  )
835
    {
836
        UniquePush (\@must_rebuild, "Updated: $dir/makefile.pl");
837
    }
838
    else
839
    {
840
        if ( $Tags->{$dir}{root} )
841
        {
842
            Verbose2 "TestMake: Root directory has no makefiles: $dir";
843
            return;
844
        }
845
 
846
        if ( $Tags->{$dir}{noplatforms} )
847
        {
848
            Verbose2 "TestMake: No make targets in $dir";
849
            return;
850
        }
851
 
852
        #
853
        #   Process only the required build targets
854
        #
855
        foreach my $tgt ( keys %{$Tags->{$dir}{platforms}}   )
856
        {
857
            next if ( %gbe_platform && not exists $gbe_platform{$tgt} );
858
            #
859
            #   Locate all the makefile dependent files
860
            #
861
            my $data = $Tags->{$dir}{full}{$tgt}{'@ScmDepends'};
862
            if ( $data )
863
            {
864
                Verbose2 ("Processing: $dir : $tgt");
865
                my $base_stamp = -M "$dir/$tgt.mk";
866
                unless ( defined $base_stamp )
867
                {
868
                    UniquePush (\@must_rebuild, "Missing: $dir/$tgt.mk");
869
                }
870
                else
871
                {
872
                    foreach my $file ( "$dir/makefile.pl" ,@$data )
873
                    {
874
                        $file = "$dir/$file" if ( $file =~ m~^\.~ );
875
                        my $stamp = -M $file;
876
                        if ( defined $stamp )
877
                        {
878
                            Verbose3 ("Timestamp: $stamp, $file");
879
                            if ( $stamp < $base_stamp  )
880
                            {
881
                                UniquePush (\@must_rebuild, $file);
882
                            }
883
                        }
884
                        else
885
                        {
886
                            UniquePush (\@must_rebuild, "Missing: $file");
887
                        }
888
                    }
889
                }
890
            }
891
            else
892
            {
893
                Warning ("No dependency information for: $tgt in $dir");
894
            }
895
        }
896
    }
897
 
898
    if ( @must_rebuild )
899
    {
900
        my @display;
901
        push (@display, CleanDirName($_) ) foreach ( @must_rebuild );
902
        Message ("One or more JATS source or internal files have changed, Makefiles will be rebuilt",
903
                  "Target is: $dir",
904
                  @display );
905
        MakeBuild ($dir);
906
    }
907
    return;
908
}
909
 
910
#-------------------------------------------------------------------------------
911
# Function        : MakeBuild
912
#
913
# Description     : Rebuild the makefiles in the specified directory
914
#                   This does not involve recursion - thats already been
915
#                   done.
916
#
917
#                   Once the makefiles have been re-built the tags database
918
#                   must be read in again as it may have changed.
919
#
920
# Inputs          : 
921
#
922
# Returns         : 
923
#
924
sub MakeBuild
925
{
926
    my ($dir) = @_;
927
 
928
    #
929
    #   No makefiles to build in the root directory
930
    #   Can't rebuild the top-level Makefile.gbe file
931
    #
932
    return if ( $Tags->{$dir}{root} );
933
 
934
    #
935
    #   Try to rebuild the makefile
936
    #
937
    my @args;
938
    push @args, '--expert' if ($ScmExpert);
939
    push @args, "--interface=$::ScmInterface";
940
    push @args, '-F';
941
 
942
    chdir $dir || Error ("Cannot change directory to $dir");
943
    my $result = System ('--NoExit',$ENV{GBE_PERL}, 'makefile.pl', $::ScmRoot, $makelib, @args );
944
    chdir $::Cwd || Error ("Cannot change directory to $::Cwd");
945
 
946
    #
947
    #   Re-read the tags database
948
    #
949
    $Tags = ReadMaketags( $dir, 2 );
950
 
951
    #
952
    #   Flag that makefiles have been modified
953
    #
954
    $update = 1;
955
    $build_error = 1 if ( $result && $result != 10 );
956
}
957
 
958
#-------------------------------------------------------------------------------
959
# Function        : UnMakeFiles
960
#
961
# Description     : Delete generated makefiles and control files
962
#
963
# Inputs          : $cmd            - Current command
964
#                   $pref           - Ref to an array of platforms
965
#
966
# Returns         : 
967
#
968
sub UnMakeFiles
969
{
970
    #
971
    #   Recurse down the tree
972
    #   Recurse, then performs operations in the current directory
973
    #
974
    sub UnSubMakeFiles
975
    {
976
        my ($dir) = @_;
977
        $Tags = ReadMaketags( $dir, 1 );
978
        foreach my $subdir (@{$Tags->{$dir}{subdirs}} )
979
        {
980
            UnSubMakeFiles( CleanDirName( "$dir/$subdir") );
981
        }
982
        UnMake( $dir );
983
    }
984
 
985
    #
986
    #   Delete makefiles in the current directory
987
    #
988
    sub UnMake
989
    {
990
        my ($dir) = @_;
991
        Verbose("Unmake in $dir");
992
        foreach my $tgt ( keys %{$Tags->{$dir}{platforms}}   )
993
        {
994
            Verbose2("Unmake. Delete ${tgt}.mk");
995
            unlink ("$dir/${tgt}.mk");
996
        }
997
        unlink ("$dir/Makefile.gbe");
998
        Verbose2("Unmake. Delete Makefile.gbe");
999
    }
1000
 
1001
    #
1002
    #   Depth first recursion through the tree
1003
    #
1004
    UnSubMakeFiles ( $Cwd );
1005
}
1006
 
1007
#-------------------------------------------------------------------------------
1008
# Function        : TestParents
1009
#
1010
# Description     : Ensure that each makefile node has exactly one parent
1011
#                   Prevent the user from creating recursive loops
1012
#
1013
# Inputs          : 
1014
#
1015
# Returns         : 
1016
#
1017
my %parents;
1018
sub TestParents
1019
{
1020
    Verbose ("Test makefile parents");
1021
    #
1022
    #   Init the hash. Command may be invoked multiple times
1023
    #
1024
    %parents = ();
1025
 
1026
    #
1027
    #   Recurse down the tree
1028
    #   Recurse, then performs operations in the current directory
1029
    #
1030
    sub RecurseDown
1031
    {
1032
        my ($dir) = @_;
1033
        $Tags = ReadMaketags( $dir, 1 );
1034
        Verbose2("TestParents in $dir");
1035
        foreach my $subdir (@{$Tags->{$dir}{subdirs}} )
1036
        {
1037
            my $subdir = CleanDirName( "$dir/$subdir");
1038
            push @{$parents{$subdir}}, $dir;
1039
            RecurseDown( $subdir );
1040
        }
1041
    }
1042
 
1043
    #
1044
    #   Depth first recursion through the tree
1045
    #
1046
    RecurseDown ( $Cwd );
1047
 
1048
    #
1049
    #   Test for only one parent
1050
    #   The root makefile won't have any
1051
    #
1052
    foreach my $dir ( sort keys %{$Tags} )
1053
    {
1054
        my $count = $#{$parents{$dir}};
1055
        if ( $count > 0 )
1056
        {
1057
            if ( defined($GBE_ABT) )
1058
            {
1059
                Warning ("Error supressed by ABT");
1060
                Warning ("makefile.pl with multiple parents",
1061
                         "makefile.pl in: $dir",
1062
                         "Parented by:", @{$parents{$dir}} );
1063
            }
1064
            else
1065
            {
1066
                unlink $Tags->{$dir}{config};
1067
                Error ("makefile.pl with multiple parents",
1068
                       "makefile.pl in: $dir",
1069
                       "Parented by:", @{$parents{$dir}} );
1070
            }
1071
        }
1072
    }
1073
}
1074
 
1075
#-------------------------------------------------------------------------------
1076
# Function        : TestPackages
1077
#
1078
# Description     : Ensure that files that are packaged and installed are
1079
#                   only done from one makefile. Warn if the same file
1080
#                   is being packaged from multiple makefiles.
1081
#
1082
# Inputs          : 
1083
#
1084
# Returns         : 
1085
#
1086
my %test_packages;
1087
sub TestPackages
1088
{
1089
    Verbose ("Test packaged files");
1090
    #
1091
    #   Init the hash. Command may be invoked multiple times
1092
    #
1093
    %test_packages = ();
1094
 
1095
    #
1096
    #   Recurse down the tree
1097
    #   Recurse, then performs operations in the current directory
1098
    #
1099
    sub TRecurseDown
1100
    {
1101
        my ($dir) = @_;
1102
        Verbose2("TestPackages in $dir");
1103
        $Tags = ReadMaketags( $dir, 1 );
1104
        #
1105
        #   Process makefile
1106
        #   Examine all the %PACKAGE_xxx and %INSTALL_xxx fields thare listed
1107
        #   in the @PACKAGE_VARS and @INSTALL_VARS array
1108
        #
1109
        foreach my $target ( keys %{$Tags->{$dir}{platforms}}   )
1110
        {
1111
            next if ( %gbe_platform && not exists $gbe_platform{$target} );
1112
            foreach my $field ( @{$Tags->{$dir}{full}{$target}{'@PACKAGE_VARS'}},
1113
                                @{$Tags->{$dir}{full}{$target}{'@INSTALL_VARS'}} )
1114
            {
1115
                foreach my $entry ( keys %{$Tags->{$dir}{full}{$target}{$field}} )
1116
                {
1117
                    Verbose3("TestPackages: $target, File: $entry");
1118
                    push @{$test_packages{$target}{$entry}}, $dir;
1119
                }
1120
            }
1121
        }
1122
 
1123
        #
1124
        #   Process subdirectories too
1125
        #
1126
        foreach my $subdir (@{$Tags->{$dir}{subdirs}} )
1127
        {
1128
            TRecurseDown( CleanDirName( "$dir/$subdir") );
1129
        }
1130
    }
1131
 
1132
 
1133
 
1134
    #
1135
    #   Depth first recursion through the tree
1136
    #
1137
    TRecurseDown ( $Cwd );
1138
 
1139
    #
1140
    #   Test and report files that are packaged in different makefiles
1141
    #   There are two issues:
1142
    #       1) Not good practice
1143
    #       2) May be different files
1144
    #   The warning message is a bit ugly - but it shouldn't pop up often.
1145
    #
1146
#    DebugDumpData ("test_packages", \%test_packages);
1147
 
1148
    foreach my $target ( sort keys %test_packages )
1149
    {
1150
        foreach my $file ( sort keys %{$test_packages{$target}} )
1151
        {
1152
            #
1153
            #   The descpkg file is known to be packaged from multiple dirs
1154
            #
1155
            next if ( $file =~ m~/descpkg~ );
1156
            if ( $#{$test_packages{$target}{$file}} > 0 )
1157
            {
1158
                Warning ("File is packaged or installed from multiple makefiles",
1159
                         "Target: $target, Packaged file: $file",
1160
                         "Packaged from the following directories:", @{$test_packages{$target}{$file}} );
1161
            }
1162
        }
1163
    }
1164
}
1165
 
1166
 
1167
#-------------------------------------------------------------------------------
1168
# Function        : DoHelp
1169
#
1170
# Description     : Display basic help information
1171
#
1172
# Inputs          : None
1173
#
1174
# Returns         :
1175
#
1176
sub DoHelp
1177
{
1178
 
1179
#
1180
#   Display basic usage information
1181
#
1182
    pod2usage(-verbose => 0,
1183
              -message => "Version: $VERSION",
1184
              -exitval => 'NOEXIT');
1185
 
1186
    print "Platform targets\n";
1187
    foreach ( sort keys %BUILDINFO )
1188
    {
1189
        print "    $_\n";
1190
    }
1191
 
1192
    my @alias = sort keys %ScmBuildAliases;
1193
    if ( $#alias >=0 )
1194
    {
1195
        print "Alias targets\n";
1196
        foreach ( @alias )
1197
        {
1198
            print "    $_   - $ScmBuildAliases{$_}\n";
1199
        }
1200
    }
1201
}
1202
 
1203
#-------------------------------------------------------------------------------
1204
# Function        : ReadMaketags
1205
#
1206
# Description     : Read in the global make tags data base
1207
#                   This contains information on all the components within
1208
#                   the build. The tags allow recursive makes to be pruned.
1209
#
1210
#                   The file may change while the build is running
1211
#                   It can be read multiple times
1212
#
1213
# Inputs          : $dir            - Directory entry to read
1214
#                   $test           - 1: Read if available
1215
#                                     2: Force read
1216
#
1217
# Returns         : Reference to the tag data for the requested directory
1218
#                   Will error if no available
1219
#
1220
our %cf_minfo;
1221
our %cf_minfo2;
1222
my %tag_data;
1223
sub ReadMaketags
1224
{
1225
    my ($dir, $test) = @_;
1226
 
1227
    #
1228
    #   Force data re-aquisition
1229
    #
1230
    delete $tag_data{$dir}
1231
        if ( $test && $test == 2 );
1232
 
1233
    #
1234
    #   Do we already have the entry
1235
    #
1236
    return \%tag_data
1237
        if ( exists ($tag_data{$dir} ));
1238
 
1239
    #
1240
    #   If the entry is not known, then re-read the index file
1241
    #
1242
    unless ( $::cf_filelist{$dir} )
1243
    {
1244
        my $index_file = "$::ScmRoot/$::ScmInterface/Makefile.cfg";
1245
        Error ("Makefile index missing. Rebuild required") unless ( -f $index_file );
1246
 
1247
        #
1248
        #   Kill the entry in %INC to force the file to be read in
1249
        #
1250
        $::cf_filelist = ();
1251
        delete $INC{ $index_file };
1252
        require $index_file;
1253
    }
1254
 
1255
    my $cfg_filen = $::cf_filelist{$dir};
1256
    unless ( $cfg_filen )
1257
    {
1258
        return undef
1259
            if ( $test );
1260
        Error ("Makefile index entry missing: $dir. Rebuild required");
1261
    }
1262
 
1263
 
1264
    #
1265
    #   Read in all the Makefile_x.cfg files
1266
    #
1267
    %cf_minfo = ();
1268
    %cf_minfo2 = ();
1269
    my $cfg_file = "$::ScmRoot/$::ScmInterface/$cfg_filen";
1270
 
1271
    unless ( -f $cfg_file )
1272
    {
1273
        return undef
1274
            if ( $test );
1275
        Error ("Make data file missing: $cfg_file. Rebuild required");
1276
    }
1277
 
1278
    delete $INC{ $cfg_file };
1279
    require $cfg_file;
1280
 
1281
    Error ("Makefile info2 not present")
1282
        unless ( keys %::cf_info2 );
1283
 
1284
    Error ("Makefile info2 incorrect version. Rebuild required")
1285
        unless ( exists $::cf_info2{version} && $::cf_info2{version} eq 1 );
1286
 
1287
    %{$tag_data{$dir}} = %::cf_info2;
1288
    $tag_data{$dir}{config} = $cfg_file;
1289
    %{$tag_data{$dir}{full}} = %::cf_info;
1290
 
1291
#DebugDumpData ("ReadMaketags", $tag_data{$dir});
1292
    return \%tag_data;
1293
 
1294
}
1295
 
1296
#-------------------------------------------------------------------------------
1297
# Function        : ExpandComposite
1298
#
1299
# Description     : Expand a command via composite command expansion
1300
#                   A composite command may contain other composite commands
1301
#
1302
# Inputs          : $cmd    - command to expand
1303
#
1304
# Returns         : An array of commands
1305
#
1306
sub ExpandComposite
1307
{
1308
    my ($cmd) = @_;
1309
    my @cmds;
1310
    my @scmds = $cmd;
1311
    while ( @scmds )
1312
    {
1313
        my $cmd = shift @scmds;
1314
        if ( exists $composite_commands{$cmd} )
1315
        {
1316
            @scmds = (@{$composite_commands{$cmd}}, @scmds);
1317
        }
1318
        else
1319
        {
1320
            push @cmds, $cmd;
1321
        }
1322
    }
1323
 
1324
    return @cmds;
1325
}
1326
 
1327
 
1328
#-------------------------------------------------------------------------------
1329
#   Documentation
1330
#
1331
 
1332
=pod
1333
 
1334
=head1 NAME
1335
 
1336
jmake - JATS make support tool
1337
 
1338
=head1 SYNOPSIS
1339
 
1340
 Usage: jats make [options][targets][flags]
1341
 
1342
 Where options:
1343
    -h, -h -h, -man     - Help messages with increasing verbosity
1344
    -verbose            - Verbose operation
1345
    -debug              - Debug operation
1346
    -default=target     - Default 'target' if none is supplied
1347
 
1348
 Where flags are of the form Name=Value
1349
    SHOWENV=1           - Show make environment
1350
    LEAVETMP=1          - Leave temp working files
1351
    NODEPEND=1          - Ignore dependency checking
1352
    OPTIONS=[opt]       - Maketime options [args,allargs,filter...]
1353
 
1354
 Valid targets include:
1355
    all                 - build and install everything(p*)
1356
    build               - build everything (pu)
1357
    debug               - build all things for debug (pu)
1358
    prod                - build all things for production (pu)
1359
    install             - install public components (pu*)
1360
    lint                - lints the source (assumes debug)(p)
1361
    package             - build all packages (pu*)
1362
    package-{set}       - build specific package (see below) (pu*)
1363
    run_tests           - Run the tests specified in the makefile
1364
    run_unit_tests      - Run the automatic unit tests specified in the makefile
1365
    deploy              - Run the deployment scripts (p*)
1366
    rebuild             - recursively rebuild makefiles
1367
    depend              - construct the dependencies (u*)
1368
    rmlitter            - remove build litter (core, tmp and err) (*)
1369
    clean               - delete generate, obj, libraries and programs (p*)
1370
    clobber             - delete everything which can be remade (p*)
1371
    help                - A list of alias and platforms
1372
 
1373
      (u) undo target available (ie uninstall)
1374
      (p) optional [platform_] prefix targets (ie XXX_build)
1375
      (*) optional [_debug|_prod] postfix targets (ie clean_debug)
1376
 
1377
 
1378
=head1 OPTIONS
1379
 
1380
=over 8
1381
 
1382
=item B<-help>
1383
 
1384
Print a brief help message and exits.
1385
 
1386
=item B<-help -help>
1387
 
1388
Print a detailed help message with an explanation for each option.
1389
 
1390
=item B<-man>
1391
 
1392
Prints the manual page and exits.
1393
 
1394
=item B<-verbose>
1395
 
1396
Increase the level of verbosity of the program execution
1397
 
1398
=item B<-debug>
1399
 
1400
Increase the debug output during program execution
1401
 
1402
 
1403
=item B<-default=target>
1404
 
1405
This options specifies the default target if none is provided on the command
1406
line. Used by the jats wrapper to simplify processing.
1407
 
1408
=back
1409
 
1410
FLAGS
1411
 
1412
=over 8
1413
 
1414
Flags are in the form TAG=value. This is a format that it used as they will be
1415
passed directly to the underlying makefiles. The recognised flags are:
1416
 
1417
=item B<SHOWENV=1>
1418
 
1419
This flag will force the 'Environment' to be displayed before commands are executed
1420
 
1421
=item B<LEAVETMP=1>
1422
 
1423
This flag will cause temp files, created by the build process, to notr be deleted.
1424
 
1425
=item B<NODEPEND=1>
1426
 
1427
This flag will supress dependency checking. Makefiles will not be created when
1428
the makefile.pl is changed. Source files will not be scanned for header files.
1429
 
1430
=item B<OPTIONS=list,list>
1431
 
1432
This flags passes a list of comma seperated options into the makefile. The exact
1433
set of available options is target specific. Refer to the JATS manual.
1434
 
1435
 
1436
=back