Subversion Repositories DevTools

Rev

Rev 6198 | Go to most recent revision | 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
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
6276 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
6198 dpurdie 243
    #
6133 dpurdie 244
    my $ff_datap = \@{$self->{info}};
245
    my $ff_file = $self->{file};
246
    my $ff_all = $self->{locateAll};
247
    my $ff_self = $self;
248
    my $ff_ant = ( $ff_file =~ m~(.+)\.xml$~i ) ? $1 : '';
245 dpurdie 249
 
6133 dpurdie 250
    #
251
    #   Anonymous sub for the file::find wanted function
252
    #       Use closure to allow access to local variables
253
    #       Use no_chdir to allow very deep (windows) structures
254
    #
255
    my $wanted = sub 
245 dpurdie 256
    {
6133 dpurdie 257
        # Using no_chdir - extract just the filename
258
        my $file = $_;
259
        $file =~ s~.*/~~;
260
        Verbose3( "locateBuildFile: $file");
261
 
262
        if ( -d $_)
245 dpurdie 263
        {
6133 dpurdie 264
            #
265
            #   Skip known dirs
266
            #   
6177 dpurdie 267
            if ($file eq '.git' || $file eq '.svn' || $file eq 'lost+found')
6133 dpurdie 268
            {
269
                $File::Find::prune = 1;
270
                Verbose3( "locateBuildFile: PRUNE: $file");
271
                return;
272
            }
273
 
274
            #
275
            #   Limit the depth of the scan
6177 dpurdie 276
            #       Suggestion 3 or 4 below the package base
6133 dpurdie 277
            #
278
            if ($self->{LimitDepth})
279
            {
280
                my $depth = $File::Find::name =~ tr~/~/~;
281
                if ($depth >= $self->{LimitDepth})
282
                {
283
                    $File::Find::prune = 1;
284
                    Verbose3( "locateBuildFile: LimitDepth: $_");
285
                    return;
286
                }
287
            }
288
        }
289
 
290
        if ( $file eq $ff_file  )
291
        {
267 dpurdie 292
            if ( $ff_ant )
293
            {
6133 dpurdie 294
                if ( -f ( $File::Find::dir . '/' . ${ff_ant} . 'depends.xml') )
267 dpurdie 295
                {
6133 dpurdie 296
                    Verbose ("locateBuildFile: FOUND $File::Find::dir, $file");
297
                    push @{$ff_datap}, BuildEntry( $File::Find::dir, $file, 2);
267 dpurdie 298
                }
299
            }
300
            else
301
            {
6133 dpurdie 302
                $file = 'auto.pl' if ( $ff_self->{scandeps} && -f 'auto.pl' );
303
                Verbose ("locateBuildFile: FOUND $File::Find::dir, $file");
304
                push @{$ff_datap}, BuildEntry( $File::Find::dir, $file, 1);
267 dpurdie 305
            }
306
            return;    
245 dpurdie 307
        }
308
 
309
        #
310
        #   Detect ANT {packagename}depends.xml file
6133 dpurdie 311
        #       These are file pairs (mostly)
6198 dpurdie 312
        #       Must not be empty
245 dpurdie 313
        #
6198 dpurdie 314
        if ( $ff_all && $file =~ m/(.+)depends.xml$/ && -s $file )
245 dpurdie 315
        {
6198 dpurdie 316
            my $baseFile = $File::Find::dir . '/' . $1 . '.xml';
317
            if ( -f $baseFile && -s $baseFile )
245 dpurdie 318
            {
6133 dpurdie 319
                Verbose ("locateBuildFile: FOUND $File::Find::dir, $file");
320
                push @{$ff_datap}, BuildEntry( $File::Find::dir, $file, 2);
245 dpurdie 321
            }
322
        }
6133 dpurdie 323
    };
245 dpurdie 324
 
325
    #
326
    #   Find all matching files
255 dpurdie 327
    #   Call helper rouine to populate the data strcutures
