Subversion Repositories DevTools

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
########################################################################
263 dpurdie 2
# Copyright (C) 2008 ERG Limited, All rights reserved
227 dpurdie 3
#
263 dpurdie 4
# Module name   : jats_sandbox.pl
5
# Module type   : JATS Utility
6
# Compiler(s)   : Perl
7
# Environment(s): JATS
227 dpurdie 8
#
9
# Description   : A script to build a collection of packages in the
10
#                 same sandbox. This script will:
11
#
12
#                   Determine the packages in the sandbox
13
#                   Determine the build order of the packages
14
#                   Build the packages in the correct order
15
#                   Make the packages in the correct order
16
#
17
#                 The script will allow for:
18
#                   The creation of a sandbox
19
#                   The addition of packages to the sandbox
20
#                   Removal of packages from the sandbox
21
#
22
#
23
#                 Command syntax (basic)
24
#                   jats sandbox <command> (options | actions)@
25
#
26
#                 Commands include:
27
#                   create              - Create a sandbox
28
#                   delete              - Delete a sandbox
29
#
30
#                   add package_name    - Add a package to the sandbox
31
#                   rm  package_name    - Remove a package from the sandbox
32
#
33
#                   build               - Build all packages in the sandbox
34
#                   make                - make all packages in the sandbox
35
#
36
#......................................................................#
37
 
263 dpurdie 38
require 5.008_002;
227 dpurdie 39
use strict;
40
use warnings;
41
use JatsError;
42
use JatsSystem;
43
use FileUtils;
245 dpurdie 44
use JatsBuildFiles;
227 dpurdie 45
use JatsVersionUtils;
299 dpurdie 46
use File::Path qw(rmtree);
227 dpurdie 47
 
48
 
49
use Pod::Usage;                             # required for help support
50
use Getopt::Long qw(:config require_order); # Stop on non-option
51
my $VERSION = "1.0.0";                      # Update this
52
 
53
#
54
#   Options
55
#
56
my $opt_debug   = $ENV{'GBE_DEBUG'};        # Allow global debug
57
my $opt_verbose = $ENV{'GBE_VERBOSE'};      # Allow global verbose
58
my $opt_help = 0;
59
 
60
#
61
#   Globals - Provided by the JATS environment
62
#
63
my $USER         = $ENV{'USER'};
64
my $UNIX         = $ENV{'GBE_UNIX'};
65
my $HOME         = $ENV{'HOME'};
66
my $GBE_SANDBOX  = $ENV{'GBE_SANDBOX'};
67
my $GBE_DPKG_SBOX= $ENV{'GBE_DPKG_SBOX'};
68
 
69
#
70
#   Globals
71
#
72
my @build_order = ();                     # Build Ordered list of entries
255 dpurdie 73
my %extern_deps;                          # Hash of external dependencies
227 dpurdie 74
my %packages;                             # Hash of packages
75
 
76
 
77
#-------------------------------------------------------------------------------
78
# Function        : Mainline Entry Point
79
#
80
# Description     :
81
#
82
# Inputs          :
83
#
84
my $result = GetOptions (
299 dpurdie 85
                "help|h:+"      => \$opt_help,
86
                "manual:3"      => \$opt_help,
227 dpurdie 87
                "verbose+"      => \$opt_verbose,           # flag, multiple use allowed
88
                );
89
 
90
                #
91
                #   UPDATE THE DOCUMENTATION AT THE END OF THIS FILE !!!
92
                #
93
 
94
#
95
#   Process help and manual options
96
#
97
pod2usage(-verbose => 0, -message => "Version: $VERSION")  if ($opt_help == 1  || ! $result);
98
pod2usage(-verbose => 1)  if ($opt_help == 2 );
299 dpurdie 99
pod2usage(-verbose => 2)  if ($opt_help > 2 );
227 dpurdie 100
 
101
#
102
#   Configure the error reporting process now that we have the user options
103
#
104
ErrorConfig( 'name'    => 'SANDBOX',
105
             'verbose' => $opt_verbose );
