Subversion Repositories DevTools

Rev

Rev 6276 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
245 dpurdie 1
########################################################################
6177 dpurdie 2
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
245 dpurdie 3
#
4
# Module name   : jats.sh
5
# Module type   : Makefile system
6
# Compiler(s)   : n/a
7
# Environment(s): jats
8
#
9
# Description   : Class to provide utilities associated with a locating
10
#                 build files and build file dependencies.
11
#
12
#......................................................................#
13
 
14
require 5.006_001;
15
use strict;
16
use warnings;
17
 
18
package JatsBuildFiles;
19
use JatsError;
20
use File::Find;
21
use BuildName;
22
use JatsVersionUtils;
23
 
24
our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION);
25
use Exporter;
26
 
27
$VERSION = 1.00;
28
@ISA = qw(Exporter);
29
 
30
# Symbols to autoexport (:DEFAULT tag)
31
@EXPORT = qw( BuildFileScanner
32
            );
33
 
34
 
35
#-------------------------------------------------------------------------------
36
# Function        : BuildEntry
37
#
38
# Description     : Create a BuildEntry Object
39
#                   This object describes a build file. It is passed to the
40
#                   user of this class.
41
#
42
#                   There are no object accessors.
43
#                   Just use the object as a reference to hash.
44
#
45
# Inputs          : $dir                - Dir of the build file
46
#                   $file               - Name of the build file
47
#                   $type               - Type 1:Jats, 2:ANT
48
#
49
# Returns         : Reference to an object
50
#
51
sub BuildEntry
52
{
53
    my $self  = {};
54
 
55
    $self->{dir} = shift;
56
    $self->{file}  = shift;
57
    $self->{type} = shift;
58
 
59
    #
60
    #   Other fields that are known:
61
    #       name
62
    #       version
63
    #       full
64
    #       mname
65
    #       project
66
    #
67
 
68
    bless ($self, 'BuildEntry');
69
    return $self;
70
}
71
 
72
#-------------------------------------------------------------------------------
73
# Function        : BuildFileScanner
74
#
75
# Description     : Create a new instance of a build file class
76
#                   This is the one exported function of this class
77
#                   It is a constructor to allow scanning for build files
78
#
79
# Inputs          : root            - Base pathname
80
#                   file            - Build filename
81
#                   options         - Options to be processed
82
#
83
#                   Options are:
84
#                   --ScanDependencies  - Collect information on dependent packages
85
#                   --LocateAll         - Scan for ANT and JATS build files
6133 dpurdie 86
#                   --LimitDepth=n      - Limit the depth of the scan
7213 dpurdie 87
#                   --stop              - Ignore if a 'stop' file exists in the directory
245 dpurdie 88
#
89
#
90
# Returns         : A reference to class.
91
#
92
sub BuildFileScanner {
93
    my $self  = {};
94
 
95
    $self->{root} = shift;
96
    $self->{file}  = shift;
97
    $self->{info} = [];
98
    $self->{scandeps} = 0;
99
    $self->{locateAll} = 0;             # Scan Jats and Ant files
6133 dpurdie 100
    $self->{LimitDepth} = 0;            # Skim the tree
7213 dpurdie 101
    $self->{Stop} = 0;                  # Support a 'stop' file
245 dpurdie 102
 
103
    bless ($self);
104
 
105
    Error ("Locating Build files. Root directory not found",
106
           "Path: $self->{root}" ) unless ( -d $self->{root} );
107
    #
108
    #   Process user arguments.
109
    #   These are treated as options. Leading '--' is optional
110
    #
111
    foreach ( @_ )
112
    {
113
        my $opt = '--' . $_;
114
        $opt =~ s~^----~--~;
115
        $self->option ($opt) || Error( "BuildFileScanner. Unknown initialiser: $_");
116
    }
117
    return $self;
118
}
119
 
