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
 
24
require 5.6.1;
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 ) );
285
}
286
 
287
#
288
#   Read in the local Makefile.gbe file
289
#   This will provide a path to the interface directory
290
#
291
ReadMakeInfo();
292
 
293
#
294
#   Read in the global build variables
295
#
296
ReadBuildConfig( "$::ScmRoot/$::ScmInterface" );
297
 
298
#
299
#   Determine the max size of the target name
300
#   Makes a nice display
301
#
302
foreach ( @BUILDPLATFORMS )
303
{
304
    my $len = length ($_);
305
    $tlen = $len if ( $len > $tlen );
306
}
307
 
308
#-------------------------------------------------------------------------------
309
#   Scan the list of targets and perform 'alias' expansion
310
#   targets are of the form
311
#       platform_command_type
312
#       Where:
313
#           type        may be _prod or _debug or not present
314
#           command     may be either a composite command or a simple command
315
#           platform    may be a platform or an alias
316
#
317
#
318
foreach my $arg ( @targets )
319
{
320
    my $suffix = '';
321
    my @commands;
322
    my @platforms;
323
 
324
    #
325
    #   Remove the only available suffix ( _debug | _prod )
326
    #   Also detect commnads of debug or prod
327
    #
328
    if ( $arg =~ m~(.+)_(debug)$~ ) {
329
        $suffix = $2;
330
        $arg = $1;
331
    } elsif ( $arg =~ m~(.+)_(prod)$~ ) {
332
        $suffix = $2;
333
        $arg = $1;
334
    } elsif ( $arg eq 'debug' ) {
335
        $suffix = $arg;
336
        $arg = '';
337
    } elsif ( $arg eq 'prod' ) {
338
        $suffix = $arg;
339
        $arg = '';
340
    }
341
 
342
    #
343
    #   Remove valid make commands from the remaining argument
344
    #   Have a hash of all known makefile commands
345
    #
346
    foreach my $cmd ( keys %composite_commands, keys %commands )
347
    {
348
        if ( $arg eq $cmd )
349
        {
350
            @commands = ExpandComposite($cmd);
351
            $arg = '';
352
        }
353
        elsif ( $arg =~ m~(.+)_($cmd)$~ )
354
        {
355
            $arg = $1;
356
            @commands = ExpandComposite($2);
357
            last;
358
        }
359
    }
360
 
361
    #
362
    #   See of we can extract an platform alias from the command
363
    #   Rip into target + command
364
    #
365
    if ( $arg )
366
    {
367
        if ( exists $ScmBuildAliases{$arg}  )
368
        {
369
            @platforms =  split (/ /, $ScmBuildAliases{$arg} );
370
        }
371
        elsif ( exists $ScmBuildPlatforms{$arg} )
372
        {
373
            @platforms = ($arg);
374
        }
375
        else
376
        {
377
            if ( @commands )
378
            {
379
                Error ("Unknown target: $arg");
380
            }
381
            else
382
            {
383
                Message ("Unknown command. Passed to make: $arg");
384
                @commands = $arg;
385
            }
386
        }
387
    }
388
 
389
    #
390
    #   Insert defaults
391
    #
392
    @platforms = @DEFBUILDPLATFORMS
393
        unless ( @platforms );
394
 
395
    @commands = ExpandComposite('all')
396
        unless ( @commands);
397
 
398
    #
399
    #   Perform GBE_PLATFORM filtering
400
    #
401
    if ( %gbe_platform )
402
    {
403
        my @filter;
404
        foreach ( @platforms )
405
        {
406
            push @filter, $_ if ( exists $gbe_platform{$_} );
407
        }
408
        @platforms = @filter;
409
    }
410
 
411
    Error ("No platforms to be processed. Check GBE_PLATFORM")
412
        unless ( @platforms );
413
 
414
    #
415
    #   Iterate commands over platforms
416
    #
417
    foreach my $cmd ( @commands )
418
    {
419
        PerformCommand (\@platforms, $cmd, $suffix );
420
    }
421
}
422
 
423
#
424
#   All done
425
#
426
exit (0);
427
 
