Subversion Repositories DevTools

Rev

Rev 7299 | Rev 7322 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
#! perl
2
########################################################################
7300 dpurdie 3
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
227 dpurdie 4
#
5
# Module name   : jats.sh
6
# Module type   : Perl Package
7
# Compiler(s)   : n/a
8
# Environment(s): jats
9
#
10
# Description   : This package contains functions to create a makefile entry
11
#                 The entry may consist of the following (optional) fields
12
#
13
#                 .PHONY <targets>
14
#                 <targets> : VARAIBLE = VALUE
15
#                 <targets> : <prerequisites>
16
#                       @echo "Run time comment"
17
#                       Recipe
18
#                       ( ShellRecipe )
19
#
20
#                 The order that information is added is not important
21
#                 Recipes and prerequisites may be mixed to simplify
22
#                 programming.
23
#
4344 dpurdie 24
#                 The --Define option creates
25
#                 define <name>
26
#                   Recipe
27
#                 endef
28
#
29
#                The --Raw option creates
30
#                   Recipe
31
#
4778 dpurdie 32
#                The --Raw with --Phony option creates
33
#                   .PHONY <targets>
34
#                   <targets>:
35
#                       Recipe
4344 dpurdie 36
#
4778 dpurdie 37
#
38
#
227 dpurdie 39
#......................................................................#
40
 
255 dpurdie 41
use 5.006_001;
227 dpurdie 42
use strict;
43
use warnings;
44
 
45
package MakeEntry;
46
use JatsError;
47
use ArrayHashUtils;
48
 
49
#
50
#   Global variables
51
#
52
my $llength = 80;                           # Target line length
53
 
54
 
55
#-------------------------------------------------------------------------------
56
# Function        : New
57
#
58
# Description     : Create a new empty entry
59
#
271 dpurdie 60
# Inputs          : handle              - FILEHANDLE or variable ref
61
#                                         eg: *MAKEFILE     - print to handle
62
#                                              \$var        - print to variable
63
#
64
#                   name                - Primary target name
65
#                   options             - Preload the fields
227 dpurdie 66
#                           --Target=name,name,name
67
#                           --Comment=text
68
#                           --Prereq=name,name,name
69
#                           --Recipe=text
70
#                           --Phony
4344 dpurdie 71
#                           --Define
72
#                           --Raw
227 dpurdie 73
#                           --Print
74
#
75
# Returns         : Ref to a an object that can be manipulated
76
#
77
sub New
78
{
79
    my ($handle, $name, @args ) = @_;
80
    my $self  = {};
81
    $self->{DEPENDANCY}     = [];
82
    $self->{NAME}           = [];
83
    $self->{RECIPE}         = [];
84
    $self->{SHELL}          = [];
85
    $self->{PRINTED}        = 0;
86
    $self->{COMMENT}        = [];
87
    $self->{DEFN}           = {};
88
    $self->{PHONY}          = 0;
4344 dpurdie 89
    $self->{DEFINE}         = 0;
90
    $self->{RAW}            = 0;
227 dpurdie 91
    $self->{FH}             = $handle;
271 dpurdie 92
    $self->{FH_inmemory}    = 0;
227 dpurdie 93
    $self->{RECIPE_PREFIX}  = '';
5708 dpurdie 94
    $self->{RECIPE_WRAPPER}  = ['(', ')'];
227 dpurdie 95
    $self->{RECIPE_COMMENT} = '';
4778 dpurdie 96
    $self->{STACK}          = [];
4781 dpurdie 97
    $self->{SDEF}           = '';
227 dpurdie 98
 
99
    push @{$self->{NAME}}, split(/,/,$name) if ( $name );
100
 
101
    bless ($self, __PACKAGE__);
102
 
103
    #
104
    #   Process any argument
105
    #
106
    my $print = 0;
107
    foreach ( @args )
108
    {
109
        if ( m/^--Target=(.*)/ ) {
110
            $self->AddName(split(/,/,$1));
111
 
112
        } elsif ( m/^--Comment=(.*)/ ) {
113
            $self->AddComment($1);
114
 
115
        } elsif ( m/^--Prereq=(.*)/ ) {
116
            $self->AddDependancy(split(/,/,$1));
117
 
118
        } elsif ( m/^--Phony/ ) {
119
            $self->Phony();
120
 
4344 dpurdie 121
        } elsif ( m/^--Define/ ) {
122
            $self->Define();
123
 
124
        } elsif ( m/^--Raw/ ) {
125
            $self->Raw();
126
 
227 dpurdie 127
        } elsif ( m/^--Recipe=(.*)/ ) {
128
            $self->AddRecipe($1);
129
 
130
        } elsif ( m/^--Print/ ) {
131
            $print = 1;
132
 
133
        } elsif ( m/^--/ ) {
134
            Error ("MakeEntry: Unknown option: $_");
135
 
136
        } else {
137
            $self->AddDependancy( $_ );
138
 
139
        }
140
    }
141
 
271 dpurdie 142
    #
143
    #   Set up the printer handle
144
    #   May be
145
    #       Empty           - use stderr
146
    #       ref to a scalar - use in memory file
147
    #       typeglob        - normal handle
148
    #
149
    if ( ref $handle eq 'SCALAR' )
150
    {
151
        $$handle = '' if ( ! defined $$handle );
152
        open(MEMORY,'>>', $handle) || Error ("Cannot open in-memory file");
153
        $self->{FH_inmemory} = $handle;
154
        $self->{FH} = \*MEMORY;
155
 
156
    } elsif ( ! $handle ) {
157
       $self->{FH} = \*STDERR;
158
    }
159
 
160
    #
161
    #   Print simple entry if required
162
    #
227 dpurdie 163
    $self->Print() if ( $print );
164
    return $self;
165
}
166
 