120
#-------------------------------------------------------------------------------
121
# Function        : option
122
#
123
# Description     : Function to simplify the processing of arguments
124
#                   Given an argument this function will act on it or
125
#                   return false
126
#
127
# Inputs          : option          - One possible standard search option
128
#
129
# Returns         : True            - Option is an  option and its been
130
#                                     processed
131
#
132
sub option
133
{
134
    my ($self, $opt) = @_;
135
    my $result = 1;
136
 
137
    if ( $opt =~ m/^--ScanDependencies/ ) {
138
        $self->{scandeps} = 1;
139
 
359 dpurdie 140
    } elsif ( $opt =~ m/^--ScanExactDependencies/ ) {
141
        $self->{scandeps} = 2;
142
 
245 dpurdie 143
    } elsif ( $opt =~ m/^--LocateAll/ ) {
144
        $self->{locateAll} = 1;
145
 
7213 dpurdie 146
    } elsif ( $opt =~ m/^--Stop/ ) {
147
        $self->{Stop} = 1;
148
 
6133 dpurdie 149
    } elsif ( $opt =~ m/^--LimitDepth=(\d+)/ ) {
150
        $self->{LimitDepth} = $1;
151
 
245 dpurdie 152
    } else {
153
        $result = 0;
154
 
155
    }
156
    return $result;
157
}
158
 
159
#-------------------------------------------------------------------------------
6133 dpurdie 160
# Function        : getLocation 
161
#
162
# Description     : Serialize location data such that it can be used by the
6276 dpurdie 163
#                   setLocation function.
164
#                   Format:
165
#                       RootDirectory
166
#                       Number of BuildEntry(s) that follow
167
#                       BuildEntry
168
#                   Where each BuildEntry is:
169
#                       Path
170
#                       BuildFile
171
#                       Type 1:Jats, 2:ANT    
6133 dpurdie 172
#
173
# Inputs          :  $self
174
#
175
# Returns         :  Text string of serailised data
176
#
177
sub getLocation
178
{
179
    my ($self) = shift;
180
    my @locationData;
181
    push @locationData,  $self->{root};
182
    push @locationData,  scalar  @{$self->{info}};
183
    foreach my $be ( @{$self->{info}} )
184
    {
185
        push @locationData,  $be->{dir}, $be->{file}, $be->{type};
186
    }
187
    return (join($;, @locationData));
188
}
189
 
190
 
191
#-------------------------------------------------------------------------------
192
# Function        : setLocation 
193
#
194
# Description     : Insert location data
195
#                   Bypass the need to perform a 'locate' operation
196
#                   Used to cache location data in large systems 
197
#                   
198
#                   Will detect missing build files and allow the user to
199
#                   handle the error.
200
#
201
# Inputs          : $self
202
#                   ...     Location data as returned by getLocation
203
#
204
# Returns         : 1   - All Build files exist
205
#                   0   - At least one of the build files does not exist
206
#
207
sub setLocation
208
{
209
    my ($self, $data) = @_;
210
    my @locationData =  split($;, $data);
211
    my $rv = 1;
212
 
213
    my $root = shift @locationData;
214
    my $count = shift @locationData;
215
 
216
    while ($count-- > 0)
217
    {
218
        my $buildfile = join('/',$locationData[0], $locationData[1]);
219
        $rv = 0 unless -f $buildfile;    
220
 
221
        push @{$self->{info}}, BuildEntry( @locationData);
222
        splice @locationData, 0, 3;
223
    }
224
 
225
    $self->{locate_done} = 1;
226
    return $rv;
227
}
228
 