428
#-------------------------------------------------------------------------------
429
# Function        : PerformCommand
430
#
431
# Description     : Perform the make on a platform command
432
#
433
# Inputs          : $platform_ref   - platforms to process
434
#                   $cmd            - simple make command
435
#                   $suffix         - debug,prod,none
436
#
437
# Returns         : 
438
#
439
sub PerformCommand
440
{
441
    my ($platform_ref, $cmd, $suffix) = @_;
442
    my $do_prod = 1;
443
    my $do_debug = 1;
444
 
445
    #
446
    #   Clean up some ugly commands from lint and ctags
447
    #
448
    if ( $cmd =~ m~(.+)_(debug)$~ )
449
    {
450
        $cmd = $1;
451
        $suffix = $2;
452
    }
453
    Verbose2 ("Processing Target: $cmd, $suffix");
454
 
455
    #
456
    #   Limit command to production or debug
457
    #
458
    if ( $suffix eq 'debug' ) {
459
        $do_prod  = 0;
460
    } elsif ( $suffix eq 'prod' ) {
461
        $do_debug  = 0;
462
    }
463
 
464
    #
465
    #   Some commands can be suppressed by options
466
    #
467
    if ( exists $commands{$cmd}{'unless'}  )
468
    {
469
        my $check = $commands{$cmd}{'unless'};
470
        if ( exists $defs{$check} && $defs{$check} )
471
        {
472
            Verbose2 ("Skipping: $cmd because $check");
473
            return;
474
        }
475
    }
476
 
477
    #
478
    #   Interecpt commands that are handled locally
479
    #
480
    if ( exists $commands{$cmd}{'local'} )
481
    {
482
        $commands{$cmd}{'local'}( $cmd, $platform_ref );
483
        return;
484
    }
485
 
486
    #
487
    #   Process and recurse directory tree
488
    #
489
    Verbose ("Processing: $cmd, @{$platform_ref}");
490
 
491
    #
492
    #   Determine the local tag name and state
493
    #   The tag is used to short circuit makefile commands in recursive makes
494
    #   It greatly reduces the work required
495
    #
496
    #   Many commands have a tag that is the same as the command, but a few don't
497
    #
498
    my $tag = 0;
499
    if ( exists $commands{$cmd}{tag}  )
500
    {
501
        $tag = $commands{$cmd}{tag};
502
        $tag = $cmd if ( $tag eq '1' );
503
    }
504
 
505
    if ( $commands{$cmd}{'common'} )
506
    {
507
        InvokeSubMake( $::Cwd, $platform_ref ,$cmd, $tag, 1 );
508
    }
509
    else
510
    {
511
        InvokeSubMake( $::Cwd, $platform_ref ,$cmd, $tag, 1 ) if $do_debug;
512
        InvokeSubMake( $::Cwd, $platform_ref ,$cmd, $tag, 0 ) if $do_prod;
513
    }
514
}
515
 
516
#-------------------------------------------------------------------------------
517
# Function        : InvokeSubMake
518
#
519
# Description     : Build recursion
520
#                   Determine if there is anything to be done for the current
521
#                   target within a specified directory
522
#
523
# Inputs          : $dir
524
#                   $pref                   - Ref to an array of platforms
525
#                   $cmd
526
#                   $tag
527
#                   $do_prod
528
#
529
# Returns         : 
530
#
531
sub InvokeSubMake
532
{
533
    my ( $dir, $pref, $cmd, $tag, $do_debug) = @_;
534
 
535
    #
536
    #   Ensure the current directory is known to the Tags database
537
    #
538
    $Tags = ReadMaketags( $dir );
539
    Error ("Directory not in tag database", $dir ) unless ( $Tags );
540
 
541
    #
542
    #   Process commands in the current directory
543
    #   If the command is tagged, then we may be able to skip it
544
    #   Otherwise we need to do it
545
    #
546
    InvokeMake( $dir, $pref, $cmd, $tag, $do_debug );
547
 
548
    #
549
    #   Recurse into any subdirectories that may be a part of the build
550
    #
551
    unless ( exists $defs{NORECURSE} && $defs{NORECURSE}  )
552
    {
553
        #
554
        #   Process subdirectories for the current command
555
        #   If the command is tagged, then we may be able to skip it
556
        #   Otherwise we need to do it
557
        #
558
        foreach my $subdir (@{$Tags->{$dir}{subdirs}} )
559
        {
560
 
561
            InvokeSubMake( CleanDirName( "$dir/$subdir"), $pref, $cmd, $tag, $do_debug );
562
        }
563
    }
564
}
565
 
