Subversion Repositories DevTools

Rev

Rev 7388 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
263 dpurdie 1
########################################################################
6177 dpurdie 2
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
227 dpurdie 3
#
5499 dpurdie 4
# Module name   : create_dpkg.pl
4549 dpurdie 5
# Module type   : Makefile system
263 dpurdie 6
# Compiler(s)   : Perl
4549 dpurdie 7
# Environment(s): jats
227 dpurdie 8
#
263 dpurdie 9
# Description   : This script is used to create a dpkg_archive.
5527 dpurdie 10
#                 Features:
4549 dpurdie 11
#                   * No user interaction
5527 dpurdie 12
#                   * Generates files list for ReleaseNote integration
13
#                   * Can generate package fragemts as a tarball for build system
227 dpurdie 14
#
4549 dpurdie 15
# Usage:        : See POD
227 dpurdie 16
#
263 dpurdie 17
#......................................................................#
227 dpurdie 18
 
4549 dpurdie 19
 
263 dpurdie 20
require 5.008_002;
227 dpurdie 21
 
22
# Include Standard Perl Functions
23
#
24
use strict;
263 dpurdie 25
use warnings;
227 dpurdie 26
use Cwd;
27
use Getopt::Long;
28
use File::Basename;
29
use File::Find;
30
use File::Path;
31
use File::Copy;
32
use Pod::Usage;
263 dpurdie 33
use Digest::MD5;
4549 dpurdie 34
use XML::Simple;
4633 dpurdie 35
use Encode qw(decode encode);
227 dpurdie 36
 
37
use JatsError;
263 dpurdie 38
use JatsEnv;
227 dpurdie 39
use DescPkg;
40
use FileUtils;
5527 dpurdie 41
use JatsSystem;
6177 dpurdie 42
use JatsVersionUtils;
227 dpurdie 43
 
363 dpurdie 44
#
45
#   Under Windows we need the Win32::FileSecurity module
46
#   It only exists under windows
47
#
48
my $Win32 = eval "require Win32::FileSecurity";
49
 
227 dpurdie 50
# define Global variables
51
#
4549 dpurdie 52
my $VERSION = "3.0.0";
227 dpurdie 53
my $PROGNAME = "create_dpkg.pl";
54
 
279 dpurdie 55
# Globals imported from environment
56
#
57
our $GBE_MACHTYPE;
58
our $GBE_HOSTNAME;
59
our $USER;
60
our $GBE_ABT;
227 dpurdie 61
 
279 dpurdie 62
# Global variables
63
#
227 dpurdie 64
my $DPKG_NAME     = "";
65
my $DESC_NAME     = "";
66
my $DPKG_VERSION  = "";
6177 dpurdie 67
my $DPKG_PRJ      = "";
227 dpurdie 68
my $DESCPKG_FILE  = "";
69
my $DESCPKG_TYPE  = "";
70
my $CWD_DIR       = cwd;
71
my $SRC_ROOT;
72
my $DPKG_DIR;
73
my $DPKG_ROOT;
4003 dpurdie 74
my $PKG_BASE;
263 dpurdie 75
my $bad_merge_count = 0;
4424 dpurdie 76
my @bad_symlinks;
4549 dpurdie 77
my @fileList;
78
my $descPkgCount = 0;
5578 dpurdie 79
my @tmpFiles;
227 dpurdie 80
 
81
#
82
#   Option variables
83
#
84
my $opt_help = 0;
85
my $opt_manual = 0;
86
my $opt_verbose = 0;
87
my $opt_quiet = 0;
4549 dpurdie 88
my $opt_delete = 0;
227 dpurdie 89
my $opt_override = 0;
90
my $opt_merge = 0;
91
my $opt_archive;
92
my $opt_generic;
93
my $opt_pname;
94
my $opt_pversion;
95
my $opt_test;
263 dpurdie 96
my $opt_md5 = 1;
4549 dpurdie 97
my $opt_outfile;
98
my $opt_info;
5527 dpurdie 99
my $opt_tarmode;
100
my $opt_testArchive;
5550 dpurdie 101
my $opt_noBuild;
5578 dpurdie 102
my $opt_keepTemp;
7547 dpurdie 103
my $opt_tarzip;
227 dpurdie 104
 
105
#
106
#   Structure to translate -archive=xxx option to archive variable
107
#   These are the various dpkg_archives known to JATS
108
#
4688 dpurdie 109
my %Archive2Var =( 'main'      => 'GBE_DPKG',
110
                   'store'     => 'GBE_DPKG_STORE',
111
                   'cache'     => 'GBE_DPKG_CACHE',
112
                   'local'     => 'GBE_DPKG_LOCAL',
113
                   'sandbox'   => 'GBE_DPKG_SBOX',
114
                   'deploy'    => 'GBE_DPLY',
115
                   'replica'   => 'GBE_DPKG_REPLICA',
6276 dpurdie 116
                   'escrow'    => 'GBE_DPKG_ESCROW',
227 dpurdie 117
                   );
118
 
119
#------------------------------------------------------------------------------
120
#------------------------------------------------------------------------------
121
# Subroutines
122
#------------------------------------------------------------------------------
123
#------------------------------------------------------------------------------
124
 
125
#------------------------------------------------------------------------------
126
sub LogFileOp
127
#
128
# Description:
129
#       This sub-routine is used to generate a consistent informational log
130
#------------------------------------------------------------------------------
131
{
132
    my ($opr, $file) = @_;
5582 dpurdie 133
 
7547 dpurdie 134
    # Shorten the display by using symbolic pathnames where possible
6177 dpurdie 135
    $file =~ s/\Q$DPKG_DIR\E/PKGDIR/;
5532 dpurdie 136
    $file =~ s/\Q$DPKG_ROOT\E/DPKG/;
137
    $file =~ s/\Q$SRC_ROOT\E/PKG/;
5578 dpurdie 138
    $file =~ s/\Q$CWD_DIR\E/CWD/;
227 dpurdie 139
 
140
    Information (sprintf( "%-15s [%s]", $opr, $file));
141
}
142
 
4549 dpurdie 143
#-------------------------------------------------------------------------------
144
# Function        : addFile 
145
#
146
# Description     : Add a file to the list of transferred files
147
#
148
# Inputs          : $type           - File type
149
#                   $source         - Source file - full path
150
#                                     Use local copy, not network copy for file ops
151
#                   $target         - Target file name
152
#                   $md5sum         - Precalculated MD5 sum 
153
#
154
# Returns         : 
155
#
156
sub addFile
157
{
158
    my ($type, $source, $target, $md5sum) = @_;
159
    my %data;
160
 
161
    if ((not defined $md5sum) && ($type eq 'file'))
162
    {
163
        Verbose("Calculate MD5 Digest: $source");
164
        open(my $fh , $source) or Error ("Can't open '$source': $!");
165
        binmode $fh, ':crlf';
166
        $md5sum = Digest::MD5->new->addfile($fh)->hexdigest;
167
        close $fh;
168
    }
169
 
5532 dpurdie 170
    $target =~ s~\Q$DPKG_DIR\E~~;
4549 dpurdie 171
    $target =~ s~^/~~;
172
    $target =~ s~/$~~;
173
 
4633 dpurdie 174
    #
175
    #   Convert from iso-8859-1 into utf-8
176
    #
177
    $target = decode( 'iso-8859-1', $target );
178
    $target = encode( 'utf-8', $target );
179
 
4549 dpurdie 180
    if ($type eq 'dir')
181
    {
182
        $data{path} = $target;
183
    }
184
    else
185
    {
186
        $data{path} = StripFileExt($target);
187
        $data{name} = StripDir($target);
188
        if ($type eq 'file')
189
        {
190
            $data{size} = (stat($source))[7];
191
            $data{md5sum} = $md5sum;
192
        }
193
    }
194
 
195
    $data{fullname} = $target;
196
    $data{type} = $type;
197
    $data{machtype} = $GBE_MACHTYPE;
198
    $data{host} = $GBE_HOSTNAME;
199
 
5499 dpurdie 200
    # Put a nice '/' on the end of the path elements
4549 dpurdie 201
    $data{path} .= '/'
202
        if ( exists ($data{path}) && length($data{path}) > 0);
203
 
204
    push @fileList, \%data;
205
}
206
 
