Subversion Repositories DevTools

Rev

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