Subversion Repositories DevTools

Rev

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

Rev Author Line No. Line
392 dpurdie 1
########################################################################
2
# Copyright ( C ) 2004 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   : Get package information
10
#                 for a package name and version as specified on the
11
#                 command line.
12
#
13
#                 Determine the package id
14
#                 Locate all packages that have the same package name
15
#                 Create a hash of previous versions
16
#                 Create a JPG showing the version history
17
#
18
#......................................................................#
19
 
20
require 5.006_001;
21
use strict;
22
use warnings;
23
use JatsError;
24
use JatsRmApi;
25
 
26
 
27
#use Data::Dumper;
28
use Cwd;
29
use DBI;
30
use Getopt::Long;
31
use Pod::Usage;                             # required for help support
32
my $RM_DB;
33
 
34
my $opt_package;
35
 
36
################################################################################
37
#   Global data
38
#
39
my $VERSION = "1.0.0";
40
my %ReleasePackages;            # Packages in the release
41
my %BuildPackages;              # Packages for this build
42
my $last_pv_id;
43
my $pkg_id;
44
my %versions;
45
my %suffixes;
46
my @startPoints;
47
my @endPoints;
48
 
49
 
50
#
51
#   Options
52
#
53
my $opt_help = 0;
54
my $opt_manual = 0;
55
my $opt_verbose = 0;
56
my $opt_flat;
57
 
58
my $result = GetOptions (
59
                "help+"     => \$opt_help,          # flag, multiple use allowed
60
                "manual"    => \$opt_manual,        # flag
61
                "verbose+"  => \$opt_verbose,       # flag
62
                "flat!"     => \$opt_flat,          # Flat structure
63
                );
64
 
65
#
66
#   Process help and manual options
67
#
68
pod2usage(-verbose => 0, -message => "Version: $VERSION")  if ($opt_help == 1  || ! $result);
69
pod2usage(-verbose => 1)  if ($opt_help == 2 );
70
pod2usage(-verbose => 2)  if ($opt_manual || ($opt_help > 2));
71
 
72
#
73
#   Configure the error reporting process now that we have the user options
74
#
75
ErrorConfig( 'name'    =>'PLAY9c',
76
             'verbose' => $opt_verbose );
77
 
78
unless ( $ARGV[0] )
79
{
80
    Error( "Specify a package as 'name'" );
81
}
82
$opt_package = $ARGV[0];
83
Verbose( "Base Package: $opt_package");
84
 
85
#
86
#   Body of the process
87
#
88
GetPkgIdByName ( $opt_package );
89
GetData_by_pkg_id ( $pkg_id );
90
MassageData();
91
 
92
DebugDumpData ("Versions", \%versions );
93
DebugDumpData ("Starts", \@startPoints );
94
DebugDumpData ("Ends", \@endPoints );
95
#DebugDumpData ("Suffixes", \%suffixes );
96
 
97
#
98
#   Ordered by PVID. Which will be creation date
99
#   as they are created sequentially.
100
#
101
#foreach my $entry (sort {$versions{$a}{version} cmp $versions{$b}{version}} keys(%versions) )
102
#{
103
#    print GetVname($entry)," $versions{$entry}{version}\n";
104
#}
105
 
106
 
107
my $filebase = "$ARGV[0]_versions";
108
open (FH, ">$filebase.dot" ) or die "Cannot open output";
109
print FH "digraph world {\n";
110
#print FH "\trankdir=LR;\n";
111
print FH "\tnode[fontsize=24];\n";
112
 
113
sub GetVname
114
{
115
    my ($entry) = @_;
116
    my $me = $versions{$entry}{vname};
117
    unless ( $me )
118
    {
119
        $me = 'Unknown-' . $entry;
120
    }
121
    return $me;
122
}
123
 