106
 
107
#
108
#   Validate user options
109
#
110
 
111
#
112
#   Parse the user command and decide what to do
113
#
114
#
115
my $cmd = shift @ARGV || "";
116
help(1)                                 if ( $cmd =~ m/^help$/ || $cmd eq "" );
299 dpurdie 117
delete_sandbox()                        if ( $cmd =~ m/^delete$/ );
227 dpurdie 118
create_sandbox()                        if ( $cmd =~ m/^create$/ );
119
info(@ARGV)                             if ( $cmd =~ m/^info$/ );
120
cmd(@ARGV)                              if ( $cmd =~ m/^cmd$/ );
275 dpurdie 121
cmd($cmd, @ARGV )                       if ( $cmd =~ m/(^all$)|(^build$)|(^make$)/  );
273 dpurdie 122
clean($cmd, @ARGV)                      if ( $cmd =~ m/(^clobber$)|(^clean$)/  );
227 dpurdie 123
 
124
Error ("Unknown sandbox command: $cmd");
125
exit 1;
126
 
127
 
128
#-------------------------------------------------------------------------------
129
#
130
#   Give the user a clue
131
#
132
sub help
133
{
134
    my ($level) = @_;
135
    $level = $opt_help unless ( $level );
136
 
137
    pod2usage(-verbose => 0, -message => "Version: ". $VERSION)  if ($level == 1 );
138
    pod2usage(-verbose => $level -1 );
139
}
140
 
141
#-------------------------------------------------------------------------------
142
# Function        : create_sandbox
143
#
144
# Description     : create a sandbox in the current current directory
145
#
146
# Inputs          : None
147
#
148
#
149
sub create_sandbox
150
{
151
    Error ("Cannot create a sandbox within a sandbox",
152
           "Sandbox base is: $GBE_SANDBOX" ) if ( $GBE_SANDBOX );
153
    mkdir ('sandbox_dpkg_archive') || Error ("Cannot create the directory: sandbox_dpkg_archive") ;
154
    exit  0;
155
}
156
 
157
#-------------------------------------------------------------------------------
299 dpurdie 158
# Function        : delete_sandbox
159
#
160
# Description     : Delete a sandbox
161
#                   Its up to the user the delete the components in the sandbox
162
#
163
# Inputs          : None
164
#
165
# Returns         : 
166
#
167
sub delete_sandbox
168
{
169
    unless ( $GBE_SANDBOX )
170
    {
171
        Warning("No sandbox found to delete");
172
    }
173
    else
174
    {
175
        my $sdir = "$GBE_SANDBOX/sandbox_dpkg_archive";
176
        rmtree($sdir,0,0);
177
        Error ("Sandbox directory not completly removed")
178
            if ( -e $sdir );
179
 
180
        Message("Sandbox information deleted",
181
                "Sandbox components must be manually deleted");
182
    }
183
    exit 0;
184
}
185
 