566
#-------------------------------------------------------------------------------
567
# Function        : InvokeMake
568
#
569
# Description     : Actually invoke the make for debug and production as required
570
#
571
# Inputs          : $dir
572
#                   $pref
573
#                   $cmd
574
#                   $tag
575
#                   $do_debug
576
#
577
#
578
# Returns         : 
579
#
580
sub InvokeMake
581
{
582
    my ( $dir, $pref, $cmd, $tag, $do_debug ) = @_;
583
    my $text = 'C';
584
 
585
    #
586
    #   Ensure that the current directory actually has makefile targets
587
    #   Not all makefile.pl create .mk files
588
    #       1) Top-level makefile.pl
589
    #       2) Makefiles that use --NoPlatformBuilds
590
    #
591
    if ( $Tags->{$dir}{root} )
592
    {
593
        Verbose2 "Root directory has no makefiles: $dir";
594
        return;
595
    }
596
 
597
    if ( $Tags->{$dir}{noplatforms} )
598
    {
599
        Verbose2 "No make targets in $dir";
600
        return;
601
    }
602
 
603
    #
604
    #   Process each platform
605
    #
606
    foreach my $target ( @{$pref} )
607
    {
608
        unless ( exists $Tags->{$dir}{platforms}{$target} )
609
        {
610
            Verbose2 "No make targets in $dir, for $target";
611
            next;
612
        }
613
 
614
        #
615
        #   Do we need to do any thing for this target / tag
616
        #
617
        if ( $tag )
618
        {
619
            unless ( exists $Tags->{$dir}{full}{$target}{'%MakeTags'}{$tag} )
620
            {
621
                Verbose2 ("Skipping $cmd in $dir");
622
                next;
623
            }
624
        }
625
 
626
        #
627
        #   common mode == 2
628
        #   Only process target ONCE for each platform
629
        #   Don't care when its used
630
        #
631
        if ( exists $commands{$cmd}{common} && $commands{$cmd}{common} & 2 )
632
        {
633
            if ( $common_cmd{$cmd}{$target} )
634
            {
635
                Verbose2 "Already performed $cmd for $target";
636
                next;
637
            }
638
            $common_cmd{$cmd}{$target} = 1;
639
        }
640
 
641
        #
642
        #   Is the build limited to only debug or production
643
        #
644
        unless ( exists $commands{$cmd}{common} )
645
        {
646
            if ( $do_debug == 0 ) {
647
                next unless ( $Tags->{$dir}{platforms}{$target}{prod} );
648
                $text = 'P';
649
 
650
            } elsif  ( $do_debug == 1 ) {
651
                next unless ( $Tags->{$dir}{platforms}{$target}{debug} );
652
                $text = 'D';
653
            }
654
        }
655
 
656
        #
657
        #   Final sanity test
658
        #   Must have the makefile. We should have detected this error before now
659
        #
660
        Error ("Makefile not found - $target") unless ( -f "$dir/$target.mk" );
661
 
662
        #
663
        #   Build up the make command line
664
        #   Examine command specfic flags
665
        #
666
        my @args = @defs;
667
        push @args, "IFLAG=$commands{$cmd}{IFLAG}" if ( exists $commands{$cmd}{IFLAG} );
668
        push @args, "NOSCMDEPEND=1" if ( exists $commands{$cmd}{nomakecheck} );
669
 
670
        my $cdir = CleanDirName ($dir);
671
        my @make_command;
672
        push @make_command, "$ENV{GBE_BIN}/xmake";
673
        push @make_command, "-s" if $opt_silent;
674
        push @make_command, "--no-print-directory";
675
        push @make_command, "-C", $cdir unless ( $cdir eq $::Cwd );
676
        push @make_command, "-f", "$target.mk";
677
        push @make_command, @args;
678
        push @make_command, 'make_dir' unless exists $commands{$cmd}{nomakecheck} ;
679
        push @make_command, $cmd;
680
 
681
        Message ( sprintf ("[$text] %-${tlen}s, $cmd, $cdir", $target ));
682
        System (@make_command, "DEBUG=$do_debug" );
683
    }
684
}
685
 
686
#-------------------------------------------------------------------------------
687
# Function        : TestMakeFiles
688
#
689
# Description     : Test all the makefile dependent files to see if any of the
690
#                   makefiles need to be rebuilt.
691
#
692
#                   Walk the directory tree looking for makefiles to be
693
#                   rebuilt.
694
#
695
# Inputs          : $cmd            - Current command
696
#                   $pref           - Ref to an array of platforms
697
#
698
# Returns         : 
699
#
700
sub TestMakeFiles
701
{
702
    my ( $cmd, $pref ) = @_;
703
    Verbose ("Test makefile dependencies");
704
 
705
    #
706
    #   Read in the Tag file for the current directory
707
    #   This will provide the current list of subdirectories
708
    #
709
    #   Process Test the current makefiles, then test any subdirs
710
    #
711
    TestSubMake( $cmd, $Cwd, $pref );
712
 
713
    #
714
    #   Report build errors
715
    #   Done after all makefiles have been processed
716
    #
717
    if ( $build_error )
718
    {
719
        Error ("Error creating makefiles. Errors previously reported");
720
    }
721
 
722
    #
723
    #   Post makefile build processing
724
    #   Only required if a files have been re-built
725
    #
726
    if ( $update )
727
    {
728
        #
729
        #   Sanity test the makefile structure
730
        #   Ensure that makefiles have only one parent.
731
        #
732
        TestParents();
733
 
734
        #
735
        #   Sanity test the installed and packaged files
736
        #   Generate warnings and errors if a file if packaged from two
737
        #   locations
738
        #
739
        TestPackages();
740
 
741
        #
742
        #   Generate DPACKAGE file if required
743
        #
744
        JatsDPackage::DPackageGenerate( $::ScmRoot, $::ScmInterface  );
745
    }
746
 
747
    #
748
    #
749
    #   Check that the build.pl file is not newer that the tags file
750
    #   This will not be an error, but it will be a nagging warning
751
    #
752
    my @build_warn;
753
    my $bstamp = -M "$::ScmRoot/$ScmBuildSrc";
754
    my $tstamp = -M "$::ScmRoot/Makefile.gbe";
755
 
756
    push @build_warn, "Missing: Makefile.gbe" unless ( defined $tstamp );
757
    push @build_warn, "Modified build file ($ScmBuildSrc)" if ( $tstamp && $bstamp < $tstamp );
758
    if ( @build_warn )
759
    {
760
        Warning ("The build file is newer than the generated files",
761
                 "The project needs to be built for these changes to be included",
762
                 @build_warn );
763
    }
764
}
765
 
