Subversion Repositories DevTools

Rev

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

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