186
#-------------------------------------------------------------------------------
227 dpurdie 187
# Function        : info
188
#
189
# Description     : Display Sandbox information
190
#
191
# Inputs          : Command line args
192
#                   -v  - Be more verbose
193
#
194
# Returns         : Will exit
195
#
196
sub info
197
{
198
    #
199
    #   Allow user to specify verboseness as an argument
200
    #
201
    foreach  ( @_ )
202
    {
203
        $opt_verbose++ if ( m/^-v/ )
204
    }
205
 
206
    #
207
    #   Determine Sandbox information
208
    #   Populate global variables
209
    #
210
    calc_sandbox_info();
211
 
212
    #
213
    #   Display information
214
    #
215
    Message ("Base: $GBE_SANDBOX");
216
    Message ("Archive: $GBE_DPKG_SBOX");
217
 
218
    Message ("Build Order");
219
    foreach my $fe ( @build_order )
220
    {
245 dpurdie 221
        Message( "    Level:" . $fe->{level} . " Name: " . $fe->{mname} );
227 dpurdie 222
        Message( DisplayPath ("        Path: $fe->{dir}" )) if $opt_verbose;
223
 
224
        if ( $opt_verbose )
225
        {
226
            foreach my $idep ( sort keys %{$fe->{ideps}} )
227
            {
228
                my ($ppn,$pps) = split( $; , $idep);
229
                Message ("        I:$ppn.$pps");
230
            }
231
 
232
            foreach my $edep ( sort keys %{$fe->{edeps}} )
233
            {
234
                my ($ppn,$pps) = split( $; , $edep);
235
                my $pv = $fe->{edeps}{$edep};
236
                Message ("        E:$ppn $pv.$pps");
237
            }
238
 
239
        }
240
    }
241
 
255 dpurdie 242
    Message("External Dependencies");
227 dpurdie 243
    foreach my $de ( sort keys %extern_deps )
244
    {
245
        my ($pn,$ps) = split( $; , $de);
246
        my @vlist = keys %{$extern_deps{$de}};
247
        my $flag = $#vlist ? '*' : ' ';
248
        foreach my $pv ( @vlist )
249
        {
250
            Message ("   $flag$pn $pv.$ps");
251
            if ( $opt_verbose )
252
            {
253
                foreach my $pkg ( @{$extern_deps{$de}{$pv}} )
254
                {
255
                    my ($ppn,$pps) = split( $; , $pkg);
256
                    Message ("        U:$ppn.$pps");
257
 
258
                }
259
            }
260
        }
261
    }
262
 
263
    if ( $opt_verbose > 2 )
264
    {
265
        DebugDumpData( "extern_deps", \%extern_deps);
266
        DebugDumpData( "build_order", \@build_order);
267
        DebugDumpData( "packages", \%packages);
268
    }
269
    exit (0);
270
}
271
 