766
#-------------------------------------------------------------------------------
767
# Function        : TestSubMake
768
#
769
# Description     : Test the makefiles in the current directory
770
#                   Recurse into subdirectories
771
#
772
# Inputs          : $cmd            - Current command
773
#                   $dir            - Directory to test
774
#                   $pref           - Ref to an array of platforms
775
#
776
# Returns         : 
777
#
778
sub TestSubMake
779
{
780
    my ($cmd, $dir, $pref ) = @_;
781
 
782
    $Tags = ReadMaketags( $dir, 1 );
783
    unless ( $Tags )
784
    {
785
        Verbose2 ("TestSubMake :Directory not in tag database", $dir );
786
        MakeBuild( $dir);
787
    }
788
    else
789
    {
790
        TestMake( $cmd, $dir, $pref );
791
    }
792
 
793
    #
794
    #   Recurse into any subdirectories that may be a part of the build
795
    #
796
    unless ( exists $defs{NORECURSE} && $defs{NORECURSE}  )
797
    {
798
        foreach my $subdir (@{$Tags->{$dir}{subdirs}} )
799
        {
800
            TestSubMake( $cmd, CleanDirName( "$dir/$subdir"), $pref );
801
        }
802
    }
803
}
804
 
805
#-------------------------------------------------------------------------------
806
# Function        : TestMake
807
#
808
# Description     : Test the makefiles component files
809
#                   and determine if the makefile(s) need to be rebuilt
810
#
811
# Inputs          : $cmd            - Current command
812
#                   $dir            - Directory to test
813
#                   $pref           - Ref to an array of platforms
814
#
815
# Returns         : 
816
#
817
sub TestMake
818
{
819
    my ( $cmd, $dir, $pref ) = @_;
820
    my @must_rebuild;
821
 
822
    if ( $cmd eq 'rebuild' )
823
    {
824
        MakeBuild( $dir);
825
        return;
826
    }
827
 
828
    #
829
    #   Check that the local makefile.pl is not newer than
830
    #   the Tags file. Not all makefile.pl's create .mk files
831
    #
832
    my $mstamp = -M "$dir/makefile.pl";
833
    my $tstamp = -M "$dir/Makefile.gbe";
834
 
835
    unless ( defined $tstamp )
836
    {
837
        UniquePush (\@must_rebuild, "Missing: $dir/Makefile.gbe");
838
    }
839
 
840
    if ( $mstamp && $mstamp < $tstamp  )
841
    {
842
        UniquePush (\@must_rebuild, "Updated: $dir/makefile.pl");
843
    }
844
    else
845
    {
846
        if ( $Tags->{$dir}{root} )
847
        {
848
            Verbose2 "TestMake: Root directory has no makefiles: $dir";
849
            return;
850
        }
851
 
852
        if ( $Tags->{$dir}{noplatforms} )
853
        {
854
            Verbose2 "TestMake: No make targets in $dir";
855
            return;
856
        }
857
 
858
        #
859
        #   Process only the required build targets
860
        #
861
        foreach my $tgt ( keys %{$Tags->{$dir}{platforms}}   )
862
        {
863
            next if ( %gbe_platform && not exists $gbe_platform{$tgt} );
864
            #
865
            #   Locate all the makefile dependent files
866
            #
867
            my $data = $Tags->{$dir}{full}{$tgt}{'@ScmDepends'};
868
            if ( $data )
869
            {
870
                Verbose2 ("Processing: $dir : $tgt");
871
                my $base_stamp = -M "$dir/$tgt.mk";
872
                unless ( defined $base_stamp )
873
                {
874
                    UniquePush (\@must_rebuild, "Missing: $dir/$tgt.mk");
875
                }
876
                else
877
                {
878
                    foreach my $file ( "$dir/makefile.pl" ,@$data )
879
                    {
880
                        $file = "$dir/$file" if ( $file =~ m~^\.~ );
881
                        my $stamp = -M $file;
882
                        if ( defined $stamp )
883
                        {
884
                            Verbose3 ("Timestamp: $stamp, $file");
885
                            if ( $stamp < $base_stamp  )
886
                            {
887
                                UniquePush (\@must_rebuild, $file);
888
                            }
889
                        }
890
                        else
891
                        {
892
                            UniquePush (\@must_rebuild, "Missing: $file");
893
                        }
894
                    }
895
                }
896
            }
897
            else
898
            {
899
                Warning ("No dependency information for: $tgt in $dir");
900
            }
901
        }
902
    }
903
 
904
    if ( @must_rebuild )
905
    {
906
        my @display;
907
        push (@display, CleanDirName($_) ) foreach ( @must_rebuild );
908
        Message ("One or more JATS source or internal files have changed, Makefiles will be rebuilt",
909
                  "Target is: $dir",
910
                  @display );
911
        MakeBuild ($dir);
912
    }
913
    return;
914
}
915
 