207
#-------------------------------------------------------------------------------
208
# Function        : writeFileInfo 
209
#
210
# Description     : Write out an XML file that contains this processes
211
#                   contribution to the output package 
212
#
5527 dpurdie 213
# Inputs          : $targetDir          - Base directory for the file 
4549 dpurdie 214
#
215
# Returns         : 
216
#
217
sub writeFileInfo
218
{
5527 dpurdie 219
    my ($targetDir) = @_;
4549 dpurdie 220
    my $data;
221
    $data->{file} = \@fileList;
222
 
223
    #
224
    #   Write out sections of XML
225
    #       Want control over the output order
226
    #       Use lots of attributes and only elements for arrays
227
    #       Save as one attribute per line - for readability
228
    #
5527 dpurdie 229
    $opt_outfile = $opt_generic ? "built.files.generic.xml" : "built.files.$GBE_HOSTNAME.xml";
230
    $opt_outfile = catfile( $targetDir, $opt_outfile); 
4549 dpurdie 231
 
232
    LogFileOp ('Meta File', $opt_outfile);
233
    my $xs = XML::Simple->new( NoAttr =>0, AttrIndent => 1 );
234
 
235
    open (my $XML, '>', $opt_outfile) || Error ("Cannot create output file: $opt_outfile", $!);
236
    $xs->XMLout($data, 
237
                'RootName' => 'files', 
238
                'XMLDecl'  => '<?xml version="1.0" encoding="UTF-8"?>',
239
                'OutputFile' => $XML);
240
    close $XML;
241
 
242
}
243
 
227 dpurdie 244
#------------------------------------------------------------------------------
245
sub Init
246
#
247
# Description:
248
#     This function is used to process any command line arguements
249
#     and print the start banner.
250
#
251
#------------------------------------------------------------------------------
252
{
253
    # Process any command line arguements...
254
    my $result = GetOptions (
5527 dpurdie 255
                'help:+'        => \$opt_help,              # flag, multiple use allowed
256
                'manual:3'      => \$opt_help,              # flag
257
                'verbose:+'     => \$opt_verbose,           # flag, multiple use allowed
258
                'override!'     => \$opt_override,          # [no]flag (No longer used. Backward compat with build tool)
259
                'delete!'       => \$opt_delete,            # [no]flag
260
                'merge|m!'      => \$opt_merge,             # [no]flag.
261
                'archive=s'     => \$opt_archive,           # string
262
                'quiet+'        => \$opt_quiet,             # Flag
263
                'generic!'      => \$opt_generic,           # [no]Flag
264
                'pname=s'       => \$opt_pname,             # string
265
                'pversion=s'    => \$opt_pversion,          # string
266
                'test!'         => \$opt_test,              # [no]flag
267
                'md5!'          => \$opt_md5,               # [no]flag
268
                'info!'         => \$opt_info,              # [no]flag
269
                'tarmode!'      => \$opt_tarmode,           # [no]flag
270
                'testArchive'   => \$opt_testArchive,       # [no]flag
5550 dpurdie 271
                'nobuild'       => \$opt_noBuild,           # flag
5578 dpurdie 272
                'keepTemp'      => \$opt_keepTemp,          # flag
7547 dpurdie 273
                'tarzip'        => \$opt_tarzip,            # flag
227 dpurdie 274
                );
275
 
276
 
277
    #
278
    #   Process help and manual options
279
    #
280
    pod2usage(-verbose => 0, -message => "Version: $VERSION")  if ($opt_help == 1  || ! $result);
281
    pod2usage(-verbose => 1)  if ($opt_help == 2 );
263 dpurdie 282
    pod2usage(-verbose => 2)  if ($opt_help > 2);
227 dpurdie 283
 
284
    #
285
    #   Init the error and message subsystem
286
    #
287
    ErrorConfig( 'name'    =>'CREATE_DPKG',
288
                 'verbose' => $opt_verbose,
289
                 'quiet'   => $opt_quiet );
290
 
291
    if ($opt_verbose)
292
    {
293
       Verbose ("Program: $PROGNAME");
294
       Verbose ("Version: $VERSION");
295
    }
296
 
7547 dpurdie 297
    Error ("-tarmode and -tarzip are mutually exclusive") if ($opt_tarmode && $opt_tarzip);
298
    Error ("-test and -tarzip are mutually exclusive") if ($opt_test && $opt_tarzip);
299
 
227 dpurdie 300
    #
263 dpurdie 301
    #   Needed EnvVars
302
    #
303
    EnvImport ('GBE_MACHTYPE');
279 dpurdie 304
    EnvImport ('GBE_HOSTNAME');
305
    EnvImport ('USER' );
306
    EnvImportOptional ('GBE_ABT', "");
6276 dpurdie 307
    EnvImportOptional ('GBE_DPKG_ESCROW', "");
5578 dpurdie 308
    $CWD_DIR = catdir($CWD_DIR);
263 dpurdie 309
 
4003 dpurdie 310
    #
311
    #   Determine the target archive
312
    #   The default archive is GBE_DPKG, but this may be changed
313
    #
6276 dpurdie 314
    unless ( $opt_archive )
315
    {
316
        $opt_archive = 'main';
317
        $opt_archive = 'escrow' if ($::GBE_DPKG_ESCROW);
318
    }
7547 dpurdie 319
 
4003 dpurdie 320
    my $archive_tag = $Archive2Var{$opt_archive};
321
    Error("Unknown archive specified: $opt_archive")
322
        unless ( $archive_tag );
323
    $DPKG_ROOT = $ENV{$archive_tag} || '';
324
    Verbose ("Archive Variable: $archive_tag" );
325
    Verbose2 ("Archive Path: $DPKG_ROOT" );
279 dpurdie 326
 
263 dpurdie 327
    #
5527 dpurdie 328
    #   Append testArchive path
329
    #
330
    $DPKG_ROOT = catdir ($DPKG_ROOT, '.dpkg_archive', 'test_dpkg') if $opt_testArchive;
331
 
332
    #
4003 dpurdie 333
    #   Detect NoBuild marker
334
    #   This will bypass most of the operation of this package
335
    #
5550 dpurdie 336
    $opt_noBuild = 2 if -f 'noBuild.gbe';
337
    if ( $opt_noBuild)
4003 dpurdie 338
    {
6276 dpurdie 339
        Verbose ("NoBuild Marker file found");
4003 dpurdie 340
 
341
        $SRC_ROOT = '';
342
        $DPKG_NAME = 'pkg';
343
        $DESCPKG_FILE = 'descpkg';
344
        $PKG_BASE =$CWD_DIR;
5550 dpurdie 345
 
6281 dpurdie 346
        #
347
        #   Use provided name and version
348
        #   Comparatbility only with ANT build system. Not needed by JATS
349
        #
350
        if ($opt_pname && $opt_pversion) {
351
            $DPKG_NAME = $opt_pname;
352
            $DPKG_VERSION = $opt_pversion;
353
        }
354
 
4003 dpurdie 355
        return;
356
    }
357
 
358
    #
227 dpurdie 359
    #   Check for a "pkg" directory
360
    #   This may be in:
361
    #       1) The deploy directory (DEPLOY) build/deploy/descpkg
362
    #       2) The build directory (ANT)     build/pkg/descpkg
363
    #       3) The current directory (JATS)  pkg/xxxx/descpkg
364
    #
4003 dpurdie 365
    $PKG_BASE = "$CWD_DIR/build/deploy";
227 dpurdie 366
    Verbose2 ("Looking for descpkg: $PKG_BASE");
367
    if ( -f "$PKG_BASE/descpkg" )
368
    {
369
        #
370
        #   This is a deployment package.
371
        #   Force the use of the GBE_DPLY
372
        #
373
        $opt_archive = 'deploy' unless ( $opt_archive );
374
    }
375
    else
376
    {
377
        $PKG_BASE = "$CWD_DIR/build/pkg";
378
        Verbose ("Looking for descpkg: $PKG_BASE");
379
        if ( ! -f  "$PKG_BASE/descpkg" )
380
        {
381
            $PKG_BASE = "$CWD_DIR/pkg";
382
            Verbose ("Looking for descpkg: $PKG_BASE");
7388 dpurdie 383
            my @descPkgs = glob ("$PKG_BASE/*/descpkg");
227 dpurdie 384
            Error("Failed to find a package to transfer. Looked in:",
385
                  "./build/deploy",
386
                  "./build/pkg",
7388 dpurdie 387
                  "./pkg/*"
227 dpurdie 388
                  )
7388 dpurdie 389
                unless( @descPkgs );
390
            Error("Multiple packages detected:", @descPkgs) if (scalar @descPkgs > 1);
227 dpurdie 391
        }
392
    }
393
    Verbose("Package directory: $PKG_BASE");
394
 
395
    Error("Repository location not specified: $archive_tag")
396
        unless $DPKG_ROOT;
397
 
398
    Error("Failed to find Repository: $DPKG_ROOT")
399
        unless ( -d $DPKG_ROOT );
400
 
401
    #   Locate the package
402
    #   Packages are located by looking for a file called descpkg within the
403
    #   main package directory.
404
    #
405
    #   This installation process only handles one such file
406
    #
4549 dpurdie 407
    $descPkgCount = 0;
227 dpurdie 408
    File::Find::find( \&pkgFind, $PKG_BASE);
4549 dpurdie 409
 
410
    if ($descPkgCount > 1 )
411
    {
412
        Warning ("Package contains multiple ($descPkgCount) descpkg files");
413
    }
4003 dpurdie 414
}
227 dpurdie 415
 