272
#-------------------------------------------------------------------------------
273
# Function        : calc_sandbox_info
274
#
275
# Description     : Examine the sandbox and determine all the important
276
#                   information
277
#
278
# Inputs          : None
279
#
280
# Returns         : Will exit if not in a sandbox
281
#                   Populates global variables
282
#                       @build_order - build ordered array of build entries
283
#
284
sub calc_sandbox_info
285
{
286
    #
287
    #   Start from the root of the sandbox
288
    #
289
    Error ("Command must be executed from within a Sandbox") unless ( $GBE_SANDBOX );
290
    chdir ($GBE_SANDBOX) || Error ("Cannot chdir to $GBE_SANDBOX");
291
 
292
    #
293
    #   Locate all packages within the sandbox
294
    #   These will be top-level directories - one per package
295
    #
296
    my @dirlist;
297
    my @build_list;
255 dpurdie 298
    foreach my $pname ( glob("*") )
227 dpurdie 299
    {
255 dpurdie 300
        next if ( $pname =~ m~^\.~ );
301
        next if ( $pname =~ m~dpkg_archive$~ );
297 dpurdie 302
        next if ( $pname =~ m~^CVS$~ );
255 dpurdie 303
        next unless ( -d $pname );
304
        Verbose ("Package discovered: $pname");
275 dpurdie 305
 
306
        if ( -f "$pname/stop" )
307
        {
308
            Warning("Package contains stop file: $pname");
309
            next;
310
        }
311
 
255 dpurdie 312
        push @dirlist, $pname;
227 dpurdie 313
 
314
        #
315
        #   Locate the build files in each package
245 dpurdie 316
        #   Scan the build files and extract dependancy information
227 dpurdie 317
        #
255 dpurdie 318
        my $bscanner = BuildFileScanner( $pname, 'build.pl', '--LocateAll', '--ScanDependencies' );
245 dpurdie 319
        $bscanner->scan();
320
        my @blist = $bscanner->getInfo();
255 dpurdie 321
        Warning ("Package does not have build files: $pname") unless ( @blist );
322
        Warning ("Package has multiple build files: $pname") if ( $#blist > 0 );
245 dpurdie 323
        push @build_list, @blist;
227 dpurdie 324
    }
325
 
326
    #
327
    #   Process each build file and extract
328
    #       Name of the Package
329
    #       Dependency list
330
    #   Build up a hash of dependence information
331
    #
332
 
333
    my %depends;
299 dpurdie 334
    my %multi;
245 dpurdie 335
    foreach my $be ( @build_list )
227 dpurdie 336
    {
245 dpurdie 337
        Verbose( DisplayPath ("Build file: " . $be->{dir} . " Name: " . $be->{file} ));
338
#        DebugDumpData ("be", $be );
227 dpurdie 339
 
340
        #
299 dpurdie 341
        #   Catch multiple builds for the same package
342
        #   Report later - when we have all
343
        #
321 dpurdie 344
        next unless ( $be->{mname} );
299 dpurdie 345
        push @{$multi{$be->{mname}}},$be->{dir};
346
 
347
        #
227 dpurdie 348
        #   Add into dependency struct
349
        #
245 dpurdie 350
        $depends{$be->{package}}{depends} = $be->{depends};
351
        $depends{$be->{package}}{entry} = $be;
227 dpurdie 352
    }
353
 
299 dpurdie 354
    foreach my $mname ( sort keys %multi )
355
    {
356
        ReportError ("Mutiple builders for : $mname", @{$multi{$mname}} )
357
            if ( scalar @{$multi{$mname}} > 1 );
358
    }
359
    ErrorDoExit();
360
 
245 dpurdie 361
#DebugDumpData ("depends", \%depends );
362
 
227 dpurdie 363
    #
364
    #   Determine the build order
365
    #
366
    @build_order = ();
367
    my $more = 1;
368
    my $level = 0;
369
 
370
    #
255 dpurdie 371
    #   Remove any dependencies to 'external' packages
227 dpurdie 372
    #   These will not be met internally and can be regarded as constant
373
    #
374
    foreach my $key ( keys %depends )
375
    {
376
        foreach my $build ( keys( %{$depends{$key}{depends}} ))
377
        {
378
            unless (exists $depends{$build})
379
            {
380
                push @{$extern_deps{$build}{$depends{$key}{depends}{$build}}}, $key;
381
                $depends{$key}{entry}{edeps}{$build} = $depends{$key}{depends}{$build};
382
                delete ($depends{$key}{depends}{$build}) ;
383
                Verbose2( "Not in set: $build");
384
            }
385
            else
386
            {
387
                $depends{$key}{entry}{ideps}{$build} = 1;
388
            }
389
        }
390
    }
391
    while ( $more )
392
    {
393
        $more = 0;
394
        $level++;
395
        my @build;
396
 
397
        #
398
        #   Locate packages with no dependencies
399
        #
400
        foreach my $key ( keys %depends )
401
        {
402
            next if ( keys( %{$depends{$key}{depends}} ) );
403
            push @build, $key;
404
        }
405
 
406
        foreach my $build ( @build )
407
        {
408
            $more = 1;
409
            my $fe = $depends{$build}{entry};
410
            $fe->{level} = $level;
411
            $packages{$build} = $fe;
412
            push @build_order, $fe;
413
            delete $depends{$build};
414
            delete $fe->{depends};                          # remove now its not needed
415
        }
416
 
417
        foreach my $key ( keys %depends )
418
        {
419
            foreach my $build ( @build )
420
            {
421
                delete $depends{$key}{depends}{$build};
422
            }
423
        }
424
    }
425
 
426
    #
427
    #   Just to be sure to be sure
428
    #
245 dpurdie 429
    if ( keys %depends )
430
    {
431
        #DebugDumpData ("depends", \%depends );
432
        Error( "Internal algorithm error: Bad dependancy walk",
433
               "Possible circular dependency");
434
    }
227 dpurdie 435
 
436
#    DebugDumpData ("Order", \@build_order);
437
}
438
 
439
#-------------------------------------------------------------------------------
440
# Function        : cmd
441
#
442
# Description     : Execute a command in all the sandboxes
443
#                       Locate the base of the sandbox
444
#                       Locate all packages in the sandbox
445
#                       Locate all build files in each sandbox
446
#                       Determine build order
447
#                       Issue commands for each sandbox in order
448
#
255 dpurdie 449
# Inputs          : Arguments passed to jats build
227 dpurdie 450
#
451
# Returns         : Will exit
452
#
453
sub cmd
454
{
455
    my @cmds = @_;
456
    #
457
    #   Determine Sandbox information
458
    #   Populate global variables
459
    #
460
    calc_sandbox_info();
461
    foreach my $fe ( @build_order )
462
    {
463
        my $dir = $fe->{dir};
255 dpurdie 464
        Message( "Level:" . $fe->{level} . " Name: " . $fe->{mname} ,
227 dpurdie 465
                  DisplayPath ("        Path: $fe->{dir}" ));
466
 
263 dpurdie 467
        my $result = JatsCmd( "-cd=$dir", @cmds);
255 dpurdie 468
        Error ("Cmd failure") if ( $result );
227 dpurdie 469
    }
470
 
471
    exit 0;
472
}
473
 
474
#-------------------------------------------------------------------------------
273 dpurdie 475
# Function        : clean
476
#
477
# Description     : Execute a command in all the sandboxes
478
#                       Locate the base of the sandbox
479
#                       Locate all packages in the sandbox
480
#                       Locate all build files in each sandbox
481
#                       Determine build order
482
#                       Issue commands for each sandbox in order
483
#
484
# Inputs          : Arguments passed to jats build
485
#
486
# Returns         : Will exit
487
#
488
sub clean
489
{
490
    my ($mode, @cmds ) = @_;
491
    #
492
    #   Determine Sandbox information
493
    #   Populate global variables
494
    #
495
    calc_sandbox_info();
496
 
497
    my @cmd = $mode eq 'clobber' ? ('clobber') : ('make', 'clean' );
498
 
499
    #
500
    #   Clobber and clean need to be done in the reverse order
501
    #
502
    foreach my $fe ( reverse @build_order )
503
    {
504
        my $dir = $fe->{dir};
505
        Message( "Level:" . $fe->{level} . " Name: " . $fe->{mname} ,
506
                  DisplayPath ("        Path: $fe->{dir}" ));
507
 
508
        my $result = JatsCmd( "-cd=$dir", @cmd, @cmds);
509
        Error ("Cmd failure") if ( $result );
510
    }
511
 
512
    exit 0;
513
}
514
 
515
 
516
#-------------------------------------------------------------------------------
227 dpurdie 517
#   Documentation
518
#
519
 
520
=pod
521
 
522
=head1 NAME
523
 
524
jats_sandbox - Build in a Development Sandbox
525
 
526
=head1 SYNOPSIS
527
 
255 dpurdie 528
  jats sandbox [options] [commands]
227 dpurdie 529
 
530
 Options:
299 dpurdie 531
    -help[=n]          - Display help with specified detail
227 dpurdie 532
    -help -help        - Detailed help message
533
    -man               - Full documentation
534
 
535
 Commands:
536
    help                - Same as -help
537
    create              - Create a sandbox in the current directory
299 dpurdie 538
    delete              - Delete the sandbox
255 dpurdie 539
    info [[-v]-v]       - Sandbox information. -v: Be more verbose
227 dpurdie 540
    cmd                 - Do commands in all sandbox components
275 dpurdie 541
    all                 - Do 'build and make' in all sandbox components
542
    build               - Do 'build' in all sandbox components
543
    make                - Do 'make' in all sandbox components
273 dpurdie 544
    clean               - Do 'make clean' in all sandbox components
545
    clobber             - Do 'build clobber' is all sandbox components
227 dpurdie 546
 
547
=head1 OPTIONS
548
 
549
=over 8
550
 
299 dpurdie 551
=item B<-help[=n]>
227 dpurdie 552
 
553
Print a brief help message and exits.
299 dpurdie 554
There are three levels of help
227 dpurdie 555
 
299 dpurdie 556
=over 8
557
 
558
=item   1 Brief synopsis
559
 
560
=item   2 Synopsis and option summary
561
 
562
=item   3 Detailed help in man format
563
 
564
=back 8
565
 
227 dpurdie 566
=item B<-help -help>
567
 
568
Print a detailed help message with an explanation for each option.
569
 
570
=item B<-man>
571
 
299 dpurdie 572
Prints the manual page and exits. This is the same a -help=3
227 dpurdie 573
 
279 dpurdie 574
=back
575
 
227 dpurdie 576
=head1 DESCRIPTION
577
 
299 dpurdie 578
This program is the primary tool for the maintenance of Development Sandboxes.
579
 
227 dpurdie 580
More documentation will follow.
581
 
279 dpurdie 582
=head2 SANDBOX DIRECTORY
583
 
299 dpurdie 584
The sandbox directory is marked as being a sandbox through the use of the
585
'sandbox create' command. This will create a suitable structure within the
279 dpurdie 586
current directory.
587
 
588
Several JATS commands operate differently within a sandbox. The 'extract' and
589
'release' commands will create static viwes within the sandbox and not the
590
normal directory. The 'sandbox' sub commands can only be used within a sandbox.
591
 
592
The sandbox directory contains sub directories, each should contain a single
593
package. Sub directories may be created with the 'jats extract' command.
594
 
595
Note: Symbolic links are not supported. They cannot work as he sandbox mechanism
596
requires that all the packages be conatined within a sub directory tree so
597
that the root of the sandbox can be located by a simple scan of the directory
598
tree.
599
 
600
If a package subdirectory contains a file called 'stop', then that package
601
will not be considered as a part of the build-set.
602
 
299 dpurdie 603
=head2 COMMAND SUMMARY
604
 
605
=head3 create
606
 
607
The 'create' command will create a sandbox in the users current directory. It is
608
not possible to create a sandbox within a sandbox.
609
 
610
A sandbox can be created in a directory that contains files and subdirectories.
611
 
612
The create command simply places a known directory in the current directory.
613
This dorectory is used by the sandboxing process. It may be manually deleted, or
614
deleted with the 'delete' command.
615
 
616
=head3 delete
617
 
618
The 'delete' command will delete the sandbox's marker directory. The command may
619
be executed anywhere within the sandbox.
620
 
621
Once the sanbox has been deleted, the user must remove the components within the
622
sandbox.
623
 
624
=head3 info
625
 
626
The 'info' command will display information about the build order and the
627
depenedencies of packages that it finds within the sandbox.
628
 
629
The command will accept one option '-v' to increase the verbosity of the
630
information being displayed.
631
 
632
=over 8
633
 
634
=item * No Verbosity
635
 
636
The basic command will display the build order and the external
637
dependencies
638
 
639
=item Verbosity of 1
640
 
641
This level of verbosoity will display the build order and detailed information
642
on the dependencies. The dependencies will be prefixed with:
643
 
644
=over 8
645
 
646
=item   E   Dependent Package is external to the sandbox
647
 
648
=item   I   Dependent Package is internal to the sandbox
649
 
650
=back
651
 
652
This level of verbosity display information on packages that are external to the
653
sandbox. External dependencies may be prefixed with a '*'. This indicates that
654
multiple versions of this package are being used by sandboxed components.
655
 
656
The internal consumer of the external package is also shown. These are
657
prefixed with a 'U'.
658
 
659
=item Verbosity of 2
660
 
661
Reserved forfuture use
662
 
663
=item Verbosity over 2
664
 
665
This should be considered a debug option. Undocument internal information will
666
be displayed.
667
 
668
=back
669
 
227 dpurdie 670
=cut
671
 
255 dpurdie 672