916
#-------------------------------------------------------------------------------
917
# Function        : MakeBuild
918
#
919
# Description     : Rebuild the makefiles in the specified directory
920
#                   This does not involve recursion - thats already been
921
#                   done.
922
#
923
#                   Once the makefiles have been re-built the tags database
924
#                   must be read in again as it may have changed.
925
#
926
# Inputs          : 
927
#
928
# Returns         : 
929
#
930
sub MakeBuild
931
{
932
    my ($dir) = @_;
933
 
934
    #
935
    #   No makefiles to build in the root directory
936
    #   Can't rebuild the top-level Makefile.gbe file
937
    #
938
    return if ( $Tags->{$dir}{root} );
939
 
940
    #
941
    #   Try to rebuild the makefile
942
    #
943
    my @args;
944
    push @args, '--expert' if ($ScmExpert);
945
    push @args, "--interface=$::ScmInterface";
946
    push @args, '-F';
947
 
948
    chdir $dir || Error ("Cannot change directory to $dir");
949
    my $result = System ('--NoExit',$ENV{GBE_PERL}, 'makefile.pl', $::ScmRoot, $makelib, @args );
950
    chdir $::Cwd || Error ("Cannot change directory to $::Cwd");
951
 
952
    #
953
    #   Re-read the tags database
954
    #
955
    $Tags = ReadMaketags( $dir, 2 );
956
 
957
    #
958
    #   Flag that makefiles have been modified
959
    #
960
    $update = 1;
961
    $build_error = 1 if ( $result && $result != 10 );
962
}
963
 
964
#-------------------------------------------------------------------------------
965
# Function        : UnMakeFiles
966
#
967
# Description     : Delete generated makefiles and control files
968
#
969
# Inputs          : $cmd            - Current command
970
#                   $pref           - Ref to an array of platforms
971
#
972
# Returns         : 
973
#
974
sub UnMakeFiles
975
{
976
    #
977
    #   Recurse down the tree
978
    #   Recurse, then performs operations in the current directory
979
    #
980
    sub UnSubMakeFiles
981
    {
982
        my ($dir) = @_;
983
        $Tags = ReadMaketags( $dir, 1 );
984
        foreach my $subdir (@{$Tags->{$dir}{subdirs}} )
985
        {
986
            UnSubMakeFiles( CleanDirName( "$dir/$subdir") );
987
        }
988
        UnMake( $dir );
989
    }
990
 
991
    #
992
    #   Delete makefiles in the current directory
993
    #
994
    sub UnMake
995
    {
996
        my ($dir) = @_;
997
        Verbose("Unmake in $dir");
998
        foreach my $tgt ( keys %{$Tags->{$dir}{platforms}}   )
999
        {
1000
            Verbose2("Unmake. Delete ${tgt}.mk");
1001
            unlink ("$dir/${tgt}.mk");
1002
        }
1003
        unlink ("$dir/Makefile.gbe");
1004
        Verbose2("Unmake. Delete Makefile.gbe");
1005
    }
1006
 
1007
    #
1008
    #   Depth first recursion through the tree
1009
    #
1010
    UnSubMakeFiles ( $Cwd );
1011
}
1012
 