229
#-------------------------------------------------------------------------------
245 dpurdie 230
# Function        : locate
231
#
232
# Description     : Locate all build files within a given directory tree
233
#                   Collects the data and builds up a data structure
234
#
267 dpurdie 235
#                   If the file is an xml file, then we are looking for
236
#                   an ant pair of files.
237
#
245 dpurdie 238
# Inputs          : $self
239
#
240
# Returns         : Number of buildfiles found 0,1 ....
241
#
242
sub locate
243
{
244
    my ($self) = @_;
245
 
246
    #
247
    #   Locate all the build files that match the users request
6198 dpurdie 248
    #
6133 dpurdie 249
    my $ff_datap = \@{$self->{info}};
250
    my $ff_file = $self->{file};
251
    my $ff_all = $self->{locateAll};
252
    my $ff_self = $self;
253
    my $ff_ant = ( $ff_file =~ m~(.+)\.xml$~i ) ? $1 : '';
245 dpurdie 254
 
6133 dpurdie 255
    #
256
    #   Anonymous sub for the file::find wanted function
257
    #       Use closure to allow access to local variables
258
    #       Use no_chdir to allow very deep (windows) structures
259
    #
260
    my $wanted = sub 
245 dpurdie 261
    {
6133 dpurdie 262
        # Using no_chdir - extract just the filename
263
        my $file = $_;
264
        $file =~ s~.*/~~;
265
        Verbose3( "locateBuildFile: $file");
266
 
267
        if ( -d $_)
245 dpurdie 268
        {
6133 dpurdie 269
            #
270
            #   Skip known dirs
271
            #   
6177 dpurdie 272
            if ($file eq '.git' || $file eq '.svn' || $file eq 'lost+found')
6133 dpurdie 273
            {
274
                $File::Find::prune = 1;
275
                Verbose3( "locateBuildFile: PRUNE: $file");
276
                return;
277
            }
278
 
279
            #
280
            #   Limit the depth of the scan
6177 dpurdie 281
            #       Suggestion 3 or 4 below the package base
6133 dpurdie 282
            #
283
            if ($self->{LimitDepth})
284
            {
285
                my $depth = $File::Find::name =~ tr~/~/~;
286
                if ($depth >= $self->{LimitDepth})
287
                {
288
                    $File::Find::prune = 1;
289
                    Verbose3( "locateBuildFile: LimitDepth: $_");
290
                    return;
291
                }
292
            }
7213 dpurdie 293
 
294
            #
295
            #   Stop file processing
296
            #   If the directory conatins a 'stop' file then we won't find any build files in it
297
            #   Nor should we find any below it.
298
            #
299
            if ($self->{Stop})
300
            {
301
               if ( -f $File::Find::name . '/stop' )
302
               {
303
                   $File::Find::prune = 1;
304
                   Verbose0( "locateBuildFile: Stop file: $_");
305
                   return;
306
 
307
               }
308
            }
6133 dpurdie 309
        }
310
 
311
        if ( $file eq $ff_file  )
312
        {
267 dpurdie 313
            if ( $ff_ant )
314
            {
6133 dpurdie 315
                if ( -f ( $File::Find::dir . '/' . ${ff_ant} . 'depends.xml') )
267 dpurdie 316
                {
6133 dpurdie 317
                    Verbose ("locateBuildFile: FOUND $File::Find::dir, $file");
318
                    push @{$ff_datap}, BuildEntry( $File::Find::dir, $file, 2);
267 dpurdie 319
                }
320
            }
321
            else
322
            {
6133 dpurdie 323
                $file = 'auto.pl' if ( $ff_self->{scandeps} && -f 'auto.pl' );
324
                Verbose ("locateBuildFile: FOUND $File::Find::dir, $file");
325
                push @{$ff_datap}, BuildEntry( $File::Find::dir, $file, 1);
267 dpurdie 326
            }
327
            return;    
245 dpurdie 328
        }
329
 
330
        #
331
        #   Detect ANT {packagename}depends.xml file
6133 dpurdie 332
        #       These are file pairs (mostly)
6198 dpurdie 333
        #       Must not be empty
245 dpurdie 334
        #
6198 dpurdie 335
        if ( $ff_all && $file =~ m/(.+)depends.xml$/ && -s $file )
245 dpurdie 336
        {
6198 dpurdie 337
            my $baseFile = $File::Find::dir . '/' . $1 . '.xml';
338
            if ( -f $baseFile && -s $baseFile )
245 dpurdie 339
            {
6133 dpurdie 340
                Verbose ("locateBuildFile: FOUND $File::Find::dir, $file");
341
                push @{$ff_datap}, BuildEntry( $File::Find::dir, $file, 2);
245 dpurdie 342
            }
343
        }
6133 dpurdie 344
    };
245 dpurdie 345
 
346
    #
347
    #   Find all matching files
255 dpurdie 348
    #   Call helper rouine to populate the data strcutures
245 dpurdie 349
    #
6133 dpurdie 350
    File::Find::find ( { wanted => $wanted, no_chdir => 1 }, $self->{root} );
245 dpurdie 351
 
352
    #
353
    #   Flag that the directories have been scanned
354
    #
355
    $self->{locate_done} = 1;
356
    return scalar  @{$self->{info}};
357
}
358
 