4003 dpurdie 416
#-------------------------------------------------------------------------------
417
# Function        : CheckDescPkg
418
#
419
# Description     : Check the descpkg file
420
#
421
# Inputs          : Globals
422
#
423
# Returns         : Will not return on error
424
#
425
sub CheckDescPkg
426
{
6281 dpurdie 427
    #
428
    #   May not need a package description if performing a no-build
429
    #       The package name and version will may be provided on the command line
430
    #   Compatability for ANT builds
431
    #       
432
    #
433
    if ($opt_noBuild) {
434
        if ($opt_pname && $opt_pversion) {
435
            return;
436
        }
437
        Error("NoBuild operation requires package name and version")
438
    }
5550 dpurdie 439
 
227 dpurdie 440
    # Get the dpkg_archive version number we are  going to create.
441
    #
442
    Error("Descpkg file not found in package directory: $PKG_BASE")
443
        unless ( -f "$DESCPKG_FILE" );
444
 
445
    #
446
    #   Read in the package description and validate essential fields
447
    #
448
    GetDpkgArchiveVersion($DESCPKG_FILE);
449
    unless ( "$DPKG_VERSION" )
450
    {
451
        Error ("Incorrect descpkg content detected.",
452
               "Check JATS build.pl config.");
453
    }
454
 
455
    #
456
    #   Need to support two forms of pkg subdirectory
457
    #       1) packages are in a named subdir within 'pkg'
458
    #       2) package is within 'pkg' or 'deploy'
459
    #
460
    if ( $DPKG_NAME eq 'pkg' || $DPKG_NAME eq 'deploy' )
461
    {
462
        $DPKG_NAME = $DESC_NAME;
463
        unless ( $DESC_NAME )
464
        {
465
            Error ("Cannot determine package name",
466
                   "The packages 'descpkg' file is bad or missing");
467
        }
468
    }
469
    elsif ( $DESC_NAME ne $DPKG_NAME )
470
    {
471
        Error ("Package name MUST match package description",
472
               "Check build.pl and package.pl",
473
               "Package name: $DPKG_NAME",
474
               "Description : $DESC_NAME" );
475
    }
476
 
477
    #
478
    # lets just check to see if we have a version number before
479
    # we proceed.
480
    #
481
    unless ( $DPKG_VERSION )
482
    {
483
        Error("Cannot determine dpkg_archive version number.",
484
              "Check JATS build config.");
485
    }
486
 
6177 dpurdie 487
    my ($pn, $pv, $ps ) = SplitPackage ($DPKG_NAME, $DPKG_VERSION );
488
    $DPKG_PRJ = '.' . $ps if ( $ps ); 
489
 
227 dpurdie 490
    #
491
    #   Sanity test package name and version, if provided
492
    #
493
    if ( $opt_pname )
494
    {
495
        ReportError ("Package Name does not match expected name",
496
                     "Expected: '$opt_pname'",
497
                     "Descpkg : '$DPKG_NAME'") unless ( $DPKG_NAME eq $opt_pname );
498
    }
499
    if ( $opt_pversion )
500
    {
501
        ReportError ("Package Version does not match expected version",
502
                     "Expected: '$opt_pversion'",
503
                     "Descpkg : '$DPKG_VERSION'") unless ( $DPKG_VERSION eq $opt_pversion );
504
    }
505
    ErrorDoExit();
4003 dpurdie 506
}
227 dpurdie 507
 
4003 dpurdie 508
#-------------------------------------------------------------------------------
509
# Function        : ShowInfo
510
#
511
# Description     : Show info to the user
512
#
513
# Inputs          : 
514
#
515
# Returns         : 
516
#
517
sub ShowInfo
518
{
227 dpurdie 519
    #
520
    #   Set up the target directory path and name
521
    #   It will be created later
522
    #
5527 dpurdie 523
    if ($opt_tarmode)
524
    {
525
        $DPKG_DIR = catdir($DPKG_ROOT, '.dpkg_archive', 'fragments');
526
    }
527
    else
528
    {
529
        $DPKG_DIR = catdir($DPKG_ROOT, $DPKG_NAME, $DPKG_VERSION );
530
    }
4003 dpurdie 531
 
227 dpurdie 532
    #
533
    #   Information for the user
534
    #
535
    Information ("---------------------------------------------------------------");
536
    Information ("Dpkg archive creation tool...");
537
    Information ("Version: $VERSION");
538
    Information ("");
539
    Information ("Information:");
6276 dpurdie 540
    Information ("Escrow Build") if ($opt_archive eq 'escrow');
7547 dpurdie 541
    Information ("Working dir      = [$CWD_DIR]");
542
    Information ("Package Root     = [$SRC_ROOT]");
543
    Information ("Repository       = [$DPKG_ROOT]") unless ($opt_tarzip);
544
    Information ("                   *Non Standard archive") unless ($opt_archive eq 'main' || $opt_archive eq 'escrow' || $opt_tarzip);
545
    Information ("Target dir       = [$DPKG_DIR]") unless ($opt_tarzip);
546
    Information ("Target dir       = [$CWD_DIR]") if ($opt_tarzip);
547
    Information1("DPKG_NAME        = [$DPKG_NAME]");
548
    Information1("DPKG_VERSION     = [$DPKG_VERSION]");
549
    Information1("DPKG_PRJ         = [$DPKG_PRJ]");
550
    Information1("GBE_MACHTYPE     = [$GBE_MACHTYPE]");
551
    Information1("GBE_HOSTNAME     = [$GBE_HOSTNAME]");
552
    Information1("GBE_ABT          = [$GBE_ABT]");
553
    Information1("USER             = [$USER]");
554
    Information ("")                               if ( $opt_merge || $opt_delete || $opt_info || $opt_tarmode || $opt_testArchive || $opt_noBuild || $opt_tarzip);
555
    Information ("Opt:NoBuild      = Enabled")     if ( $opt_noBuild );
556
    Information ("Opt:TarMode      = Enabled")     if ( $opt_tarmode );
557
    Information ("Opt:Delete       = Enabled")     if ( $opt_delete );
558
    Information ("Opt:Merge        = Enabled")     if ( $opt_merge );
559
    Information ("Opt:testArchive  = Enabled")     if ( $opt_testArchive );
560
    Information ("Opt:TarZip       = Enabled")     if ( $opt_tarzip );
561
    Information ("Opt:TestMode     = Enabled. No Package Transferred") if ( $opt_test );
562
    Information ("Opt:Info         = Enabled. No Package Transferred") if ( $opt_info );
563
    Warning     ("Sandbox Build    = Yes") if ($ENV{GBE_DPKG_SBOX}) ;
227 dpurdie 564
    Information ("---------------------------------------------------------------");
565
 
369 dpurdie 566
    #
567
    #   If the environment variable GBE_DPKG_SBOX is defined then the package
568
    #   is being built within a development sandbox. In such a sandbox the
569
    #   version numbers of the packages are ignored. Publishing a package
5499 dpurdie 570
    #   from such an environment is certainly not reproducible - so don't allow
369 dpurdie 571
    #   it to happen
572
    #
573
    #   Allow versions of 99.99.99 as these are known to be test versions
574
    #
6276 dpurdie 575
    unless ( $opt_archive eq 'local' || $opt_archive eq 'sandbox' || $opt_testArchive || $::GBE_DPKG_ESCROW)
369 dpurdie 576
    {
577
        if ( $ENV{GBE_DPKG_SBOX} )
578
        {
579
            unless ( $DPKG_VERSION =~ /^99.99.99/ )
580
            {
581
                Error("Cannot not publish a package that has been generated",
582
                   "within a Sandbox as the version of dependent packages",
583
                   "is not guaranteed.",
584
                   "Only version 99.99.99 is allowed");
585
            }
586
        }
587
    }
4003 dpurdie 588
}
369 dpurdie 589
 
590
 