124
if ( $opt_flat )
125
{
126
    my $last = 0;
127
    foreach my $entry (sort {$versions{$a}{version} cmp $versions{$b}{version}} keys(%versions) )
128
    {
129
print "-- $entry, $versions{$entry}{version}, $versions{$entry}{vname}\n";
130
        if ( $last )
131
        {
132
            my $me = GetVname($last);
133
            print FH "\t", pentry($me)  ," -> { ", plist ( ' ; ', GetVname( $entry) ), " }\n";
134
            print FH "\t", pentry($me)  ,"[label=\"$me\\n$last\"];\n";
135
        }
136
        $last = $entry;
137
    }
138
}
139
else
140
{
141
    foreach my $entry ( sort keys(%versions) )
142
    {
143
        my @versions;
144
        my $me = GetVname($entry);
145
        my $distanceCount = $versions{$entry}{distance};
146
        foreach ( @{ $versions{$entry}{next}} )
147
        {
148
            push @versions, GetVname( $_);
149
        }
150
 
151
        print FH "\t", pentry($me)  ," -> { ", plist ( ' ; ', @versions ), " }\n";
152
        print FH "\t", pentry($me)  ,"[label=\"$versions{$entry}{vname}\"];\n";
153
 #       print FH "\t", pentry($me)  ,"[label=\"$me\\n$distanceCount\\n$entry\"];\n";
154
        print FH "\t", pentry($me)  ,"[shape=rectangle];\n" if ($versions{$entry}{main});
155
    #    print FH "\t", pentry($me)  ,"[shape=circle];\n" if ($versions{$entry}{main});
156
        print FH "\t", pentry($me)  ,"[shape=octagon];\n" if ($versions{$entry}{branchPoint});
157
        print FH "\t", pentry($me)  ,"[shape=invhouse];\n" if ($versions{$entry}{newSuffix});
158
 
159
    }
160
}
161
 
162
 
163
print FH "\n};\n";
164
close FH;
165
 
166
#
167
#   Convert DOT to a SVG
168
#
169
print "Generating graphical images\n";
170
system( "dot $filebase.dot -Tjpg -o$filebase.jpg" );  # -v
171
system( "dot $filebase.dot -Tsvg -o$filebase.svg" );  # -v
172
 
173
#
174
#   Display a list of terminal packages
175
#   These are packages that are not used by any other package
176
#
177
print "\n";
178
print "Generated: $filebase.dot\n";
179
print "Generated: $filebase.jpg\n";
180
print "Generated: $filebase.svg\n";
181
 
182
 
183
exit 0;
184
 
185
 
186
#-------------------------------------------------------------------------------
187
# Function        : MassageData
188
#
189
# Description     : 
190
#
191
# Inputs          : 
192
#
193
# Returns         : 
194
#
195
my %seenSuffixes;
196
sub MassageData
197
{
198
    #
199
    #   Process the 'versions' hash and:
200
    #   Add back references
201
    #   Find starts and ends
202
    #       Entry with no previous
203
    #       Entry with no next
204
    #
205
    foreach my $entry ( keys(%versions) )
206
    {
207
        foreach ( @{ $versions{$entry}{next}} )
208
        {
209
            $versions{$_}{last} = $entry;
210
        }
211
    }
212
    foreach my $entry ( keys(%versions) )
213
    {
214
        push @startPoints, $entry
215
            unless ( exists $versions{$entry}{last} );
216
 
217
        push @endPoints, $entry
218
            unless ( @{$versions{$entry}{next}} > 0  )
219
    }
220
 
221
    #
222
    #   Walk each starting point list and determine new Projects
223
    #
224
    foreach my $entry ( @startPoints )
225
    {
226
        processBranchLists($entry);
227
 
228
        sub processBranchLists
229
        {
230
            foreach my $entry ( @_ )
231
            {
232
                my $s = $versions{$entry}{suffix};
233
                unless ( exists $seenSuffixes{$s} )
234
                {
235
                    $seenSuffixes{$s} = 1;
236
                    $versions{$entry}{branchPoint} = 1;
237
                    $versions{$entry}{newSuffix} = 1;
238
                }
239
                processBranchLists (@{$versions{$entry}{next}});
240
            }
241
        }
242
    }
243
 
244
    #
245
    #   For each leaf ( end point ), walk backwards and mark each node with the
246
    #   distance from the end. If we get to a node which already has been marked then
247
    #   stop if our length is less. We want the value to be the longest distance to
248
    #   a leaf
249
    #
250
    my $distanceCount;
251
    foreach my $entryPoint ( @endPoints )
252
    {
253
        $distanceCount = 0;
254
        my $entry = $entryPoint;
255
        while ( $entry )
256
        {
257
            if ( defined $versions{$entry}{distance} )
258
            {
259
                if ( $versions{$entry}{distance} > $distanceCount )
260
                {
261
                    last;
262
                }
263
            }
264
            $versions{$entry}{distance} = $distanceCount++;
265
            $entry = $versions{$entry}{last};
266
        }
267
    }
268
 
269
    #
270
    #   Locate all instances where a package-version branches
271
    #   Determine the version that should be on the non-branching path
272
    #
273
    #   Reorder the 'next' list so that the first item is the non-branching
274
    #   path. This will be used in the data-insertion phase to simplify the
275
    #   processing.
276
    #
277
    foreach my $entry ( sort keys(%versions) )
278
    {
279
        my @next = @{$versions{$entry}{next}};
280
        my $count = @next;
281
        my @ordered;
282
        my $main;
283
 
284
        if ( $count > 0 )
285
        {
286
            my %nexts = map { $_ => 1 } @next;
287
            foreach my $e ( @next )
288
            {
289
                #
290
                #   Remove those that already have a branch
291
                #
292
                if ( $versions{$e}{branchPoint} || $versions{$e}{newSuffix}  )
293
                {
294
                    push @ordered, $e;
295
                    delete $nexts{$e};
296
                }
297
            }
298
 
299
            #
300
            #   Select longest arm as the non-branching path
301
            #
302
            my $count = -1;
303
            my $countEntry;
304
            foreach my $e ( sort keys %nexts )
305
            {
306
                if ( $versions{$e}{distance} > $count )
307
                {
308
                    $count = $versions{$e}{distance};
309
                    $countEntry = $e;
310
                }
311
            }
312
            if ($countEntry)
313
            {
314
                $main = $countEntry;
315
                delete $nexts{$countEntry};
316
            }
317
 
318
            #
319
            #   Mark remaining as non-main
320
            #
321
            foreach my $e ( keys %nexts )
322
            {
323
                push @ordered, $e;
324
                $versions{$e}{branchPoint} = 1;
325
            }
326
 
327
            #
328
            #   Re-order 'next' so that the main path is first
329
            #
330
            @ordered = sort @ordered;
331
            unshift @ordered, $main if ( $main );
332
            @{$versions{$entry}{next}} = @ordered;
333
        }
334
    }
335
}
336
 