359
#-------------------------------------------------------------------------------
360
# Function        : scan
361
#
362
# Description     : Scan all buildfiles and determine the packages that are
363
#                   created by file(s)
364
#
365
#                   This routine can extract build dependency information, but
366
#                   this is not done by default
367
#
368
# Inputs          : $self
369
#
370
# Returns         : 
371
#
372
sub scan
373
{
374
    my ($self) = @_;
375
 
376
    #
377
    #   Locate the buildfiles, unless this has been done
378
    #
379
    locate ( $self ) unless ( $self->{locate_done} );
380
 
381
    #
382
    #   Scan all build files and determine the target package name
383
    #
384
    #
385
    foreach my $be ( @{$self->{info}} )
386
    {
387
        if ( $be->{type} == 2 ) {
388
            scan_ant ( $be, $self->{scandeps} );
389
        } else {
359 dpurdie 390
            scan_jats( $be, $self->{scandeps} );
245 dpurdie 391
        }
392
 
393
        #
261 dpurdie 394
        #   Skip invalid build files
395
        #
396
        next unless ( $be->{name} &&  $be->{version}  );
397
 
398
        #
245 dpurdie 399
        #   Calculate internal information from the basic information
359 dpurdie 400
        #   To be used as a Display Name (Display to user)
401
        #   full    - Full package version and extension
402
        #   mname   - name and extension
403
        #
404
        #   To be used for data processing (Hash key into data)
405
        #   fullTag - Full package version and extension $; joiner
245 dpurdie 406
        #   package - name and extension with a $; joiner
407
        #
359 dpurdie 408
        #
409
        $be->{fullTag} = join $;, $be->{name}, $be->{version}, $be->{prj};
410
        $be->{package} = join $;, $be->{name}, $be->{prj};
411
 
245 dpurdie 412
        $be->{version} .= '.' . $be->{prj} if ( $be->{prj} );
359 dpurdie 413
 
414
        $be->{full} = $be->{name} . ' ' . $be->{version};
245 dpurdie 415
        $be->{mname} = $be->{name};
416
        $be->{mname} .= '.' . $be->{prj} if ( $be->{prj} );
417
 
253 dpurdie 418
        Verbose2( "Buildfile: $be->{dir}, $be->{file},$be->{name}");
245 dpurdie 419
    }
420
    $self->{scan_done} = 1;
421
}
422
 
