Subversion Repositories DevTools

Rev

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