Subversion Repositories DevTools

Rev

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