1013
#-------------------------------------------------------------------------------
1014
# Function        : TestParents
1015
#
1016
# Description     : Ensure that each makefile node has exactly one parent
1017
#                   Prevent the user from creating recursive loops
1018
#
1019
# Inputs          : 
1020
#
1021
# Returns         : 
1022
#
1023
my %parents;
1024
sub TestParents
1025
{
1026
    Verbose ("Test makefile parents");
1027
    #
1028
    #   Init the hash. Command may be invoked multiple times
1029
    #
1030
    %parents = ();
1031
 
1032
    #
1033
    #   Recurse down the tree
1034
    #   Recurse, then performs operations in the current directory
1035
    #
1036
    sub RecurseDown
1037
    {
1038
        my ($dir) = @_;
1039
        $Tags = ReadMaketags( $dir, 1 );
1040
        Verbose2("TestParents in $dir");
1041
        foreach my $subdir (@{$Tags->{$dir}{subdirs}} )
1042
        {
1043
            my $subdir = CleanDirName( "$dir/$subdir");
1044
            push @{$parents{$subdir}}, $dir;
1045
            RecurseDown( $subdir );
1046
        }
1047
    }
1048
 
1049
    #
1050
    #   Depth first recursion through the tree
1051
    #
1052
    RecurseDown ( $Cwd );
1053
 
1054
    #
1055
    #   Test for only one parent
1056
    #   The root makefile won't have any
1057
    #
1058
    foreach my $dir ( sort keys %{$Tags} )
1059
    {
1060
        my $count = $#{$parents{$dir}};
1061
        if ( $count > 0 )
1062
        {
1063
            if ( defined($GBE_ABT) )
1064
            {
1065
                Warning ("Error supressed by ABT");
1066
                Warning ("makefile.pl with multiple parents",
1067
                         "makefile.pl in: $dir",
1068
                         "Parented by:", @{$parents{$dir}} );
1069
            }
1070
            else
1071
            {
1072
                unlink $Tags->{$dir}{config};
1073
                Error ("makefile.pl with multiple parents",
1074
                       "makefile.pl in: $dir",
1075
                       "Parented by:", @{$parents{$dir}} );
1076
            }
1077
        }
1078
    }
1079
}
1080
 