167
#-------------------------------------------------------------------------------
168
# Function        : AddName
169
#
4778 dpurdie 170
# Description     : Add a target name to the entry
227 dpurdie 171
#
172
# Inputs          : An array of names to add
173
#
174
# Returns         :
175
#
176
sub AddName
177
{
178
    my $self = shift;
179
    push @{$self->{NAME}}, @_
180
}
181
 
182
#-------------------------------------------------------------------------------
183
# Function        : Phony
184
#
185
# Description     : Flag the entry as a phony
186
#
187
# Inputs          :
188
#
189
# Returns         :
190
#
191
sub Phony
192
{
193
    my $self = shift;
194
    $self->{PHONY} = 1;
195
}
196
 
4344 dpurdie 197
#-------------------------------------------------------------------------------
198
# Function        : Define
199
#
200
# Description     : Flag the entry as a define, rather than a normal target
201
#                   Many of the other commands are not supported
202
#                   Only AddComment and AddRecipe
203
#
204
# Inputs          :
205
#
206
# Returns         :
207
#
208
sub Define
209
{
210
    my $self = shift;
211
    $self->{DEFINE} = 1;
212
}
227 dpurdie 213
 
214
#-------------------------------------------------------------------------------
4344 dpurdie 215
# Function        : Raw
216
#
217
# Description     : Flag the entry as raw text
218
#                   Many of the other commands are not supported
219
#                   Only AddComment and AddRecipe
220
#
221
# Inputs          :
222
#
223
# Returns         :
224
#
225
sub Raw
226
{
227
    my $self = shift;
228
    $self->{RAW} = 1;
229
}
230
 
231
#-------------------------------------------------------------------------------
227 dpurdie 232
# Function        : AddComment
233
#
234
# Description     : Add a comment to the entry
235
#
236
# Inputs          : An array of names to add
237
#
238
# Returns         :
239
#
240
sub AddComment
241
{
242
    my $self = shift;
243
    push @{$self->{COMMENT}}, @_
244
}
245
 
5991 dpurdie 246
 
227 dpurdie 247
#-------------------------------------------------------------------------------
5991 dpurdie 248
# Function        : QuoteDependency
249
#
250
# Description     : Escape/Quote a pathname for make
251
#                       Allow files with a $ in the name
252
#                       Allow files with a space in the name
253
#                       Allow files with a comma in the name
254
#                       Allow files with a colon in the name
255
#                       Allow for paths that have make-varible prefixes
6073 dpurdie 256
#                           $(GBE_...) or ${GBE_...} or $(OBJDIR)
5991 dpurdie 257
#                           as these may be generated internally
258
#
259
#                       Must also allow $(GBE_TYPE) in the remainder
260
#
261
# Inputs          : uarg                - Arg to quote
262
#
263
# Returns         : Quoted arg
264
#
265
 