227 dpurdie 591
#------------------------------------------------------------------------------
592
sub pkgFind
593
#
594
# Description:
235 dpurdie 595
#     This subroutine is used to locate the FIRST descpkg file in
227 dpurdie 596
#     the local pkg dir.
597
#
598
#------------------------------------------------------------------------------
599
{
600
    my($item)= "$File::Find::name";
601
    my($file)= File::Basename::basename($item);
602
 
603
    # we get the absolute path from the find, but we only require
604
    # a relative path from the starting dir.
605
    # so our start dir.
606
 
607
    # we need to determine which file we are dealing with
235 dpurdie 608
    if ( ! -d $item && $file =~ /^descpkg$/ )
227 dpurdie 609
    {
4549 dpurdie 610
        $descPkgCount++;
611
 
235 dpurdie 612
        #
613
        #   Only grab the first one
614
        #
615
        if ( $DESCPKG_FILE )
616
        {
5532 dpurdie 617
            $item =~ s~\Q$PKG_BASE\E/~~;
4549 dpurdie 618
            Verbose ("Multiple descpkg files:", $item );
235 dpurdie 619
            return;
620
        }
621
 
227 dpurdie 622
        $DESCPKG_FILE = $item;
623
        my($dir)= File::Basename::dirname($item);
624
        $DPKG_NAME = File::Basename::basename($dir);
5582 dpurdie 625
        $SRC_ROOT = catdir($dir);
227 dpurdie 626
    }
627
}
628
 
629
 
630
#------------------------------------------------------------------------------
631
sub GetDpkgArchiveVersion
632
#
633
# Description:
634
#     This subroutine is used to determine the version of the dpkg_archive.
635
#     We assume that the version number is in the descpkg file.
636
#
637
#     Need to allow for two forms of descpkg. Some one decided that a Java
638
#     Manifest would be a good descpkg file - a long time after the rest of the
639
#     world had been using an existing file format.
640
#
641
#     Lines are tagged
642
#
643
#     Once the version number is determined we set the
644
#     global DPKG_VERSION variable.
645
#
646
#------------------------------------------------------------------------------
647
{
648
    my ($path) = @_;
649
    my $line;
650
    my $type;
651
 
652
    #
653
    #   Use a common routine to parse the package descriptor
654
    #   There are several forms that may need to be processed
655
    #
7388 dpurdie 656
    Verbose ("GetDpkgArchiveVersion from $path");
227 dpurdie 657
    my $pkg_data = ReadDescpkg( $path );
658
    Error("Failed to open file [$path].") unless $pkg_data;
659
 
660
    $DESC_NAME    = $pkg_data->{'NAME'};
661
    $DPKG_VERSION = $pkg_data->{'VERSION_FULL'};
662
}
663
 
664
#-------------------------------------------------------------------------------
665
# Function        : TransferDescpkg
666
#
667
# Description     : Copy and process the descpkg file to the target
668
#
669
# Inputs          :
670
#
671
# Returns         :
672
#
673
sub TransferDescpkg
674
{
675
    my $result = CopyDescpkg( @_ );
676
    Error("Transfer descpkg: $result") if ( $result );
677
}
678
 
679
#------------------------------------------------------------------------------
680
sub CreateDpkgArchive
681
#
682
# Description:
683
#     This subroutine is used to create the dpkg_archive in the $DPKG_ROOT
684
#     location 
685
#
4969 dpurdie 686
#     We use the global DPKG_ROOT, DPKG_DIR, DPKG_NAME, and DPKG_VERSION
227 dpurdie 687
#     to create the required directory structure.
688
#
689
#     If the dpkg_archive is new (ie not a new version) it is assumed the user
690
#     has access to create the top level dir for the new dpkg_archive.
691
#
692
#     The new dpkg_archive is created with the permission of the user 
693
#     executing this script.
694
#
695
#     If an error ocurs during the dpkg_archive creation the script
696
#     will terminate.
697
#
698
#------------------------------------------------------------------------------
699
{
263 dpurdie 700
    #
227 dpurdie 701
    # first we need to ensure we have the top level directory
702
    #
703
    if ( -d $DPKG_DIR )
704
    {
705
        Warning("Detected previous dpkg_archive [$DPKG_DIR]");
4549 dpurdie 706
        Error ("Package already exists and Package merging not selected")
707
            unless ( $opt_delete || $opt_merge );
263 dpurdie 708
 
709
        #
710
        #   Target exists
711
        #   Unless we are merging, we need to blow the entire tree away
712
        #
713
        unless ( $opt_merge )
714
        {
715
            LogFileOp("Remove Prev Pkg",$DPKG_DIR);
716
            rmtree($DPKG_DIR);
717
 
718
            #
719
            #   At this point the target directory 'should not' exist
720
            #   but it may. Some packges (like JATS) have Unix links within
721
            #   dpkg_archive filesystem. These cannot be deleted under windows
722
            #
723
            #   Not nice, but we live with it.
724
            #
725
            Warning ("Unable to delete previous instance of the package")
726
                if ( -d $DPKG_DIR );
727
        }
227 dpurdie 728
    }
729
    Information("");
730
 
731
    #
732
    #   Create the top level directory
733
    #
734
    mkpath($DPKG_DIR, 0, 0775);
735
 
736
    #
4003 dpurdie 737
    #   Transfer source directory, unless this is a noBuild
738
    #
739
    if ( $SRC_ROOT ne '' )
227 dpurdie 740
    {
4003 dpurdie 741
        # Process the files
742
        #
743
        if ( -d $SRC_ROOT )
744
        {
745
            File::Find::find( \&pkgFind2, $SRC_ROOT );
263 dpurdie 746
 
4424 dpurdie 747
            if (@bad_symlinks)
748
            {
749
                my $msg = "Bad Symlinks: " . scalar @bad_symlinks;
750
                $opt_test ? ReportError($msg, @bad_symlinks) : Warning($msg, @bad_symlinks);
751
            }
752
 
4003 dpurdie 753
            if ( $bad_merge_count )
754
            {
755
                my $msg = "Merged files that differ: $bad_merge_count";
4424 dpurdie 756
                $opt_md5 ? ReportError($msg) : Warning($msg);
4003 dpurdie 757
            }
4424 dpurdie 758
            ErrorDoExit();
4003 dpurdie 759
        }
760
        else
263 dpurdie 761
        {
4003 dpurdie 762
            Error("Failed to find dir [$SRC_ROOT]",
763
                  "Check JATS config.");
263 dpurdie 764
        }
227 dpurdie 765
    }
766
 
767
    #
768
    #   Transfer of data is complete
4424 dpurdie 769
    #       Mark the archive with the build machine to indicate which parts of
770
    #       a multi-machine build have been performed
227 dpurdie 771
    #
5527 dpurdie 772
    my $touchfile = createBuiltFile($DPKG_DIR);
4634 dpurdie 773
    addFile('file', $touchfile, $touchfile);
227 dpurdie 774
 
775
    #
776
    #   If there is a .lnk file in the archive then remove it now that the
777
    #   archive has been transferred. The .lnk files are created in 'local'
778
    #   archives in order to simplify multi-package builds
779
    #
780
    my $link_file = "$DPKG_ROOT/$DPKG_NAME/$DPKG_VERSION.lnk";
781
    if ( -f $link_file )
782
    {
783
        LogFileOp("Removing Link",$link_file);
784
        unlink $link_file;
785
    }
786
 
4969 dpurdie 787
    #
5527 dpurdie 788
    #   Create the MD5 info file
789
    #   
790
    writeFileInfo($DPKG_DIR);
227 dpurdie 791
    return 1;
792
}
793
 