245 dpurdie 328
    #
6133 dpurdie 329
    File::Find::find ( { wanted => $wanted, no_chdir => 1 }, $self->{root} );
245 dpurdie 330
 
331
    #
332
    #   Flag that the directories have been scanned
333
    #
334
    $self->{locate_done} = 1;
335
    return scalar  @{$self->{info}};
336
}
337
 
338
#-------------------------------------------------------------------------------
339
# Function        : scan
340
#
341
# Description     : Scan all buildfiles and determine the packages that are
342
#                   created by file(s)
343
#
344
#                   This routine can extract build dependency information, but
345
#                   this is not done by default
346
#
347
# Inputs          : $self
348
#
349
# Returns         : 
350
#
351
sub scan
352
{
353
    my ($self) = @_;
354
 
355
    #
356
    #   Locate the buildfiles, unless this has been done
357
    #
358
    locate ( $self ) unless ( $self->{locate_done} );
359
 
360
    #
361
    #   Scan all build files and determine the target package name
362
    #
363
    #
364
    foreach my $be ( @{$self->{info}} )
365
    {
366
        if ( $be->{type} == 2 ) {
367
            scan_ant ( $be, $self->{scandeps} );
368
        } else {
359 dpurdie 369
            scan_jats( $be, $self->{scandeps} );
245 dpurdie 370
        }
371
 
372
        #
261 dpurdie 373
        #   Skip invalid build files
374
        #
375
        next unless ( $be->{name} &&  $be->{version}  );
376
 
377
        #
245 dpurdie 378
        #   Calculate internal information from the basic information
359 dpurdie 379
        #   To be used as a Display Name (Display to user)
380
        #   full    - Full package version and extension
381
        #   mname   - name and extension
382
        #
383
        #   To be used for data processing (Hash key into data)
384
        #   fullTag - Full package version and extension $; joiner
245 dpurdie 385
        #   package - name and extension with a $; joiner
386
        #
359 dpurdie 387
        #
388
        $be->{fullTag} = join $;, $be->{name}, $be->{version}, $be->{prj};
389
        $be->{package} = join $;, $be->{name}, $be->{prj};
390
 
245 dpurdie 391
        $be->{version} .= '.' . $be->{prj} if ( $be->{prj} );
359 dpurdie 392
 
393
        $be->{full} = $be->{name} . ' ' . $be->{version};
245 dpurdie 394
        $be->{mname} = $be->{name};
395
        $be->{mname} .= '.' . $be->{prj} if ( $be->{prj} );
396
 
253 dpurdie 397
        Verbose2( "Buildfile: $be->{dir}, $be->{file},$be->{name}");
245 dpurdie 398
    }
399
    $self->{scan_done} = 1;
400
}
401
 