423
#-------------------------------------------------------------------------------
424
# Function        : scan_jats
425
#
426
# Description     : Scan a jats build file
427
#
428
# Inputs          : $be         - Reference to a BuildEntry
429
#                   $scanDeps   - Include dependency information
430
#
431
# Returns         : Nothing
432
#
433
sub scan_jats
434
{
435
    my ($be, $scanDeps ) = @_;
436
 
437
    my $infile = "$be->{dir}/$be->{file}";
438
    open ( INFILE, "<$infile" ) || Error( "Cannot open $infile" );
439
    while ( <INFILE> )
440
    {
441
        next if ( m~^\s*#~ );            # Skip comments
442
        #
443
        #   Process BuildName
444
        #
445
        if ( m~\s*BuildName[\s\(]~ )
446
        {
447
            #   Build names come in many flavours, luckily we have a function
448
            #
449
            m~\(\s*(.*?)\s*\)~;
450
            my @args = split /\s*,\s*/, $1;
451
            my $build_info = BuildName::parseBuildName( @args );
452
 
453
            $be->{name} = $build_info->{BUILDNAME_PACKAGE};
454
            $be->{version} = $build_info->{BUILDNAME_VERSION};
455
            $be->{prj} = $build_info->{BUILDNAME_PROJECT};
456
        }
457
 
458
        #
459
        #   (Optional) Process BuildPkgArchive and LinkPkgArchive
460
        #   Retain the Name and the ProjectSuffix and the version
461
        #
462
        if ( $scanDeps && ( m/^LinkPkgArchive/ or m/^BuildPkgArchive/ ))
463
        {
464
            m/['"](.*?)['"][^'"]*['"](.*?)['"]/;
465
 
466
            my ( $package, $rel, $suf, $full ) = SplitPackage( $1, $2 );
359 dpurdie 467
            if ( $scanDeps > 1 ) {
468
                $be->{depends}{$package,$rel,$suf} = join ($;, $1, $2);
469
            } else {
470
                $be->{depends}{$package,$suf} = join ($;, $1, $2);
471
            }
245 dpurdie 472
        }
473
    }
474
    close INFILE;
475
}
476
 
477
 
478
#-------------------------------------------------------------------------------
479
# Function        : scan_ant
480
#
481
# Description     : Scan an ant build file
482
#
483
# Inputs          : $be         - Reference to a BuildEntry
484
#                   $scanDeps   - Include dependency information
485
#
486
# Returns         : Nothing
487
#
488
sub scan_ant
489
{
490
    my ($be, $scanDeps ) = @_;
491
    my $infile = "$be->{dir}/$be->{file}";
492
    my $release_name;
493
    my $release_version;
494
 
495
    open ( INFILE, "<$infile" ) || Error( "Cannot open $infile" );
496
    while ( <INFILE> )
497
    {
498
        #
499
        #   Process "property" statements
500
        #
501
        if ( m~<property~ )
502
        {
503
            my $name;
504
            my $value;
505
 
506
            #
507
            #   Extract the name and version
508
            #
509
            $name = $1 if m~name=\"([^"]*)"~;
510
            $value = $1 if m~value=\"([^"]*)"~;
511
 
512
            if ( $name && $value )
513
            {
514
                if ( $name eq 'packagename' ) {
515
                    $release_name = $value;
516
 
517
                } elsif ( $name eq 'packageversion' ) {
518
                    $release_version = $value;
519
                    my ( $package, $rel, $suf, $full ) = SplitPackage( $release_name, $release_version );
520
                    $be->{name} = $package;
521
                    $be->{version} = $rel;
522
                    $be->{prj} = $suf;
523
 
255 dpurdie 524
                } elsif ( $name eq 'releasemanager.releasename' ) {
525
                    next;
526
 
527
                } elsif ( $name eq 'releasemanager.projectname' ) {
528
                    next;
529
 
245 dpurdie 530
                } elsif ( $scanDeps ) {
531
                    my ( $package, $rel, $suf, $full ) = SplitPackage( $name, $value );
359 dpurdie 532
                    if ( $scanDeps > 1 ) {
4184 dpurdie 533
                        $be->{depends}{$package,$rel,$suf} = join ($;, $name, $value);
359 dpurdie 534
                    } else {
4184 dpurdie 535
                        $be->{depends}{$package,$suf} = join ($;, $name, $value);
359 dpurdie 536
                    }
245 dpurdie 537
                }
538
            }
539
        }
540
    }
541
    close INFILE;
542
}
543
 
544
#-------------------------------------------------------------------------------
545
# Function        : getInfo
546
#
547
# Description     : Returns an array of stuff that can be used to iterate
548
#                   over the collected data.
549
#
550
#                   Will perform a 'locate' if not already done
551
#
552
#                   The elements are BuildEntries
553
#                   These are pretty simple
554
#
555
# Inputs          : $self
556
#
557
# Returns         : 
558
#
559
sub getInfo
560
{
561
    my ($self) = @_;
562
 
563
    #
564
    #   Locate the buildfiles, unless this has been done
565
    #
566
    locate ( $self ) unless ( $self->{locate_done} );
567
 
568
    return @{$self->{info}};
569
}
570
 
