Subversion Repositories DevTools

Rev

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