Subversion Repositories DevTools

Rev

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