337
#-------------------------------------------------------------------------------
338
# Function        : GetPkgIdByName
339
#
340
# Description     :
341
#
342
# Inputs          : pkg_name
343
#
344
# Returns         :
345
#
346
sub GetPkgIdByName
347
{
348
    my ( $pkg_name ) = @_;
349
    my (@row);
350
    my $pv_id;
351
 
352
    #
353
    #   Establish a connection to Release Manager
354
    #
355
    connectRM(\$RM_DB) unless ( $RM_DB );
356
 
357
    #
358
    #   Extract data from Release Manager
359
    #
360
    my $m_sqlstr = "SELECT pkg.PKG_NAME, pkg.PKG_ID" .
361
                   " FROM RELEASE_MANAGER.PACKAGES pkg" .
362
                   " WHERE pkg.PKG_NAME = \'$pkg_name\'";
363
 
364
    my $sth = $RM_DB->prepare($m_sqlstr);
365
    if ( defined($sth) )
366
    {
367
        if ( $sth->execute( ) )
368
        {
369
            if ( $sth->rows )
370
            {
371
                while ( @row = $sth->fetchrow_array )
372
                {
373
                    Verbose( "DATA: " . join(',', @row) );
374
                    $pkg_id = $row[1] || 0;
375
                    last;
376
                }
377
            }
378
            else
379
            {
380
                Warning("GetPkgIdByName:No Data for: $pkg_name");
381
            }
382
            $sth->finish();
383
        }
384
    }
385
    else
386
    {
387
        Error("GetPkgIdByName:Prepare failure" );
388
    }
389
}
390
 