794
#------------------------------------------------------------------------------
795
sub pkgFind2
796
#
797
# Description:
798
#   This subroutine is used to locate all associated pkg files in
799
#   the local pkg dir.
800
#
801
#   This routine is called for each file and directory within the package
802
#   Some files and directories are treated in a special manner
803
#       - Top level directory is ignored
804
#
805
#
806
#
807
#------------------------------------------------------------------------------
808
{
809
    my $item = $File::Find::name;
810
    my $base = File::Basename::basename($item);
811
 
812
    #
813
    #   Calculate the target directory name
814
    #
815
    my $target = $item;
241 dpurdie 816
    $target = $DPKG_DIR . substr ( $item, length ($SRC_ROOT) );
227 dpurdie 817
 
818
    if ( -d $item )
819
    {
820
        #
821
        #   Ignore the top level directory
822
        #   It has already been created
823
        #
824
        return
825
            if ( $item eq $SRC_ROOT );
826
 
827
        #
828
        #   Directories are handled differently
829
        #       - Directories are created with nice permissions
263 dpurdie 830
        #       - If the directory already exists then it is being merged.
227 dpurdie 831
        #
832
        if ( ! -d "$target" )
833
        {
834
            LogFileOp("Creating Dir", $target);
835
            mkpath("$target", 0, 0775);
4549 dpurdie 836
            addFile('dir', $item , $target);
227 dpurdie 837
        }
838
    }
839
    else
840
    {
841
        #
842
        #   File copy
843
        #   If merging then do not overwrite an existing file
844
        #
845
        unless ( $opt_merge && -f $target )
846
        {
847
            if ( $item =~ m~/descpkg$~ )
848
            {
849
                LogFileOp("Rewrite File",$target);
4549 dpurdie 850
                TransferDescpkg( $item, $target );
363 dpurdie 851
                CORE::chmod oct("0664"), $target;
4549 dpurdie 852
                addFile('file', $item, $target);
227 dpurdie 853
            }
854
            else
855
            {
856
                #
857
                #   Copy file to destination
858
                #   If the file is a link, then duplicate the link contents
859
                #   Use: Unix libraries are created as two files:
860
                #        lib.xxxx.so -> libxxxx.so.vv.vv.vv
861
                #
862
                if ( -l $item )
863
                {
6387 dpurdie 864
                    my $niceBase = substr ( $item, 1+length ($SRC_ROOT) );
6276 dpurdie 865
                    if (TestSymlink( $niceBase,$_))
227 dpurdie 866
                    {
6276 dpurdie 867
                        # Don't copy broken Symlinks
868
                        # Perhaps this should be an error - but is will break escrow builds
869
                        #
870
                        LogFileOp("Bad SymLink", $target);
871
                    }
872
                    elsif (-f $item)
873
                    {
4424 dpurdie 874
                        LogFileOp("Copying Link", $target);
875
                        my $link = readlink $item;
876
                        Verbose( "Link: $item, $link");
877
                        symlink ($link, $target );
878
                        unless ( $link && -l $target )
879
                        {
880
                            Error("Failed to copy link [$item] to [$target]: $!");
881
                        }
4549 dpurdie 882
                        addFile('link', $item , $target);
227 dpurdie 883
                    }
884
                }
885
                elsif (File::Copy::copy($item, $target))
886
                {
887
                    LogFileOp("Copying File",$target);
363 dpurdie 888
                    #
889
                    #   Mark the file as executable by all
890
                    #   Under windows, this is tricky
891
                    #
892
                    if ( $Win32 )
893
                    {
894
                        my %hash;
895
                        $hash{Everyone} = Win32::FileSecurity::MakeMask( qw( FULL  ) );
896
                        Win32::FileSecurity::Set( $target, \%hash );
897
                    }
898
                    else
899
                    {
900
                        CORE::chmod oct("0775"), $target;
901
                    }
4549 dpurdie 902
                    addFile('file', $item, $target);
227 dpurdie 903
                }
904
                else
905
                {
906
                    Error("Failed to copy file [$item] to [$target]: $!");
907
                }
908
            }
909
        }
910
        else
911
        {
912
            #
913
            #   Merging packages
914
            #   Ensure that the descpkg file is "touched" so that caches
915
            #   that use this file as a timestamp can be updated
916
            #
917
            if ( $item =~ m~/descpkg$~ )
918
            {
919
                LogFileOp("Touch File",$target);
920
                TouchFile( $target ) && Error ( "Failed to touch: $target" );
4549 dpurdie 921
                addFile('merge', $item, $target);
227 dpurdie 922
            }
923
            else
924
            {
263 dpurdie 925
                #
926
                #   MD5 digest the files that are being merged
927
                #   Ignore version_*.h files as these are generated
928
                #   and may contain different dates and line endings
929
                #
267 dpurdie 930
                #   Don't put the files into 'binmode'
931
                #   Need to handle some level of Unix/DOS file endings
932
                #
933
                #
263 dpurdie 934
                my $msg = "Merge Skip File";
935
                unless ( $target =~ m~/version[^/]*\.h$~ )
936
                {
937
                    $msg = "Merge Test File";
938
                    #
939
                    #   Compare the two files with an MD5
940
                    #
941
                    local *FILE;
942
                    open(FILE, $target) or Error ("Can't open '$target': $!");
267 dpurdie 943
                    binmode FILE, ':crlf';
263 dpurdie 944
                    my $target_md5 = Digest::MD5->new->addfile(*FILE)->hexdigest;
945
                    close FILE;
946
 
947
                    open(FILE, $item) or Error ("Can't open '$item': $!");
267 dpurdie 948
                    binmode FILE, ':crlf';
263 dpurdie 949
                    my $source_md5 = Digest::MD5->new->addfile(*FILE)->hexdigest;
950
                    close FILE;
951
 
952
                    unless ( $source_md5 eq $target_md5 )
953
                    {
954
                        $msg = "DIFF: Merge Test File";
955
                        $bad_merge_count ++;
956
                    }
4549 dpurdie 957
                    addFile('merge', $item, $target, $target_md5);
263 dpurdie 958
                }
959
                LogFileOp($msg,$target);
227 dpurdie 960
            }
961
        }
962
    }
963
}
964
 
965
#-------------------------------------------------------------------------------
5527 dpurdie 966
# Function        : CreateDpkgArchiveTarBall 
967
#
968
# Description     : Similar to CreateDpkgArchive, but it will create a tar file within the target
969
#                   directory.
970
#                   
971
#                   This is used by the build system to:
972
#                       1) Greatly speedup the transfer of packages with a very large number of files
973
#                       2) Remove the need for an multi-filesytem, multi OS atomic lock on the package
974
#                       
975
#                   The build system will process the tarball and create the package archive
976
#                   In doing so it will handle merge errors
977
#
978
# Inputs          : 
979
#
980
# Returns         : 
981
#
982
sub CreateDpkgArchiveTarBall
983
{
984
    Information("");
985
    InitFileUtils();
986
 
987
 
988
    #
989
    #   If a 'noBuild' then create a dummy package directory simply
990
    #   to contain the metadata. 
991
    #   Delete any existing dir of the same name to ensure its clean.
992
    #
993
    if ( $SRC_ROOT eq '' )
994
    {
5582 dpurdie 995
        $SRC_ROOT = catdir(AbsPath('pkg/noBuild'));
5527 dpurdie 996
        RmDirTree ($SRC_ROOT);
997
        mkpath($SRC_ROOT, 0, 0775);
998
    }
999
 
1000
    #
1001
    #   Mark the archive with the build machine to indicate which parts of
1002
    #   a multi-machine build have been performed
1003
    #
1004
    createBuiltFile($SRC_ROOT);
1005
 
1006
    #   Process the source directory
1007
    #   A NoBuild will contain one metafile
1008
    #
1009
    if ( -d $SRC_ROOT )
1010
    {
1011
        File::Find::find( \&pkgFindTarBall, $SRC_ROOT );
1012
 
1013
        if (@bad_symlinks)
1014
        {
1015
            my $msg = "Bad Symlinks: " . scalar @bad_symlinks;
1016
            $opt_test ? ReportError($msg, @bad_symlinks) : Warning($msg, @bad_symlinks);
1017
        }
1018
        ErrorDoExit();
1019
    }
1020
    else
1021
    {
1022
        Error("Failed to find dir [$SRC_ROOT]",
1023
              "Check JATS config.");
1024
    }
1025
 
1026
    #
1027
    #   Create the MD5 info file
5582 dpurdie 1028
    #   Mark it as a temp file as it not being created locally
5527 dpurdie 1029
    #   
1030
    writeFileInfo($SRC_ROOT);
5582 dpurdie 1031
    push @tmpFiles, $opt_outfile;
5527 dpurdie 1032
 
1033
    #
1034
    #   Create the target path in the target archive
1035
    #
1036
    LogFileOp ('Creating', $DPKG_DIR);
1037
    mkpath($DPKG_DIR, 0, 0775);
1038
 
1039
    #
5578 dpurdie 1040
    #   Create a tar.gz file an transfer to the final location
5527 dpurdie 1041
    #
5578 dpurdie 1042
    #   Have failed to find a nice windows utility to create a tar.gz
1043
    #       bsdtar - crashed under server 2003
1044
    #       cygwin - design decision. JATS will not include cygwin
1045
    #
5527 dpurdie 1046
    my $tarTarget = join('_', $DPKG_NAME, $DPKG_VERSION, $GBE_HOSTNAME) . '.tar.gz';
1047
    my $tarPath = catfile($DPKG_DIR, $tarTarget);
7547 dpurdie 1048
    createTar($tarPath);
5578 dpurdie 1049
}
1050
 
1051
#-------------------------------------------------------------------------------
7547 dpurdie 1052
# Function        : createTar  
5578 dpurdie 1053
#
7547 dpurdie 1054
# Description     : Create a tar.gz file under unix and windows
5578 dpurdie 1055
#
1056
# Inputs          : $tarPath    - Place tarfile here 
1057
#                   $SRC_ROOT   - Directory to tar
1058
#
1059
# Returns         : Will not return on error
1060
#
7547 dpurdie 1061
sub createTar
5578 dpurdie 1062
{
1063
    my ($tarPath) = @_;
1064
 
1065
    #
1066
    #   Locate the tar utility
1067
    #   Use gtar if its available otherwise use tar
1068
    #   
1069
    my $tarProg = LocateProgInPath('gtar', '--All');
1070
    $tarProg = LocateProgInPath('tar', '--All') unless $tarProg;
1071
    Error ("Tar utility not found in path") unless $tarProg;
1072
 
1073
    #
1074
    #   Tar and gzip file directly into the final location
5527 dpurdie 1075
    LogFileOp ('TarZip', $tarPath);
1076
    my @tarArgs;
1077
    push @tarArgs, '-v' if IsVerbose(1);
7547 dpurdie 1078
    my $rv = System ('--NoShell', '--NoExit',$tarProg, @tarArgs, '--force-local', '-czf', $tarPath, '-C', $SRC_ROOT, '.' );
5527 dpurdie 1079
    if ($rv)
1080
    {
5578 dpurdie 1081
        push @tmpFiles, $tarPath;
5527 dpurdie 1082
        Error ('Cannot create tarball', "Path:$tarPath");
1083
    }
5578 dpurdie 1084
}
5527 dpurdie 1085
 