571
#-------------------------------------------------------------------------------
572
# Function        : match
573
#
574
# Description     : Determine build files that match a given package
575
#                   A full package name has three fields
576
#                       1) Name
577
#                       2) Version
578
#                       3) Extension (optional)
579
#                   The package can be specified as:
580
#                       Name.Version.Extension
581
#                       Name.Extension
582
#
583
# Inputs          : $self
584
#                   $package                - See above
585
#
586
# Returns         : Number of buildfiles that match
587
#
588
sub match
589
{
590
    my ($self, $package) = @_;
591
    return 0 unless ( $package );
592
    scan ( $self ) unless ( $self->{scan_done} );
593
 
594
    $self->{match} = [];
595
 
596
    foreach my $be ( @{$self->{info}} )
597
    {
267 dpurdie 598
        next unless ( $be->{name} &&  $be->{version}  );
3347 dpurdie 599
        if ( $package eq $be->{mname} || $package eq ($be->{name} . '.' . $be->{version}) )
245 dpurdie 600
        {
601
            push @{$self->{match}}, $be;
602
        }
603
    }
604
 
605
    $self->{match_done} = 1;
606
    return scalar @{$self->{match}}
607
}
608
 
609
#-------------------------------------------------------------------------------
610
# Function        : getMatchList
611
#
612
# Description     : Get the results of a match
613
#                   If no match has been done, then return the complete
614
#                   list - Like getInfo
615
#
616
# Inputs          : $self
617
#
618
# Returns         : Array of directories that matched the last match
619
#
620
sub getMatchList
621
{
622
    my ($self) = @_;
623
    my $set = 'info';
624
    $set = 'match' if ( $self->{match_done} );
625
 
626
    return @{$self->{$set}};
627
}
628
 
629
#-------------------------------------------------------------------------------
630
# Function        : getMatchDir
631
#
632
# Description     : Get the results of a match
633
#                   Can be an array or a scalar. If a scalar is requested
634
#                   then this rouitne will ensure that only one entry
635
#                   has been matched.
636
#
637
# Inputs          : $self
638
#
639
# Returns         : Array of directories that matched the last match
640
#
641
sub getMatchDir
642
{
643
    my ($self) = @_;
644
    my @list;
645
    foreach my $be ( $self->getMatchList() )
646
    {
647
        push @list, $be->{dir};
648
    }
649
 
650
    if ( wantarray )
651
    {
652
        return @list;
653
    }
654
 
655
    Error ("Locate Build file. Internal error",
656
           "Multiple build files have been located. This condition should have",
657
           "been handled by the application") if ( $#list > 0 );
658
 
659
    return $list[0];
660
}
661
 
662
#-------------------------------------------------------------------------------
663
# Function        : formatData
664
#
665
# Description     : Create an array of build files and package names
666
#                   Used to pretty print error messages
667
#
668
# Inputs          : $self
669
#
670
# Returns         : Array of text strings formatted as:
671
#                   path : packagename
672
#
673
#
674
sub formatData
675
{
676
 
677
    my ($self) = @_;
678
    my @text;
679
 
680
    my $max_len = 0;
681
    my %data;
682
 
683
    #
684
    #   Travserse the internal data
685
    #
686
    foreach my $be ( @{$self->{info}} )
687
    {
267 dpurdie 688
        my $package = $be->{mname} || '-Undefined-';
245 dpurdie 689
        my $path = "$be->{dir}/$be->{file}";
690
        my $len = length $path;
691
        $max_len = $len if ( $len > $max_len );
692
        $data{$path} = $package;
693
    }
694
 
695
    foreach my $path ( sort keys %data )
696
    {
697
        push (@text, sprintf ("%${max_len}s : %s", $path, $data{$path} ));
698
    }
699
 
700
    return @text;
701
}
702
 
703
1;