266
sub QuoteDependency
267
{
268
    my ($uarg) = @_;
269
 
270
    #
271
    #   Split into two
272
    #       $(xxx)/             - Makefile variables
273
    #       Remainder           - Stuff to quote
274
    #
275
    $uarg =~ m~^((\$\(.*?\)/)*)(.*)~;
276
    my $prefix = defined $1 ? $1 : '';
277
    my $arg    = defined $3 ? $3 : '';
278
 
6073 dpurdie 279
    $arg =~ s~\$(?!\(GBE_[A-Z]+\)|{GBE_[A-Z]+}|\(OBJDIR\))~\$\$~g;       # $, not followed by (GBE_ or ${GBE_ or (OBJDIR)- is not $(GBE_ AND not $(OBJDIR)
5991 dpurdie 280
    $arg =~ s~ ~\\ ~g;
281
    $arg =~ s~,~\$(comma)~g;
282
    $arg =~ s~%~\\%~g;
5999 dpurdie 283
    $arg =~ s~:~\\:~g if ($::ScmHost eq 'Unix');
5991 dpurdie 284
    return $prefix . $arg;
285
}
286
 
287
#-------------------------------------------------------------------------------
288
# Function        : AddDependancyEscaped
289
#
290
# Description     : Add a dependancy to the entry and Quote the value so that
291
#                   it can be it can be processed by make
292
#
293
# Inputs          : An array of dependencies to add
294
#
295
# Returns         :
296
#
297
sub AddDependancyEscaped
298
{
299
    my $self = shift;
300
    my @escaped;
301
 
302
    push @escaped, QuoteDependency($_) foreach (@_);
303
    UniquePush $self->{DEPENDANCY}, @escaped ;
304
}
305
 
306
#-------------------------------------------------------------------------------
227 dpurdie 307
# Function        : AddDependancy
308
#
309
# Description     : Add a dependancy to the entry
5991 dpurdie 310
#                   These will not be escaped.
227 dpurdie 311
#
5991 dpurdie 312
# Inputs          : An array of dependencies to add
227 dpurdie 313
#
314
# Returns         :
315
#
316
sub AddDependancy
317
{
318
    my $self = shift;
319
    UniquePush $self->{DEPENDANCY}, @_ ;
320
}
321
 
322
#-------------------------------------------------------------------------------
323
# Function        : AddDefn
324
#
325
# Description     : Add a definition to the entry
326
#
327
# Inputs          : A hash of definitions to add
328
#                   The Hash gets lost in the subcall. Its simply a list
329
#                   of NAME, VALUE pairs
330
#
331
# Returns         :
332
#
333
sub AddDefn
334
{
335
    my $self = shift;
336
    while ( @_ )
337
    {
338
        my $defn = shift;
339
        my $value = shift || '';
340
        $self->{DEFN}{$defn} = $value;
341
    }
342
}
343
 
344
#-------------------------------------------------------------------------------
345
# Function        : RecipePrefix
346
#
347
# Description     : Set the recipe prefix
348
#                   This will be added to ALL recipe entries
349
#
350
# Inputs          :
351
#
352
# Returns         :
353
#
354
sub RecipePrefix
355
{
356
    my $self = shift;
357
    my $prefix = shift || '';
358
    $self->{RECIPE_PREFIX} = $prefix;
359
}
360
 
361
#-------------------------------------------------------------------------------
362
# Function        : RecipeComment
363
#
364
# Description     : Add a runtime comment to the entry
365
#
366
# Inputs          : String to print at runtime
367
#
368
# Returns         :
369
#
370
sub RecipeComment
371
{
372
    my $self = shift;
373
    $self->{RECIPE_COMMENT} = join( ' ', @_);
374
}
375
 
376
 