5578 dpurdie 1086
#-------------------------------------------------------------------------------
5527 dpurdie 1087
# Function        : pkgFindTarBall 
1088
#
1089
# Description     : Used by CreateDpkgArchiveTarBall
1090
#                   File::Find processing function
1091
#                   
1092
#                   This routine is called for each file and directory within the package
1093
#
1094
# Inputs          : As per File::Find 
1095
#
1096
# Returns         : Nothing
1097
#
1098
sub pkgFindTarBall
1099
{
1100
    my $item = $File::Find::name;
1101
    my $base = File::Basename::basename($item);
1102
    my $type;
1103
 
1104
    #
1105
    #   Ignore the top level directory
1106
    #
6294 dpurdie 1107
    return if ( $item eq $SRC_ROOT );
5527 dpurdie 1108
 
1109
    #
1110
    #   Determine type of this item
1111
    #       file
1112
    #       link, badlink
1113
    #       dir
1114
    if ( -l $item)
1115
    {
6294 dpurdie 1116
        my $niceBase = substr ( $item, 1+length ($SRC_ROOT) );
6276 dpurdie 1117
        if (TestSymlink( $niceBase,$_)) {
1118
            #
1119
            # Broken/Bad symlink
5527 dpurdie 1120
            #   Remove it from the 'pkg'
1121
            #   Could try other ways of excluding it from the tar, but this is the simplest  
6276 dpurdie 1122
            LogFileOp("Bad SymLink", $item);
5527 dpurdie 1123
            $type = 'badlink';
1124
            unlink $item;
1125
            return;
6276 dpurdie 1126
 
1127
        } else {
1128
            $type = 'link';
5527 dpurdie 1129
        }
1130
    } elsif ( -f $item) {
1131
        $type = 'file';
1132
    } elsif ( -d $item) {
1133
        $type = 'dir';
1134
    } else {
1135
        Error("Unknown file type. Cannot be packaged");
1136
    }
1137
 
7547 dpurdie 1138
    unless ($opt_tarzip) {
1139
        #
1140
        #   Calculate the target directory name
1141
        #
1142
        my $target = $item;
1143
        $target = $DPKG_DIR . substr ( $item, length ($SRC_ROOT) );
1144
 
1145
        addFile($type, $item, $target);
1146
        LogFileOp('Process',$item);
1147
    }
1148
}
1149
 
1150
#-------------------------------------------------------------------------------
1151
# Function        : CreateLocalTarZip 
1152
#
1153
# Description     : Similar to CreateDpkgArchive, but it will create a tarzip file of the package
1154
#                   in the current directory.
1155
#                   
1156
#                   Can be used by developers to tar-zip a package for cross platform development
1157
#
1158
# Inputs          : 
1159
#
1160
# Returns         : 
1161
#
1162
sub CreateLocalTarZip
1163
{
1164
    Information("");
1165
    InitFileUtils();
1166
 
1167
 
5527 dpurdie 1168
    #
7547 dpurdie 1169
    #   If a 'noBuild' then create a dummy package directory simply
1170
    #   to contain the metadata. 
1171
    #   Delete any existing dir of the same name to ensure its clean.
5527 dpurdie 1172
    #
7547 dpurdie 1173
    if ( $SRC_ROOT eq '' )
1174
    {
1175
        $SRC_ROOT = catdir(AbsPath('pkg/noBuild'));
1176
        RmDirTree ($SRC_ROOT);
1177
        mkpath($SRC_ROOT, 0, 0775);
1178
    }
5527 dpurdie 1179
 
7547 dpurdie 1180
    #
1181
    #   Mark the archive with the build machine to indicate which parts of
1182
    #   a multi-machine build have been performed
1183
    #
1184
    createBuiltFile($SRC_ROOT);
1185
 
1186
    #   Process the source directory
1187
    #   A NoBuild will contain one metafile
1188
    #
1189
    if ( -d $SRC_ROOT )
1190
    {
1191
        File::Find::find( \&pkgFindTarBall, $SRC_ROOT );
1192
 
1193
        if (@bad_symlinks)
1194
        {
1195
            my $msg = "Bad Symlinks: " . scalar @bad_symlinks;
1196
            $opt_test ? ReportError($msg, @bad_symlinks) : Warning($msg, @bad_symlinks);
1197
        }
1198
        ErrorDoExit();
1199
    }
1200
    else
1201
    {
1202
        Error("Failed to find dir [$SRC_ROOT]",
1203
              "Check JATS config.");
1204
    }
1205
 
1206
    #
1207
    #   Create a tar.gz file an transfer to the final location
1208
    #
1209
    #   Have failed to find a nice windows utility to create a tar.gz
1210
    #       bsdtar - crashed under server 2003
1211
    #       cygwin - design decision. JATS will not include cygwin
1212
    #
1213
    my $tarTarget = $DPKG_NAME . '__' . $DPKG_VERSION . '.tgz';
1214
    createTarZip($tarTarget);
5527 dpurdie 1215
}
1216
 
1217
#-------------------------------------------------------------------------------
7547 dpurdie 1218
# Function        : createTarZip  
1219
#
1220
# Description     : Create a .tgz file with correct package formatting
1221
#
1222
# Inputs          : $tarPath    - Place tarfile here 
1223
#                   $SRC_ROOT   - Directory to tar
1224
#
1225
# Returns         : Will not return on error
1226
#
1227
sub createTarZip
1228
{
1229
    my ($tarPath) = @_;
1230
 
1231
    #
1232
    #   Locate the tar utility
1233
    #   Use gtar if its available otherwise use tar
1234
    #   
1235
    my $tarProg = LocateProgInPath('gtar', '--All');
1236
    $tarProg = LocateProgInPath('tar', '--All') unless $tarProg;
1237
    Error ("Tar utility not found in path") unless $tarProg;
1238
 
1239
    #
1240
    #   Tar and gzip file directly into the final location
1241
    LogFileOp ('TarZip', $tarPath);
1242
    my @tarArgs;
1243
    push @tarArgs, '-v' if IsVerbose(1);
1244
    my $rv = System ('--NoShell', '--NoExit',$tarProg, @tarArgs, '-czf', $tarPath, '-C', $SRC_ROOT, '.' , '--transform', "s~^./~$DPKG_NAME/$DPKG_VERSION/~");
1245
    if ($rv) {
1246
        push @tmpFiles, $tarPath;
1247
        Error ('Cannot create tarball', "Path:$tarPath");
1248
    }
1249
}
1250
 
1251
#-------------------------------------------------------------------------------
227 dpurdie 1252
# Function        : TestDpkgArchive
1253
#
1254
# Description     : Test the structure of the source achive
1255
#                   Ensure that it has some files
1256
#                   Warn if files are present in the root directory
1257
#
1258
# Inputs          : None
1259
#
1260
# Returns         : Warnings
1261
#
1262
my $test_dir_count = 0;
1263
my $test_file_count = 0;
1264
my @test_root_file = ();
1265
sub TestDpkgArchive
1266
{
1267
    $test_dir_count = 0;
1268
    $test_file_count = 0;
1269
    @test_root_file = ();
1270
 
4003 dpurdie 1271
    if ( $SRC_ROOT ne '' )
1272
    {
1273
        Error("Failed to find dir [$SRC_ROOT]",
1274
              "Check JATS config.") unless ( -d $SRC_ROOT );
1275
 
1276
 
1277
        #
1278
        #   Scan the package counting files and folders
1279
        #
1280
        File::Find::find( \&pkgFind3, $SRC_ROOT );
1281
    }
1282
 
227 dpurdie 1283
    Information ("Package contains:",
1284
                 "Files: $test_file_count",
1285
                 "Dirs: $test_dir_count",
1286
                 );
1287
    #
1288
    #   There shouldn't be any files in the root directory
1289
    #   other than the descpkg and incpkg.
1290
    #
1291
    Warning ("Unexpected files in package root:", @test_root_file)
1292
        if ( @test_root_file  );
4424 dpurdie 1293
 
1294
    Error ("Bad symbolic links found:", @bad_symlinks)
1295
            if ( @bad_symlinks );
1296
 
227 dpurdie 1297
}
1298
 
