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
        #
344
        push @{$multi{$be->{mname}}},$be->{dir};
345
 
346
        #
227 dpurdie 347
        #   Add into dependency struct
348
        #
245 dpurdie 349
        $depends{$be->{package}}{depends} = $be->{depends};
350
        $depends{$be->{package}}{entry} = $be;
227 dpurdie 351
    }
352
 
299 dpurdie 353
    foreach my $mname ( sort keys %multi )
354
    {
355
        ReportError ("Mutiple builders for : $mname", @{$multi{$mname}} )
356
            if ( scalar @{$multi{$mname}} > 1 );
357
    }
358
    ErrorDoExit();
359
 
245 dpurdie 360
#DebugDumpData ("depends", \%depends );
361
 
227 dpurdie 362
    #
363
    #   Determine the build order
364
    #
365
    @build_order = ();
366
    my $more = 1;
367
    my $level = 0;
368
 
369
    #
255 dpurdie 370
    #   Remove any dependencies to 'external' packages
227 dpurdie 371
    #   These will not be met internally and can be regarded as constant
372
    #
373
    foreach my $key ( keys %depends )
374
    {
375
        foreach my $build ( keys( %{$depends{$key}{depends}} ))
376
        {
377
            unless (exists $depends{$build})
378
            {
379
                push @{$extern_deps{$build}{$depends{$key}{depends}{$build}}}, $key;
380
                $depends{$key}{entry}{edeps}{$build} = $depends{$key}{depends}{$build};
381
                delete ($depends{$key}{depends}{$build}) ;
382
                Verbose2( "Not in set: $build");
383
            }
384
            else
385
            {
386
                $depends{$key}{entry}{ideps}{$build} = 1;
387
            }
388
        }
389
    }
390
    while ( $more )
391
    {
392
        $more = 0;
393
        $level++;
394
        my @build;
395
 
396
        #
397
        #   Locate packages with no dependencies
398
        #
399
        foreach my $key ( keys %depends )
400
        {
401
            next if ( keys( %{$depends{$key}{depends}} ) );
402
            push @build, $key;
403
        }
404
 
405
        foreach my $build ( @build )
406
        {
407
            $more = 1;
408
            my $fe = $depends{$build}{entry};
409
            $fe->{level} = $level;
410
            $packages{$build} = $fe;
411
            push @build_order, $fe;
412
            delete $depends{$build};
413
            delete $fe->{depends};                          # remove now its not needed
414
        }
415
 
416
        foreach my $key ( keys %depends )
417
        {
418
            foreach my $build ( @build )
419
            {
420
                delete $depends{$key}{depends}{$build};
421
            }
422
        }
423
    }
424
 
425
    #
426
    #   Just to be sure to be sure
427
    #
245 dpurdie 428
    if ( keys %depends )
429
    {
430
        #DebugDumpData ("depends", \%depends );
431
        Error( "Internal algorithm error: Bad dependancy walk",
432
               "Possible circular dependency");
433
    }
227 dpurdie 434
 
435
#    DebugDumpData ("Order", \@build_order);
436
}
437
 
438
#-------------------------------------------------------------------------------
439
# Function        : cmd
440
#
441
# Description     : Execute a command in all the sandboxes
442
#                       Locate the base of the sandbox
443
#                       Locate all packages in the sandbox
444
#                       Locate all build files in each sandbox
445
#                       Determine build order
446
#                       Issue commands for each sandbox in order
447
#
255 dpurdie 448
# Inputs          : Arguments passed to jats build
227 dpurdie 449
#
450
# Returns         : Will exit
451
#
452
sub cmd
453
{
454
    my @cmds = @_;
455
    #
456
    #   Determine Sandbox information
457
    #   Populate global variables
458
    #
459
    calc_sandbox_info();
460
    foreach my $fe ( @build_order )
461
    {
462
        my $dir = $fe->{dir};
255 dpurdie 463
        Message( "Level:" . $fe->{level} . " Name: " . $fe->{mname} ,
227 dpurdie 464
                  DisplayPath ("        Path: $fe->{dir}" ));
465
 
263 dpurdie 466
        my $result = JatsCmd( "-cd=$dir", @cmds);
255 dpurdie 467
        Error ("Cmd failure") if ( $result );
227 dpurdie 468
    }
469
 
470
    exit 0;
471
}
472
 
473
#-------------------------------------------------------------------------------
273 dpurdie 474
# Function        : clean
475
#
476
# Description     : Execute a command in all the sandboxes
477
#                       Locate the base of the sandbox
478
#                       Locate all packages in the sandbox
479
#                       Locate all build files in each sandbox
480
#                       Determine build order
481
#                       Issue commands for each sandbox in order
482
#
483
# Inputs          : Arguments passed to jats build
484
#
485
# Returns         : Will exit
486
#
487
sub clean
488
{
489
    my ($mode, @cmds ) = @_;
490
    #
491
    #   Determine Sandbox information
492
    #   Populate global variables
493
    #
494
    calc_sandbox_info();
495
 
496
    my @cmd = $mode eq 'clobber' ? ('clobber') : ('make', 'clean' );
497
 
498
    #
499
    #   Clobber and clean need to be done in the reverse order
500
    #
501
    foreach my $fe ( reverse @build_order )
502
    {
503
        my $dir = $fe->{dir};
504
        Message( "Level:" . $fe->{level} . " Name: " . $fe->{mname} ,
505
                  DisplayPath ("        Path: $fe->{dir}" ));
506
 
507
        my $result = JatsCmd( "-cd=$dir", @cmd, @cmds);
508
        Error ("Cmd failure") if ( $result );
509
    }
510
 
511
    exit 0;
512
}
513
 