1081
#-------------------------------------------------------------------------------
1082
# Function        : TestPackages
1083
#
1084
# Description     : Ensure that files that are packaged and installed are
1085
#                   only done from one makefile. Warn if the same file
1086
#                   is being packaged from multiple makefiles.
1087
#
1088
# Inputs          : 
1089
#
1090
# Returns         : 
1091
#
1092
my %test_packages;
1093
sub TestPackages
1094
{
1095
    Verbose ("Test packaged files");
1096
    #
1097
    #   Init the hash. Command may be invoked multiple times
1098
    #
1099
    %test_packages = ();
1100
 
1101
    #
1102
    #   Recurse down the tree
1103
    #   Recurse, then performs operations in the current directory
1104
    #
1105
    sub TRecurseDown
1106
    {
1107
        my ($dir) = @_;
1108
        Verbose2("TestPackages in $dir");
1109
        $Tags = ReadMaketags( $dir, 1 );
1110
        #
1111
        #   Process makefile
1112
        #   Examine all the %PACKAGE_xxx and %INSTALL_xxx fields thare listed
1113
        #   in the @PACKAGE_VARS and @INSTALL_VARS array
1114
        #
1115
        foreach my $target ( keys %{$Tags->{$dir}{platforms}}   )
1116
        {
1117
            next if ( %gbe_platform && not exists $gbe_platform{$target} );
1118
            foreach my $field ( @{$Tags->{$dir}{full}{$target}{'@PACKAGE_VARS'}},
1119
                                @{$Tags->{$dir}{full}{$target}{'@INSTALL_VARS'}} )
1120
            {
1121
                foreach my $entry ( keys %{$Tags->{$dir}{full}{$target}{$field}} )
1122
                {
1123
                    Verbose3("TestPackages: $target, File: $entry");
1124
                    push @{$test_packages{$target}{$entry}}, $dir;
1125
                }
1126
            }
1127
        }
1128
 
1129
        #
1130
        #   Process subdirectories too
1131
        #
1132
        foreach my $subdir (@{$Tags->{$dir}{subdirs}} )
1133
        {
1134
            TRecurseDown( CleanDirName( "$dir/$subdir") );
1135
        }
1136
    }
1137
 
1138
 
1139
 
1140
    #
1141
    #   Depth first recursion through the tree
1142
    #
1143
    TRecurseDown ( $Cwd );
1144
 
1145
    #
1146
    #   Test and report files that are packaged in different makefiles
1147
    #   There are two issues:
1148
    #       1) Not good practice
1149
    #       2) May be different files
1150
    #   The warning message is a bit ugly - but it shouldn't pop up often.
1151
    #
1152
#    DebugDumpData ("test_packages", \%test_packages);
1153
 
1154
    foreach my $target ( sort keys %test_packages )
1155
    {
1156
        foreach my $file ( sort keys %{$test_packages{$target}} )
1157
        {
1158
            #
1159
            #   The descpkg file is known to be packaged from multiple dirs
1160
            #
1161
            next if ( $file =~ m~/descpkg~ );
1162
            if ( $#{$test_packages{$target}{$file}} > 0 )
1163
            {
1164
                Warning ("File is packaged or installed from multiple makefiles",
1165
                         "Target: $target, Packaged file: $file",
1166
                         "Packaged from the following directories:", @{$test_packages{$target}{$file}} );
1167
            }
1168
        }
1169
    }
1170
}
1171
 
1172
 
1173
#-------------------------------------------------------------------------------
1174
# Function        : DoHelp
1175
#
1176
# Description     : Display basic help information
1177
#
1178
# Inputs          : None
1179
#
1180
# Returns         :
1181
#
1182
sub DoHelp
1183
{
1184
 
1185
#
1186
#   Display basic usage information
1187
#
1188
    pod2usage(-verbose => 0,
1189
              -message => "Version: $VERSION",
1190
              -exitval => 'NOEXIT');
1191
 
1192
    print "Platform targets\n";
1193
    foreach ( sort keys %BUILDINFO )
1194
    {
1195
        print "    $_\n";
1196
    }
1197
 
1198
    my @alias = sort keys %ScmBuildAliases;
1199
    if ( $#alias >=0 )
1200
    {
1201
        print "Alias targets\n";
1202
        foreach ( @alias )
1203
        {
1204
            print "    $_   - $ScmBuildAliases{$_}\n";
1205
        }
1206
    }
1207
}
1208
 
1209
#-------------------------------------------------------------------------------
1210
# Function        : ReadMaketags
1211
#
1212
# Description     : Read in the global make tags data base
1213
#                   This contains information on all the components within
1214
#                   the build. The tags allow recursive makes to be pruned.
1215
#
1216
#                   The file may change while the build is running
1217
#                   It can be read multiple times
1218
#
1219
# Inputs          : $dir            - Directory entry to read
1220
#                   $test           - 1: Read if available
1221
#                                     2: Force read
1222
#
1223
# Returns         : Reference to the tag data for the requested directory
1224
#                   Will error if no available
1225
#
1226
our %cf_minfo;
1227
our %cf_minfo2;
1228
my %tag_data;
1229
sub ReadMaketags
1230
{
1231
    my ($dir, $test) = @_;
1232
 
1233
    #
1234
    #   Force data re-aquisition
1235
    #
1236
    delete $tag_data{$dir}
1237
        if ( $test && $test == 2 );
1238
 
1239
    #
1240
    #   Do we already have the entry
1241
    #
1242
    return \%tag_data
1243
        if ( exists ($tag_data{$dir} ));
1244
 
1245
    #
1246
    #   If the entry is not known, then re-read the index file
1247
    #
1248
    unless ( $::cf_filelist{$dir} )
1249
    {
1250
        my $index_file = "$::ScmRoot/$::ScmInterface/Makefile.cfg";
1251
        Error ("Makefile index missing. Rebuild required") unless ( -f $index_file );
1252
 
1253
        #
1254
        #   Kill the entry in %INC to force the file to be read in
1255
        #
1256
        $::cf_filelist = ();
1257
        delete $INC{ $index_file };
1258
        require $index_file;
1259
    }
1260
 
1261
    my $cfg_filen = $::cf_filelist{$dir};
1262
    unless ( $cfg_filen )
1263
    {
1264
        return undef
1265
            if ( $test );
1266
        Error ("Makefile index entry missing: $dir. Rebuild required");
1267
    }
1268
 
1269
 
1270
    #
1271
    #   Read in all the Makefile_x.cfg files
1272
    #
1273
    %cf_minfo = ();
1274
    %cf_minfo2 = ();
1275
    my $cfg_file = "$::ScmRoot/$::ScmInterface/$cfg_filen";
1276
 
1277
    unless ( -f $cfg_file )
1278
    {
1279
        return undef
1280
            if ( $test );
1281
        Error ("Make data file missing: $cfg_file. Rebuild required");
1282
    }
1283
 
1284
    delete $INC{ $cfg_file };
1285
    require $cfg_file;
1286
 
1287
    Error ("Makefile info2 not present")
1288
        unless ( keys %::cf_info2 );
1289
 
1290
    Error ("Makefile info2 incorrect version. Rebuild required")
1291
        unless ( exists $::cf_info2{version} && $::cf_info2{version} eq 1 );
1292
 
1293
    %{$tag_data{$dir}} = %::cf_info2;
1294
    $tag_data{$dir}{config} = $cfg_file;
1295
    %{$tag_data{$dir}{full}} = %::cf_info;
1296
 
1297
#DebugDumpData ("ReadMaketags", $tag_data{$dir});
1298
    return \%tag_data;
1299
 
1300
}
1301
 
1302
#-------------------------------------------------------------------------------
1303
# Function        : ExpandComposite
1304
#
1305
# Description     : Expand a command via composite command expansion
1306
#                   A composite command may contain other composite commands
1307
#
1308
# Inputs          : $cmd    - command to expand
1309
#
1310
# Returns         : An array of commands
1311
#
1312
sub ExpandComposite
1313
{
1314
    my ($cmd) = @_;
1315
    my @cmds;
1316
    my @scmds = $cmd;
1317
    while ( @scmds )
1318
    {
1319
        my $cmd = shift @scmds;
1320
        if ( exists $composite_commands{$cmd} )
1321
        {
1322
            @scmds = (@{$composite_commands{$cmd}}, @scmds);
1323
        }
1324
        else
1325
        {
1326
            push @cmds, $cmd;
1327
        }
1328
    }
1329
 
1330
    return @cmds;
1331
}
1332
 
1333
 
1334
#-------------------------------------------------------------------------------
1335
#   Documentation
1336
#
1337
 
1338
=pod
1339
 
1340
=head1 NAME
1341
 
1342
jmake - JATS make support tool
1343
 
1344
=head1 SYNOPSIS
1345
 
1346
 Usage: jats make [options][targets][flags]
1347
 
1348
 Where options:
1349
    -h, -h -h, -man     - Help messages with increasing verbosity
1350
    -verbose            - Verbose operation
1351
    -debug              - Debug operation
1352
    -default=target     - Default 'target' if none is supplied
1353
 
1354
 Where flags are of the form Name=Value
1355
    SHOWENV=1           - Show make environment
1356
    LEAVETMP=1          - Leave temp working files
1357
    NODEPEND=1          - Ignore dependency checking
1358
    OPTIONS=[opt]       - Maketime options [args,allargs,filter...]
1359
 
1360
 Valid targets include:
1361
    all                 - build and install everything(p*)
1362
    build               - build everything (pu)
1363
    debug               - build all things for debug (pu)
1364
    prod                - build all things for production (pu)
1365
    install             - install public components (pu*)
1366
    lint                - lints the source (assumes debug)(p)
1367
    package             - build all packages (pu*)
1368
    package-{set}       - build specific package (see below) (pu*)
1369
    run_tests           - Run the tests specified in the makefile
1370
    run_unit_tests      - Run the automatic unit tests specified in the makefile
1371
    deploy              - Run the deployment scripts (p*)
1372
    rebuild             - recursively rebuild makefiles
1373
    depend              - construct the dependencies (u*)
1374
    rmlitter            - remove build litter (core, tmp and err) (*)
1375
    clean               - delete generate, obj, libraries and programs (p*)
1376
    clobber             - delete everything which can be remade (p*)
1377
    help                - A list of alias and platforms
1378
 
1379
      (u) undo target available (ie uninstall)
1380
      (p) optional [platform_] prefix targets (ie XXX_build)
1381
      (*) optional [_debug|_prod] postfix targets (ie clean_debug)
1382
 
1383
 
1384
=head1 OPTIONS
1385
 
1386
=over 8
1387
 
1388
=item B<-help>
1389
 
1390
Print a brief help message and exits.
1391
 
1392
=item B<-help -help>
1393
 
1394
Print a detailed help message with an explanation for each option.
1395
 
1396
=item B<-man>
1397
 
1398
Prints the manual page and exits.
1399
 
1400
=item B<-verbose>
1401
 
1402
Increase the level of verbosity of the program execution
1403
 
1404
=item B<-debug>
1405
 
1406
Increase the debug output during program execution
1407
 
1408
 
1409
=item B<-default=target>
1410
 
1411
This options specifies the default target if none is provided on the command
1412
line. Used by the jats wrapper to simplify processing.
1413
 
1414
=back
1415
 
1416
FLAGS
1417
 
1418
=over 8
1419
 
1420
Flags are in the form TAG=value. This is a format that it used as they will be
1421
passed directly to the underlying makefiles. The recognised flags are:
1422
 
1423
=item B<SHOWENV=1>
1424
 
1425
This flag will force the 'Environment' to be displayed before commands are executed
1426
 
1427
=item B<LEAVETMP=1>
1428
 
1429
This flag will cause temp files, created by the build process, to notr be deleted.
1430
 
1431
=item B<NODEPEND=1>
1432
 
1433
This flag will supress dependency checking. Makefiles will not be created when
1434
the makefile.pl is changed. Source files will not be scanned for header files.
1435
 
1436
=item B<OPTIONS=list,list>
1437
 
1438
This flags passes a list of comma seperated options into the makefile. The exact
1439
set of available options is target specific. Refer to the JATS manual.
1440
 
1441
 
1442
=back