5708 dpurdie 377
#-------------------------------------------------------------------------------
378
# Function        : RecipeWrapper
379
#
380
# Description     : Add a wrapper around the recipe
381
#
382
# Inputs          : begin   - Start of wrapper
383
#                   end     - End of wrapper
384
#
385
# Returns         :
386
#
387
sub RecipeWrapper
388
{
389
    my $self = shift;
390
    Error("RecipeWrapper requires exacly two arguments") unless scalar @_ == 2;
391
    my ($begin, $end) = @_;
392
    $self->{RECIPE_WRAPPER}[0] = $begin;
393
    $self->{RECIPE_WRAPPER}[1] = $end;
394
}
227 dpurdie 395
 
396
#-------------------------------------------------------------------------------
397
# Function        : AddRecipe
398
#
399
# Description     : Add a line to the line-by-line recipe
400
#
401
# Inputs          : One or more recipe lines
402
#                   Each line of the recipe will be prefixed with the current
403
#                   recipe prefix.
404
#
405
#                   An array will be treated as a recipe with implicit line
4344 dpurdie 406
#                   breaks for formatting purposes.
227 dpurdie 407
#
408
# Returns         :
409
#
410
sub AddRecipe
411
{
412
    my $self = shift;
413
    my $prefix = $self->{RECIPE_PREFIX};
414
    foreach ( @_ )
415
    {
416
        if (ref($_) eq "ARRAY")
417
        {
418
            $_->[0] = $prefix . $_->[0];
419
            push @{$self->{RECIPE}}, $_;
420
        }
421
        else
422
        {
423
            push @{$self->{RECIPE}}, $prefix . $_;
424
        }
425
    }
426
}
427
 
428
#-------------------------------------------------------------------------------
429
# Function        : AddShellRecipe
430
#
431
# Description     : Add recipe lines that will be processed within a 'shell'
432
#
433
# Inputs          : One or more recipe lines
434
#
435
#                   An array will be treated as a recipe with implicit line
4778 dpurdie 436
#                   breaks for formatting purposes.
227 dpurdie 437
#
438
# Returns         :
439
#
440
sub AddShellRecipe
441
{
442
    my $self = shift;
443
    push @{$self->{SHELL}}, @_
444
}
445
 
4778 dpurdie 446
#-------------------------------------------------------------------------------
447
# Function        : NewSection 
448
#
449
# Description     : Create a new section within the current recipe
450
#                   Only used within a standard recipe (not raw or defined)
451
#                   Used to create multiple sections in cases with multiple shell
452
#                   and recipe sections.
453
#
454
#                   Save existing entries and start again
455
#
456
# Inputs          : None 
457
#
458
# Returns         : 
459
#
460
sub NewSection
461
{
462
    my $self = shift;
463
    my %data;
227 dpurdie 464
 
4778 dpurdie 465
    $data{RECIPE}           = $self->{RECIPE};
466
    $data{SHELL}            = $self->{SHELL};
467
    $data{RECIPE_PREFIX}    = $self->{RECIPE_PREFIX};
468
    $data{RECIPE_COMMENT}   = $self->{RECIPE_COMMENT};
5708 dpurdie 469
    $data{RECIPE_WRAPPER}   = $self->{RECIPE_WRAPPER};
4781 dpurdie 470
    $data{SDEF}             = $self->{SDEF};
471
 
4778 dpurdie 472
 
473
    push @{$self->{STACK}}, \%data;
474
 
475
    $self->{RECIPE}         = []; 
476
    $self->{SHELL}          = [];
477
    $self->{RECIPE_PREFIX}  = '';
478
    $self->{RECIPE_COMMENT} = '';
5708 dpurdie 479
    $self->{RECIPE_WRAPPER} = ['(', ')'];
4781 dpurdie 480
    $self->{SDEF}           = '';
4778 dpurdie 481
}
482
 
4889 dpurdie 483
#-------------------------------------------------------------------------------
484
# Function        : SectionIfDef 
485
#
486
# Description     : Place the current section within a ifdef ... endif block
487
#
488
# Inputs          : defn        - Definition to use within the ifdef construct 
489
#
490
# Returns         : Nothing
491
#
492
 
