Subversion Repositories DevTools

Rev

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

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