402
#-------------------------------------------------------------------------------
403
# Function        : scan_jats
404
#
405
# Description     : Scan a jats build file
406
#
407
# Inputs          : $be         - Reference to a BuildEntry
408
#                   $scanDeps   - Include dependency information
409
#
410
# Returns         : Nothing
411
#
412
sub scan_jats
413
{
414
    my ($be, $scanDeps ) = @_;
415
 
416
    my $infile = "$be->{dir}/$be->{file}";
417
    open ( INFILE, "<$infile" ) || Error( "Cannot open $infile" );
418
    while ( <INFILE> )
419
    {
420
        next if ( m~^\s*#~ );            # Skip comments
421
        #
422
        #   Process BuildName
423
        #
424
        if ( m~\s*BuildName[\s\(]~ )
425
        {
426
            #   Build names come in many flavours, luckily we have a function
427
            #
428
            m~\(\s*(.*?)\s*\)~;
429
            my @args = split /\s*,\s*/, $1;
430
            my $build_info = BuildName::parseBuildName( @args );
431
 
432
            $be->{name} = $build_info->{BUILDNAME_PACKAGE};
433
            $be->{version} = $build_info->{BUILDNAME_VERSION};
434
            $be->{prj} = $build_info->{BUILDNAME_PROJECT};
435
        }
436
 
437
        #
438
        #   (Optional) Process BuildPkgArchive and LinkPkgArchive
439
        #   Retain the Name and the ProjectSuffix and the version
440
        #
441
        if ( $scanDeps && ( m/^LinkPkgArchive/ or m/^BuildPkgArchive/ ))
442
        {
443
            m/['"](.*?)['"][^'"]*['"](.*?)['"]/;
444
 
445
            my ( $package, $rel, $suf, $full ) = SplitPackage( $1, $2 );
359 dpurdie 446
            if ( $scanDeps > 1 ) {
447
                $be->{depends}{$package,$rel,$suf} = join ($;, $1, $2);
448
            } else {
449
                $be->{depends}{$package,$suf} = join ($;, $1, $2);
450
            }
245 dpurdie 451
        }
452
    }
453
    close INFILE;
454
}
455
 
456
 
457
#-------------------------------------------------------------------------------
458
# Function        : scan_ant
459
#
460
# Description     : Scan an ant build file
461
#
462
# Inputs          : $be         - Reference to a BuildEntry
463
#                   $scanDeps   - Include dependency information
464
#
465
# Returns         : Nothing
466
#
467
sub scan_ant
468
{
469
    my ($be, $scanDeps ) = @_;
470
    my $infile = "$be->{dir}/$be->{file}";
471
    my $release_name;
472
    my $release_version;
473
 
474
    open ( INFILE, "<$infile" ) || Error( "Cannot open $infile" );
475
    while ( <INFILE> )
476
    {
477
        #
478
        #   Process "property" statements
479
        #
480
        if ( m~<property~ )
481
        {
482
            my $name;
483
            my $value;
484
 
485
            #
486
            #   Extract the name and version
487
            #
488
            $name = $1 if m~name=\"([^"]*)"~;
489
            $value = $1 if m~value=\"([^"]*)"~;
490
 
491
            if ( $name && $value )
492
            {
493
                if ( $name eq 'packagename' ) {
494
                    $release_name = $value;
495
 
496
                } elsif ( $name eq 'packageversion' ) {
497
                    $release_version = $value;
498
                    my ( $package, $rel, $suf, $full ) = SplitPackage( $release_name, $release_version );
499
                    $be->{name} = $package;
500
                    $be->{version} = $rel;
501
                    $be->{prj} = $suf;
502
 
255 dpurdie 503
                } elsif ( $name eq 'releasemanager.releasename' ) {
504
                    next;
505
 
506
                } elsif ( $name eq 'releasemanager.projectname' ) {
507
                    next;
508
 
245 dpurdie 509
                } elsif ( $scanDeps ) {
510
                    my ( $package, $rel, $suf, $full ) = SplitPackage( $name, $value );
359 dpurdie 511
                    if ( $scanDeps > 1 ) {
4184 dpurdie 512
                        $be->{depends}{$package,$rel,$suf} = join ($;, $name, $value);
359 dpurdie 513
                    } else {
4184 dpurdie 514
                        $be->{depends}{$package,$suf} = join ($;, $name, $value);
359 dpurdie 515
                    }
245 dpurdie 516
                }
517
            }
518
        }
519
    }
520
    close INFILE;
521
}
522
 
523
#-------------------------------------------------------------------------------
524
# Function        : getInfo
525
#
526
# Description     : Returns an array of stuff that can be used to iterate
527
#                   over the collected data.
528
#
529
#                   Will perform a 'locate' if not already done
530
#
531
#                   The elements are BuildEntries
532
#                   These are pretty simple
533
#
534
# Inputs          : $self
535
#
536
# Returns         : 
537
#
538
sub getInfo
539
{
540
    my ($self) = @_;
541
 
542
    #
543
    #   Locate the buildfiles, unless this has been done
544
    #
545
    locate ( $self ) unless ( $self->{locate_done} );
546
 
547
    return @{$self->{info}};
548
}
549
 