493
sub SectionIfDef
4781 dpurdie 494
{
495
    my $self = shift;
496
    $self->{SDEF} = $_[0];
497
}
498
 
227 dpurdie 499
#-------------------------------------------------------------------------------
500
# Function        : Print
501
#
502
# Description     : Print the entry
503
#
504
# Inputs          : None
505
#
506
# Returns         : Nothing
507
#
508
sub Print
509
{
510
    my $self = shift;
511
 
512
    #
513
    #   Set the default print stream to the desired stream
514
    #   This greatly simplifies the use of print
515
    #
516
    my $fh = $self->{FH};
517
    my $old_fh = select($fh);
518
 
271 dpurdie 519
 
227 dpurdie 520
    #
521
    #   A nice comment header
522
    #
523
    if ( @{$self->{COMMENT}} )
524
    {
525
        print( "#\n" );
526
        print map {"# $_\n"} @{$self->{COMMENT}};
527
        print( "#\n" );
528
    }
529
 
530
    #
531
    #   Print the targets
532
    #   Print PHONY information
533
    #   Print on multiple lines if multiple targets are very long
534
    #
535
    if ( $self->{PHONY} )
536
    {
537
        my $phony_string = ".PHONY: ";
538
        my $tstring = join $;, @{$self->{NAME}};
539
        my $join = length ($tstring) < $llength ? ' ' : "\n$phony_string";
540
        $tstring =~ s~$;~$join~g;
541
        print "$phony_string$tstring\n";
542
    }
543
 
544
    #
545
    #   Print any definitions
5986 dpurdie 546
    #       target: DEFN := VALUE
227 dpurdie 547
    #
548
    if ( $self->{DEFN}  )
549
    {
550
        my $tstring = join $;, @{$self->{NAME}};
551
        my $join = length ($tstring) < $llength ? ' ' : " \\\n    ";
552
        $tstring =~ s~$;~$join~g;
553
        foreach  ( keys %{$self->{DEFN}}  )
554
        {
555
            my $value = $self->{DEFN} {$_};
5986 dpurdie 556
            print( "$tstring: $_ := $value\n" );
227 dpurdie 557
        }
558
    }
559
 
4344 dpurdie 560
    if ( $self->{RAW}  )
227 dpurdie 561
    {
4778 dpurdie 562
        if ( $self->{PHONY} )
563
        {
564
            my $tstring = $self->{NAME}[0];
565
            print($tstring . ":\n");
566
        }
4344 dpurdie 567
        my $tstring = join ("\n", @{$self->{RECIPE}});
568
        print $tstring;
227 dpurdie 569
    }
4344 dpurdie 570
    elsif ( $self->{DEFINE}  )
571
    {
227 dpurdie 572
 
4344 dpurdie 573
        my $tstring = $self->{NAME}[0];
574
        print "define $tstring";
575
        print_list ( '', '', $self->{RECIPE}, '');
576
        print "\nendef";
577
    }
578
    else
579
    {
580
        #
581
        #   Print the main target name
582
        #   Print on multiple lines if multiple targets are very long
583
        #
584
        my $tstring = join $;, @{$self->{NAME}};
585
        my $join = length ($tstring) < $llength ? ' ' : " \\\n";
586
        $tstring =~ s~$;~$join~g;
587
        my $nlength = length $tstring;
588
        print "$tstring:";
589
 
590
        #
591
        #   Print the prerequisites
592
        #   Print on multiple lines if long
593
        #
594
        $tstring = join $;, @{$self->{DEPENDANCY}};
595
        $join = $nlength + length ($tstring) < $llength ? ' ' : " \\\n\t";
596
        $tstring =~ s~$;~$join~g;
597
        print $join . $tstring;
227 dpurdie 598
 
4344 dpurdie 599
        #
4778 dpurdie 600
        #   Push the current section onto the stack
601
        #   This will simplify processing of all sections
4344 dpurdie 602
        #
4778 dpurdie 603
        $self->NewSection();
604
        foreach my $recipeEntry (@{$self->{STACK}})
4344 dpurdie 605
        {
4778 dpurdie 606
            my $comment = $recipeEntry->{RECIPE_COMMENT};
5708 dpurdie 607
            my $wrapper = $recipeEntry->{RECIPE_WRAPPER};
4778 dpurdie 608
            my $prefix =  $recipeEntry->{RECIPE_PREFIX};
609
            my $recipe =  $recipeEntry->{RECIPE};
610
            my $shell =   $recipeEntry->{SHELL};
4781 dpurdie 611
            my $sdef =    $recipeEntry->{SDEF};
612
 
613
            if ($sdef)
614
            {
615
                print "\nifdef $sdef";
616
            }
617
 
4778 dpurdie 618
            #
619
            #   Print the Recipe runtime comment
620
            #
621
            if ( $comment )
622
            {
623
                print "\n\t\@echo \"$comment\"";
624
            }
625
 
626
            #
627
            #   Print the recipe
628
            #
629
            print_list ( '', '', $recipe, '');
630
 
631
            #
632
            #   Print the recipe as a shell command
633
            #   Bracket the recipes with ( .. ) and place semi colons between lines
634
            #   Use the current recipe prefix
635
            #
5708 dpurdie 636
            print_list ( $prefix . $wrapper->[0], ';\\', $shell, $wrapper->[1]);
4781 dpurdie 637
 
638
            if ($sdef)
639
            {
640
                print "\nendif";
641
            }
4344 dpurdie 642
        }
643
    }
644
 
227 dpurdie 645
    print "\n\n";
646
 
647
    #
648
    #   Flag the entry as having been printed
649
    #   Used as a sanity test when the object is destroyed
650
    #
651
    $self->{PRINT} = 1;
652
 
653
    #
654
    #   Restore default output stream handle
655
    #
656
    select($old_fh);
657
}
658
 