1299
sub pkgFind3
1300
{
1301
 
1302
    #
1303
    #   Calculate the target directory name
1304
    #
1305
    my $target = $File::Find::dir;
263 dpurdie 1306
    $target = substr ( $target, length ($SRC_ROOT) );
1307
    $target =~ s~^.~~;
227 dpurdie 1308
 
1309
    if ( -d $_ ) {
1310
        $test_dir_count++;
1311
    } else {
1312
        $test_file_count++;
1313
        unless ( $target )
1314
        {
241 dpurdie 1315
            #
1316
            #   Locate files in the package root directory that
1317
            #   are not expected to be there.
1318
            #
6276 dpurdie 1319
            unless ((  $_ eq 'descpkg' ) || ( $_ eq 'incpkg' ) || $_ =~ m~built\.*~)
263 dpurdie 1320
            {
1321
                push @test_root_file, $_;
1322
            }
227 dpurdie 1323
        }
6276 dpurdie 1324
        TestSymlink($target, $_);
1325
    }
1326
}
1327
 
1328
#-------------------------------------------------------------------------------
1329
# Function        : TestSymlink 
1330
#
1331
# Description     : Test a symlink for validity
1332
#                   Current drectory will be $dir
1333
#
1334
# Inputs          : $dir
1335
#                   $fname
1336
#
1337
# Returns         : True if a bad symlink
1338
#                   Add error messages to @bad_symlinks 
1339
#
1340
sub TestSymlink 
1341
{
1342
    my ($dir, $fname) = @_;
1343
    my $msg;
1344
    my $rv = 0;
1345
    my $file = join ('/', $dir, $fname) ;
1346
#Debug0("Testing: $file");
1347
 
1348
    if (-l $fname) {
1349
        my $link = readlink $fname;
1350
 
1351
        if ($link =~ m~^/~) {
1352
            $msg = '[Absolute link not allowed]:' . $file . ' -> ' . $link;
1353
 
1354
        #   Need to test if the symlink escapes the package
1355
        #        } elsif ($link =~ m~/~) {
1356
        #            push @bad_symlinks, '[Symlink out of directory]:' . $file . ' -> ' . $link;
1357
        } elsif (! -f $fname) {
1358
            $msg =  '[Broken Link]:' . $file . ' -> ' . $link;
1359
        } else {
1360
            my @cleanParts = split ('/', CleanPath($link));
1361
            my @dirParts = split('/', $dir);
1362
            my $upCount = 0;
1363
            foreach ( @cleanParts ) {
1364
                if ($_ eq '..') {
1365
                    $upCount++;
1366
                } else {
1367
                    last;
1368
                }
1369
            }
1370
 
1371
            if ($upCount > scalar @dirParts) {
1372
                $msg =  '[Escapes Package]:' . $file . ' -> ' . $link;
1373
            }
4424 dpurdie 1374
        }
227 dpurdie 1375
    }
6276 dpurdie 1376
    if ($msg) {
1377
        push @bad_symlinks, $msg;
1378
        $rv = 1;
1379
    }
1380
 
1381
    return $rv;
1382
 
227 dpurdie 1383
}
1384
 
6276 dpurdie 1385
 
5527 dpurdie 1386
#-------------------------------------------------------------------------------
1387
# Function        : createBuiltFile 
1388
#
1389
# Description     : Create the packages built.xxxx file
1390
#                   Used to track which build machines have contributed to the build 
1391
#
1392
# Inputs          : $targetDir          - Base directory for the file 
1393
#
1394
# Returns         : Full pathname of the generatde file
1395
#
1396
sub createBuiltFile
1397
{
1398
    my ($targetDir) = @_;
1399
 
1400
    #
1401
    #   Mark the archive with the build machine to indicate which parts of
1402
    #   a multi-machine build have been performed
1403
    #
1404
    my $touchfile = catfile( $targetDir, $opt_generic ? 'built.generic' : "built.$GBE_HOSTNAME");
1405
 
1406
    #
1407
    #   Create a string to be appended to the 'built' file
1408
    #   Comma seperated list of (possibly) useful info
1409
    #       Date-Time ( Local and GMT)
1410
    #       machine type, machine name and the user
1411
    #       GBE_ABT value
1412
    #
1413
    #   Having build issues where the file is not seen for a very long time
1414
    #
1415
    my $built_info = localtime() ."," . gmtime() . " GMT,$GBE_MACHTYPE,$GBE_HOSTNAME,$USER,$GBE_ABT";
1416
    LogFileOp("Mark File",$touchfile);
1417
    FileAppend ( $touchfile, $built_info );
1418
 
1419
    return $touchfile;
1420
}
1421
 
5578 dpurdie 1422
#-------------------------------------------------------------------------------
1423
# Function        : END Block 
1424
#
1425
# Description     : Post execution cleanup
1426
#
1427
# Inputs          : 
1428
#
1429
# Returns         : 
1430
#
1431
END
1432
{
1433
    #
1434
    #   Save the programs exit code
1435
    #   This END block may use the 'system' call and this will clobber the value in $?
1436
    #   which is the systems exit code
1437
    #
1438
    Verbose2("Cleanup processing");
1439
    local $?;
5527 dpurdie 1440
 
5578 dpurdie 1441
    #
1442
    #   Delete temp files
1443
    #
1444
    foreach my $tmpFile ( @tmpFiles)
1445
    {
1446
        if ($opt_keepTemp)
1447
        {
1448
            Information("Retain Temp file: " . $tmpFile);
1449
        }
1450
        else
1451
        {
1452
            Verbose ("Delete file: " . $tmpFile);
1453
            RmDirTree ($tmpFile) && Warning("$tmpFile not deleted");
1454
        }
1455
    }
1456
 
1457
}
1458
 
1459
 
227 dpurdie 1460
# ---------------------------------------------------------
1461
# ---------------------------------------------------------
1462
# Main
1463
# ---------------------------------------------------------
1464
# ---------------------------------------------------------
1465
 
1466
 
1467
# Initialise our world
1468
#
1469
Init();
4003 dpurdie 1470
CheckDescPkg();
1471
ShowInfo();
4549 dpurdie 1472
unless ($opt_info)
227 dpurdie 1473
{
4549 dpurdie 1474
    unless ( $opt_test )
227 dpurdie 1475
    {
7547 dpurdie 1476
        if ($opt_tarzip) {
1477
            LogFileOp("Creating package tarZip:", $DPKG_DIR);
1478
            CreateLocalTarZip();
1479
 
1480
        } elsif ($opt_tarmode) {
6177 dpurdie 1481
            LogFileOp("Creating dpkg_archive tarball:", $DPKG_DIR);
5527 dpurdie 1482
            CreateDpkgArchiveTarBall();
1483
        }
1484
        else
1485
        {
6177 dpurdie 1486
            LogFileOp("Creating dpkg_archive package:", $DPKG_DIR);
5527 dpurdie 1487
            CreateDpkgArchive();
1488
        }
227 dpurdie 1489
    }
4549 dpurdie 1490
    else
1491
    {
1492
        Information("Testing user package.");
1493
        TestDpkgArchive();
1494
    }
227 dpurdie 1495
}
1496
 
1497
# Done
1498
#
1499
Information ("Done.");
1500
exit 0;
1501
 
1502
 
1503
#-------------------------------------------------------------------------------
1504
#   Documentation
1505
#
1506
 
1507
=pod
1508
 
361 dpurdie 1509
=for htmltoc    SYSUTIL::
1510
 
227 dpurdie 1511
=head1 NAME
1512
 
1513
create_dpkg - Create a dpkg_archive entry
1514
 
1515
=head1 SYNOPSIS
1516
 
1517
 jats create_dpkg [options]
1518
 
1519
 Options:
1520
    -help              - Brief help message
1521
    -help -help        - Detailed help message
1522
    -man               - Full documentation
1523
    -quiet             - Suppress progress messages, then warning messages
1524
    -verbose           - Display additional progress messages
4549 dpurdie 1525
    -override          - Deprecated option
1526
    -delete            - Delete any previous version of the package
263 dpurdie 1527
    -[no]merge         - merge with existing version of the package
6276 dpurdie 1528
    -archive=name      - Specify archive (cache, local, main, store, sandbox, deploy, escrow)
227 dpurdie 1529
    -pname=name        - Ensure package is named correctly
1530
    -pversion=version  - Ensure package version is correct
1531
    -generic           - Create a built.generic file
5550 dpurdie 1532
    -noBuild           - Create dummy build files
5527 dpurdie 1533
    -[no]tarmode       - Transfer package as tarball
4549 dpurdie 1534
    -[no]md5           - Use MD5 comparison of merged files(enabled)
