Subversion Repositories DevTools

Rev

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