659
#-------------------------------------------------------------------------------
660
# Function        : print_list
661
#
662
# Description     : Internal helper rouitine
663
#
664
# Inputs          : $prefix     - Optional prefix to wrap the list
665
#                                 If present will cause extra indentation
666
#                   $linesep    - Line seperator string
667
#                   $ref        - Ref to the list to display
668
#                                 Each element will be a line of text, or an
669
#                                 array of arguments which will be displayed on
670
#                                 new lines for pretty printing
671
#                   $suffix     - Optional suffix.
672
#
673
# Returns         :
674
#
675
sub print_list
676
{
677
    my ($prefix, $linesep ,$ref, $suffix) = @_;
678
    my @entry = @{$ref};
679
    my $leadin = "\t";
680
 
681
    if ( @entry  )
682
    {
683
        if ( $prefix )
684
        {
685
            print( "\n" . $leadin . $prefix . "\\" );
686
            $leadin .= "\t";
687
        }
688
 
689
        foreach ( @entry )
690
        {
691
            if (ref($_) eq "ARRAY")
692
            {
693
                my @array = @{$_};
694
                my $indent = '';
695
                my $tail = '';
696
                foreach ( @array )
697
                {
698
                    print( $tail . "\n" . $leadin . $indent . $_ );
699
                    $indent = "\t";
700
                    $tail = " \\";
701
                }
702
                print $linesep;
703
            }
704
            else
705
            {
706
                print( "\n" . $leadin . $_ . $linesep );
707
            }
708
        }
709
 
710
        if ( $suffix )
711
        {
4778 dpurdie 712
            $leadin = chop($leadin) if ($prefix);
227 dpurdie 713
            print( "\n" . $leadin . $suffix );
714
        }
715
    }
716
}
717
 
718
#-------------------------------------------------------------------------------
719
# Function        : DESTROY
720
#
721
# Description     : Sanity test
722
#
723
# Inputs          :
724
#
725
# Returns         :
726
#
727
sub DESTROY
728
{
729
    my $self = shift;
730
    unless ($self->{PRINT} )
731
    {
732
        $self->{PRINT} = 1;
733
        Error ("Destroying MakeEntry before printing.",
734
               "Name: @{$self->{NAME}}");
735
    }
271 dpurdie 736
 
737
    #
738
    #   If using an in-memory file close the handle
739
    #
740
    close $self->{FH} if ( $self->{FH_inmemory} );
227 dpurdie 741
}
742
 
743
1;
744
 
745