550
#-------------------------------------------------------------------------------
551
# Function        : match
552
#
553
# Description     : Determine build files that match a given package
554
#                   A full package name has three fields
555
#                       1) Name
556
#                       2) Version
557
#                       3) Extension (optional)
558
#                   The package can be specified as:
559
#                       Name.Version.Extension
560
#                       Name.Extension
561
#
562
# Inputs          : $self
563
#                   $package                - See above
564
#
565
# Returns         : Number of buildfiles that match
566
#
567
sub match
568
{
569
    my ($self, $package) = @_;
570
    return 0 unless ( $package );
571
    scan ( $self ) unless ( $self->{scan_done} );
572
 
573
    $self->{match} = [];
574
 
575
    foreach my $be ( @{$self->{info}} )
576
    {
267 dpurdie 577
        next unless ( $be->{name} &&  $be->{version}  );
3347 dpurdie 578
        if ( $package eq $be->{mname} || $package eq ($be->{name} . '.' . $be->{version}) )
245 dpurdie 579
        {
580
            push @{$self->{match}}, $be;
581
        }
582
    }
583
 
584
    $self->{match_done} = 1;
585
    return scalar @{$self->{match}}
586
}
587
 
588
#-------------------------------------------------------------------------------
589
# Function        : getMatchList
590
#
591
# Description     : Get the results of a match
592
#                   If no match has been done, then return the complete
593
#                   list - Like getInfo
594
#
595
# Inputs          : $self
596
#
597
# Returns         : Array of directories that matched the last match
598
#
599
sub getMatchList
600
{
601
    my ($self) = @_;
602
    my $set = 'info';
603
    $set = 'match' if ( $self->{match_done} );
604
 
605
    return @{$self->{$set}};
606
}
607
 
608
#-------------------------------------------------------------------------------
609
# Function        : getMatchDir
610
#
611
# Description     : Get the results of a match
612
#                   Can be an array or a scalar. If a scalar is requested
613
#                   then this rouitne will ensure that only one entry
614
#                   has been matched.
615
#
616
# Inputs          : $self
617
#
618
# Returns         : Array of directories that matched the last match
619
#
620
sub getMatchDir
621
{
622
    my ($self) = @_;
623
    my @list;
624
    foreach my $be ( $self->getMatchList() )
625
    {
626
        push @list, $be->{dir};
627
    }
628
 
629
    if ( wantarray )
630
    {
631
        return @list;
632
    }
633
 
634
    Error ("Locate Build file. Internal error",
635
           "Multiple build files have been located. This condition should have",
636
           "been handled by the application") if ( $#list > 0 );
637
 
638
    return $list[0];
639
}
640
 
641
#-------------------------------------------------------------------------------
642
# Function        : formatData
643
#
644
# Description     : Create an array of build files and package names
645
#                   Used to pretty print error messages
646
#
647
# Inputs          : $self
648
#
649
# Returns         : Array of text strings formatted as:
650
#                   path : packagename
651
#
652
#
653
sub formatData
654
{
655
 
656
    my ($self) = @_;
657
    my @text;
658
 
659
    my $max_len = 0;
660
    my %data;
661
 
662
    #
663
    #   Travserse the internal data
664
    #
665
    foreach my $be ( @{$self->{info}} )
666
    {
267 dpurdie 667
        my $package = $be->{mname} || '-Undefined-';
245 dpurdie 668
        my $path = "$be->{dir}/$be->{file}";
669
        my $len = length $path;
670
        $max_len = $len if ( $len > $max_len );
671
        $data{$path} = $package;
672
    }
673
 
674
    foreach my $path ( sort keys %data )
675
    {
676
        push (@text, sprintf ("%${max_len}s : %s", $path, $data{$path} ));
677
    }
678
 
679
    return @text;
680
}
681
 
682
1;