391
#-------------------------------------------------------------------------------
392
# Function        : GetData_by_pkg_id
393
#
394
# Description     :
395
#
396
# Inputs          : pv_id
397
#
398
# Returns         :
399
#
400
sub GetData_by_pkg_id
401
{
402
    my ( $pkg_id ) = @_;
403
    my (@row);
404
 
405
    #
406
    #   Establish a connection to Release Manager
407
    #
408
    connectRM(\$RM_DB) unless ( $RM_DB );
409
 
410
    #
411
    #   Extract data from Release Manager
412
    #
413
    my $m_sqlstr = "SELECT pkg.PKG_NAME, pv.PKG_VERSION, pkg.PKG_ID, pv.PV_ID, pv.LAST_PV_ID, pv.CREATED_STAMP, release_manager.PK_RMAPI.return_vcs_tag(pv.PV_ID)".
414
                   " FROM RELEASE_MANAGER.PACKAGES pkg, RELEASE_MANAGER.PACKAGE_VERSIONS pv" .
415
                   " WHERE pv.PKG_ID = \'$pkg_id\' AND pkg.PKG_ID = pv.PKG_ID";
416
 
417
 
418
    my $sth = $RM_DB->prepare($m_sqlstr);
419
    if ( defined($sth) )
420
    {
421
        if ( $sth->execute( ) )
422
        {
423
            if ( $sth->rows )
424
            {
425
                while ( @row = $sth->fetchrow_array )
426
                {
427
                    Verbose( "DATA: " . join(',', @row) );
428
                    my $pkg_name = $row[0] || 'Unknown';
429
                    my $pkg_ver = $row[1] || 'Unknown';
430
                    my $pv_id = $row[3] || 'Unknown';
431
                    my $last_pv_id = $row[4] || 'Unknown';
432
                    my $created =  $row[5] || 'Unknown';
433
                    my $vcstag =  $row[6] || 'Unknown';
434
                    print "$pkg_name, $pkg_ver, $pv_id, $last_pv_id, $created\n";
435
 
436
                    #
437
                    #   Add data to the hash
438
                    #       Remove entries that address themselves
439
                    #
440
                    push (@{$versions{$last_pv_id}{next}}, $pv_id) unless ($pv_id == $last_pv_id) ;
441
                    $versions{$pv_id}{vname} = $pkg_ver;
442
                    $versions{$pv_id}{vcsTag} = $vcstag;
443
 
444
                    #
445
                    #   Convert version into full form for comparisions
446
                    #
447
                    my $version = $pkg_ver;
448
                    my $suffix;
449
                    if ( $version =~ m~(\d+)\.(\d+)\.(\d+)(\.(.*))?~ )
450
                    {
451
                        my $patch = $3;
452
                        my $build = '000';
453
                        if ( length( $patch) >= 4 )
454
                        {
455
                            $build = substr( $patch, -3 ,3);
456
                            $patch = substr( $patch,  0 ,length($patch)-3);
457
                        }
458
 
459
                        $version = sprintf("%3.3d.%3.3d.%3.3d.%3.3d%s", $1,$2,$patch,$build,$4 || '.0000');
460
                        $suffix = $4 || '';
461
                    }
462
                    elsif ( $version =~ m~(.*)\.cots$~ ) {
463
                        my $cots_base = $1;
464
                        $suffix = '.cots';
465
                        unless ( $version =~ m~(.*)(\.[0-9]4)\.cots~ )
466
                        {
467
                            $version = $cots_base . '.0000.cots';
468
                        }
469
                    }
470
                    else
471
                    {
472
                        $pkg_ver =~ m~(\.\w+)$~;
473
                        $suffix = $1 || '';
474
                    }
475
                    $versions{$pv_id}{version} = $version;
476
 
477
                    #
478
                    #   Process suffix
479
                    #
480
                    $suffix = 'Unknown' unless ( $suffix );
481
                    $suffix = lc ($suffix);
482
                    $versions{$pv_id}{suffix} = $suffix;
483
                    push @{$suffixes{$suffix}}, $pv_id;
484
 
485
#                    if ( $suffix eq '.syd' )
486
#                    {
487
#                        delete $versions{$pv_id};
488
#                    }
489
 
490
#                    last;
491
                }
492
            }
493
            $sth->finish();
494
        }
495
    }
496
    else
497
    {
498
        Error("GetData:Prepare failure" );
499
    }
500
}
501
 
502
#-------------------------------------------------------------------------------
503
# Function        : plist
504
#
505
# Description     : Generate an entry list as text
506
#                   Replace "." with "_" since DOT doesn't like .'s
507
#                   Seperate the arguments
508
#
509
# Inputs          : $pref       - Prefix string
510
#                   @_          - An array of entries to process
511
#
512
# Returns         : A string
513
#
514
sub plist
515
{
516
    my $pref = shift;
517
    my $result = "";
518
    foreach  ( @_ )
519
    {
520
        $_ =~ s~\.~_~g;
521
        $result .= '"' . $_ . '"' . $pref;
522
    }
523
    return $result;
524
}
525
 
526
sub pentry
527
{
528
 
529
    my $result = "";
530
    foreach  ( @_ )
531
    {
532
        next unless ( $_ );
533
        $_ =~ s~\.~_~g;
534
        $result .= '"' . $_ . '"'
535
    }
536
    return $result;
537
}
538
 
539