Subversion Repositories DevTools

Rev

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

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