7547 dpurdie 1535
    -tarzip            - Create a tarzip of the package.
5527 dpurdie 1536
 
1537
  Debug and Testing:
263 dpurdie 1538
    -[no]test          - Test package. Do not transfer.
4549 dpurdie 1539
    -[no]info          - Display packaging info. Do not transfer.
7547 dpurdie 1540
    -[no]testArchive   - Perform operations within a test/dev archive
5578 dpurdie 1541
    -keepTemp          - Do not delete temp files
227 dpurdie 1542
 
1543
=head1 OPTIONS
1544
 
1545
=over 8
1546
 
1547
=item B<-help>
1548
 
1549
Print a brief help message and exits.
1550
 
1551
=item B<-help -help>
1552
 
1553
Print a detailed help message with an explanation for each option.
1554
 
1555
=item B<-man>
1556
 
1557
Prints the manual page and exits.
1558
 
1559
=item B<-quiet>
1560
 
1561
This option will suppress almost all of the progress messages, except for a single
1562
copy message. It is intended to be used when the program is called from another
1563
script.
1564
 
1565
=item B<-override>
1566
 
4549 dpurdie 1567
If this option has been deprecated. It has no effect.
1568
 
1569
It is still present to preserve backward compatability with the automated 
1570
build system.
1571
 
1572
=item B<-delete>
1573
 
227 dpurdie 1574
If this option is enabled then any previous version of the target package will
4549 dpurdie 1575
be deleted.
227 dpurdie 1576
 
1577
=item B<-merge>
1578
 
1579
If this option is enabled then the package will be merged with any existing
4549 dpurdie 1580
package. This option is used by the auto build tool to assemble multi-machine 
1581
packages in dpkg_archive.
227 dpurdie 1582
 
1583
=item B<-archive=name>
1584
 
1585
This option specifies the destination archive to be used. The following names
1586
are supported:
1587
 
361 dpurdie 1588
=over 8
1589
 
1590
=item cache
1591
 
1592
The location of the target archive will be taken from C<GBE_DPKG_CACHE>.
1593
 
1594
=item local
1595
 
1596
The location of the target archive will be taken from C<GBE_DPKG_LOCAL>.
1597
 
1598
=item main (default)
1599
 
1600
The location of the target archive will be taken from C<GBE_DPKG>. This is the
1601
default target archive.
1602
 
1603
=item store
1604
 
1605
The location of the target archive will be taken from C<GBE_DPKG_STORE>.
1606
 
4688 dpurdie 1607
=item replica
1608
 
1609
The location of the target archive will be taken from C<GBE_DPKG_REPLICA>.
1610
 
361 dpurdie 1611
=item sandbox
1612
 
1613
The location of the target archive will be taken from C<GBE_DPKG_SBOX>.
1614
 
1615
=item deploy
1616
 
6276 dpurdie 1617
The location of the target archive will be taken from C<GBE_DPLY>.
361 dpurdie 1618
 
6276 dpurdie 1619
Note: This archive is no longer fully supported.
1620
 
1621
=item escrow
1622
 
1623
The location of the target archive will be taken from C<GBE_DPKG_ESCROW>.
1624
 
1625
This is the default target archive if an escrow build is detected.
1626
 
361 dpurdie 1627
=back
1628
 
227 dpurdie 1629
=item B<-pname=name>
1630
 
1631
If this option is provided, the utility will ensure that the package is named
1632
correctly.
1633
 
1634
=item B<-pversion=version>
1635
 
1636
If this option is provided, the utility will ensure that the package version is
1637
that expected.
1638
 
4549 dpurdie 1639
=item B<-generic>
227 dpurdie 1640
 
4549 dpurdie 1641
This option will create a built.generic file, instead of one based on the machine
1642
that actually built the package. This is used by the AutoBuilder toolchain.
227 dpurdie 1643
 
5550 dpurdie 1644
=item B<-noBuild>
1645
 
1646
This option is only used by the build daemons. It is used to create all required 
1647
files to indicate that the build has occured correctly. It will only be used by ANT 
1648
based builds as true JATS builds handle this situation internally.
1649
 
5527 dpurdie 1650
=item B<-[no]tarmode>
1651
 
1652
This option will cause the package to be transferred into the package archive as
1653
a tar ball. Used in the build system to address two issues:
1654
 
1655
=over 4
1656
 
1657
=item 1 
1658
 
1659
Slow speed in copying lots of files from the build machine to the package server.
1660
 
1661
=item 2 
1662
 
1663
Need for cross platform file lock in order to prevent copy collisions.  
1664
 
1665
=back
1666
 
263 dpurdie 1667
=item B<-[no]md5>
1668
 
1669
If package builds are being merged then a validity check is performed using
1670
an MD5 digest over the current and the existing file.
1671
 
1672
By default, it is an error for the user file to differ from the merged file.
1673
 
1674
This option disabled the error. The test is still done and the results are
1675
reported.
1676
 
4549 dpurdie 1677
=item B<-test>
227 dpurdie 1678
 
4549 dpurdie 1679
If this option is enabled the utility will perform initial sanity testing, but
1680
it will not perform the copy.
227 dpurdie 1681
 
4549 dpurdie 1682
=item B<-[no]info>
1683
 
1684
This option will cause the program to display information about the packaging 
1685
process and then exit. 
1686
 
1687
No data will be transferred to the archive.
1688
 
5527 dpurdie 1689
=item B<-[no]testArchive>
1690
 
1691
If this option is enabled then the assembly operation is performed within a test area within
1692
the currently configured dpkg_archive. The test area is a subdirectory 
1693
called C<.dpkg_archive/test_dpkg>
1694
 
1695
This option is intended for testing use only.
1696
 
7547 dpurdie 1697
This option allows a package created with a developers sandbox to be packaged for testing purposes.
1698
 
5578 dpurdie 1699
=item B<-keepTemp>
1700
 
1701
This option will prevent temp files, created by this utilty, from being deleted when the utilty
1702
exists.
1703
 
1704
This option is intended for testing use only.
1705
 
7547 dpurdie 1706
=item B<-tarzip>
1707
 
1708
This option will cause the utility to generate a zipped tarfile of the current package. 
1709
It is intended to allow developers to transfer package artifacts from one machine to another,
1710
perhaps during development in a multi platform environment.
1711
 
1712
This option does not create a package in dpkg_archive. It will create the resultant file in the current
1713
directory. The name of the file will be <packageName>__<version>.tgz
1714
 
1715
Several other options have no effect when used with this option.
1716
 
227 dpurdie 1717
=back
1718
 
1719
=head1 DESCRIPTION
1720
 
1721
This utility program is used to transfer a package that has been built into
1722
dpkg_archive. The package is then available for general consumption.
1723
 
4549 dpurdie 1724
The utility will perform several operations in the transfer process. These incude:
1725
 
1726
=over 4
1727
 
1728
=item * 
1729
 
1730
Create a tag file to indicate the machine that has performed the transfer
1731
 
1732
=item * 
1733
 
1734
Create an XML file of files that have been transferred. This file contains information
1735
used by the build system when it releases the package, including: name, size and MD5SUM.
1736
 
1737
=item *
1738
 
1739
Detect file conflicts when different builds are merged into a single package. Header files are 
1740
allowed to differ in line ending style, but other files must not conflict. The package will not be 
1741
reproducible if file conflicts exist.
1742
 
5527 dpurdie 1743
In 'tarmode' the package merging needs to be done by another utility.
1744
 
4549 dpurdie 1745
=item *
1746
 
1747
Suppress dead symbolic links. A valid symlink will be preserved, but invalid links will be 
1748
removed from the transferred image.
1749
 
1750
=back
1751
 
227 dpurdie 1752
=head2 PACKAGE LOCATION
1753
 
1754
The utility will locate a package by examining the following directores for
1755
the package description file(descpkg).
1756
 
1757
=over 8
1758
 
1759
=item ./build/deploy
1760
 
1761
This format is generated by the deployment builds. The default target archive
1762
will be taken from the environment variable GBE_DPLY.
1763
 
1764
=item ./pkg
1765
 
1766
This format is generated by JATS builds.
1767
 
1768
=item ./build/pkg
1769
 
1770
This format is generated by ANT builds.
1771
 
5527 dpurdie 1772
=item ./pkg/noBuild
1773
 
1774
This format is used internally by this utilty. It will be generated by the build system 
1775
in cases where there is no build to be performs on the current machine.
1776
 
227 dpurdie 1777
=back
1778
 
1779
The program should be run in the same directory as the build control files as
1780
the package subdirectory will be created in that directory.
1781
 
1782
=head1 EXAMPLE
1783
 
1784
=head2 jats create_dpkg
1785
 
1786
This will locate a generated package and install it into the dpkg_archive repository.
1787
 
1788
=cut
1789