514
 
515
#-------------------------------------------------------------------------------
227 dpurdie 516
#   Documentation
517
#
518
 
519
=pod
520
 
521
=head1 NAME
522
 
523
jats_sandbox - Build in a Development Sandbox
524
 
525
=head1 SYNOPSIS
526
 
255 dpurdie 527
  jats sandbox [options] [commands]
227 dpurdie 528
 
529
 Options:
299 dpurdie 530
    -help[=n]          - Display help with specified detail
227 dpurdie 531
    -help -help        - Detailed help message
532
    -man               - Full documentation
533
 
534
 Commands:
535
    help                - Same as -help
536
    create              - Create a sandbox in the current directory
299 dpurdie 537
    delete              - Delete the sandbox
255 dpurdie 538
    info [[-v]-v]       - Sandbox information. -v: Be more verbose
227 dpurdie 539
    cmd                 - Do commands in all sandbox components
275 dpurdie 540
    all                 - Do 'build and make' in all sandbox components
541
    build               - Do 'build' in all sandbox components
542
    make                - Do 'make' in all sandbox components
273 dpurdie 543
    clean               - Do 'make clean' in all sandbox components
544
    clobber             - Do 'build clobber' is all sandbox components
227 dpurdie 545
 
546
=head1 OPTIONS
547
 
548
=over 8
549
 
299 dpurdie 550
=item B<-help[=n]>
227 dpurdie 551
 
552
Print a brief help message and exits.
299 dpurdie 553
There are three levels of help
227 dpurdie 554
 
299 dpurdie 555
=over 8
556
 
557
=item   1 Brief synopsis
558
 
559
=item   2 Synopsis and option summary
560
 
561
=item   3 Detailed help in man format
562
 
563
=back 8
564
 
227 dpurdie 565
=item B<-help -help>
566
 
567
Print a detailed help message with an explanation for each option.
568
 
569
=item B<-man>
570
 
299 dpurdie 571
Prints the manual page and exits. This is the same a -help=3
227 dpurdie 572
 
279 dpurdie 573
=back
574
 
227 dpurdie 575
=head1 DESCRIPTION
576
 
299 dpurdie 577
This program is the primary tool for the maintenance of Development Sandboxes.
578
 
227 dpurdie 579
More documentation will follow.
580
 
279 dpurdie 581
=head2 SANDBOX DIRECTORY
582
 
299 dpurdie 583
The sandbox directory is marked as being a sandbox through the use of the
584
'sandbox create' command. This will create a suitable structure within the
279 dpurdie 585
current directory.
586
 
587
Several JATS commands operate differently within a sandbox. The 'extract' and
588
'release' commands will create static viwes within the sandbox and not the
589
normal directory. The 'sandbox' sub commands can only be used within a sandbox.
590
 
591
The sandbox directory contains sub directories, each should contain a single
592
package. Sub directories may be created with the 'jats extract' command.
593
 
594
Note: Symbolic links are not supported. They cannot work as he sandbox mechanism
595
requires that all the packages be conatined within a sub directory tree so
596
that the root of the sandbox can be located by a simple scan of the directory
597
tree.
598
 
599
If a package subdirectory contains a file called 'stop', then that package
600
will not be considered as a part of the build-set.
601
 
299 dpurdie 602
=head2 COMMAND SUMMARY
603
 
604
=head3 create
605
 
606
The 'create' command will create a sandbox in the users current directory. It is
607
not possible to create a sandbox within a sandbox.
608
 
609
A sandbox can be created in a directory that contains files and subdirectories.
610
 
611
The create command simply places a known directory in the current directory.
612
This dorectory is used by the sandboxing process. It may be manually deleted, or
613
deleted with the 'delete' command.
614
 
615
=head3 delete
616
 
617
The 'delete' command will delete the sandbox's marker directory. The command may
618
be executed anywhere within the sandbox.
619
 
620
Once the sanbox has been deleted, the user must remove the components within the
621
sandbox.
622
 
623
=head3 info
624
 
625
The 'info' command will display information about the build order and the
626
depenedencies of packages that it finds within the sandbox.
627
 
628
The command will accept one option '-v' to increase the verbosity of the
629
information being displayed.
630
 
631
=over 8
632
 
633
=item * No Verbosity
634
 
635
The basic command will display the build order and the external
636
dependencies
637
 
638
=item Verbosity of 1
639
 
640
This level of verbosoity will display the build order and detailed information
641
on the dependencies. The dependencies will be prefixed with:
642
 
643
=over 8
644
 
645
=item   E   Dependent Package is external to the sandbox
646
 
647
=item   I   Dependent Package is internal to the sandbox
648
 
649
=back
650
 
651
This level of verbosity display information on packages that are external to the
652
sandbox. External dependencies may be prefixed with a '*'. This indicates that
653
multiple versions of this package are being used by sandboxed components.
654
 
655
The internal consumer of the external package is also shown. These are
656
prefixed with a 'U'.
657
 
658
=item Verbosity of 2
659
 
660
Reserved forfuture use
661
 
662
=item Verbosity over 2
663
 
664
This should be considered a debug option. Undocument internal information will
665
be displayed.
666
 
667
=back
668
 
227 dpurdie 669
=cut
670
 
255 dpurdie 671