Subversion Repositories DevTools

Rev

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