Subversion Repositories DevTools

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
263 dpurdie 1
########################################################################
4549 dpurdie 2
# Copyright (c) VIX TECHNOLOGY (AUST) LTD
227 dpurdie 3
#
4549 dpurdie 4
# Module name   : create_dpkg2.pl
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.
4549 dpurdie 10
#                 Based on create_dpkg with following changes
11
#                   * No user interaction
12
#                   * Generates files list for ReleaseNote integration 
227 dpurdie 13
#
4549 dpurdie 14
# Usage:        : See POD
227 dpurdie 15
#
263 dpurdie 16
#......................................................................#
227 dpurdie 17
 
4549 dpurdie 18
 
263 dpurdie 19
require 5.008_002;
227 dpurdie 20
 
21
# Include Standard Perl Functions
22
#
23
use strict;
263 dpurdie 24
use warnings;
227 dpurdie 25
use Cwd;
26
use Getopt::Long;
27
use File::Basename;
28
use File::Find;
29
use File::Path;
30
use File::Copy;
31
use Pod::Usage;
263 dpurdie 32
use Digest::MD5;
4549 dpurdie 33
use XML::Simple;
4633 dpurdie 34
use Encode qw(decode encode);
227 dpurdie 35
 
36
use JatsError;
263 dpurdie 37
use JatsEnv;
227 dpurdie 38
use DescPkg;
39
use FileUtils;
40
 
363 dpurdie 41
#
42
#   Under Windows we need the Win32::FileSecurity module
43
#   It only exists under windows
44
#
45
my $Win32 = eval "require Win32::FileSecurity";
46
 
227 dpurdie 47
# define Global variables
48
#
4549 dpurdie 49
my $VERSION = "3.0.0";
227 dpurdie 50
my $PROGNAME = "create_dpkg.pl";
51
 
279 dpurdie 52
# Globals imported from environment
53
#
54
our $GBE_MACHTYPE;
55
our $GBE_HOSTNAME;
56
our $USER;
57
our $GBE_ABT;
227 dpurdie 58
 
279 dpurdie 59
# Global variables
60
#
227 dpurdie 61
my $DPKG_NAME     = "";
62
my $DESC_NAME     = "";
63
my $DPKG_VERSION  = "";
64
my $DESCPKG_FILE  = "";
65
my $DESCPKG_TYPE  = "";
66
my $CWD_DIR       = cwd;
67
my $SRC_ROOT;
68
my $DPKG_DIR;
69
my $DPKG_ROOT;
4003 dpurdie 70
my $PKG_BASE;
263 dpurdie 71
my $bad_merge_count = 0;
4424 dpurdie 72
my @bad_symlinks;
4549 dpurdie 73
my @fileList;
74
my $descPkgCount = 0;
227 dpurdie 75
 
76
#
77
#   Option variables
78
#
79
my $opt_help = 0;
80
my $opt_manual = 0;
81
my $opt_verbose = 0;
82
my $opt_quiet = 0;
4549 dpurdie 83
my $opt_delete = 0;
227 dpurdie 84
my $opt_override = 0;
85
my $opt_merge = 0;
86
my $opt_archive;
87
my $opt_generic;
88
my $opt_pname;
89
my $opt_pversion;
90
my $opt_test;
263 dpurdie 91
my $opt_md5 = 1;
4549 dpurdie 92
my $opt_outfile;
93
my $opt_info;
227 dpurdie 94
 
95
 
96
#
97
#   Structure to translate -archive=xxx option to archive variable
98
#   These are the various dpkg_archives known to JATS
99
#
4688 dpurdie 100
my %Archive2Var =( 'main'      => 'GBE_DPKG',
101
                   'store'     => 'GBE_DPKG_STORE',
102
                   'cache'     => 'GBE_DPKG_CACHE',
103
                   'local'     => 'GBE_DPKG_LOCAL',
104
                   'sandbox'   => 'GBE_DPKG_SBOX',
105
                   'deploy'    => 'GBE_DPLY',
106
                   'replica'   => 'GBE_DPKG_REPLICA',
227 dpurdie 107
                   );
108
 
109
#------------------------------------------------------------------------------
110
#------------------------------------------------------------------------------
111
# Subroutines
112
#------------------------------------------------------------------------------
113
#------------------------------------------------------------------------------
114
 
115
#------------------------------------------------------------------------------
116
sub LogFileOp
117
#
118
# Description:
119
#       This sub-routine is used to generate a consistent informational log
120
#------------------------------------------------------------------------------
121
{
122
    my ($opr, $file) = @_;
123
    $file =~ s/$DPKG_ROOT/DPKG/;
124
 
125
    Information (sprintf( "%-15s [%s]", $opr, $file));
126
}
127
 
4549 dpurdie 128
#-------------------------------------------------------------------------------
129
# Function        : addFile 
130
#
131
# Description     : Add a file to the list of transferred files
132
#
133
# Inputs          : $type           - File type
134
#                   $source         - Source file - full path
135
#                                     Use local copy, not network copy for file ops
136
#                   $target         - Target file name
137
#                   $md5sum         - Precalculated MD5 sum 
138
#
139
# Returns         : 
140
#
141
sub addFile
142
{
143
    my ($type, $source, $target, $md5sum) = @_;
144
    my %data;
145
 
146
    if ((not defined $md5sum) && ($type eq 'file'))
147
    {
148
        Verbose("Calculate MD5 Digest: $source");
149
        open(my $fh , $source) or Error ("Can't open '$source': $!");
150
        binmode $fh, ':crlf';
151
        $md5sum = Digest::MD5->new->addfile($fh)->hexdigest;
152
        close $fh;
153
    }
154
 
155
    $target =~ s~$DPKG_DIR~~;
156
    $target =~ s~^/~~;
157
    $target =~ s~/$~~;
158
 
4633 dpurdie 159
    #
160
    #   Convert from iso-8859-1 into utf-8
161
    #
162
    $target = decode( 'iso-8859-1', $target );
163
    $target = encode( 'utf-8', $target );
164
 
4549 dpurdie 165
    if ($type eq 'dir')
166
    {
167
        $data{path} = $target;
168
    }
169
    else
170
    {
171
        $data{path} = StripFileExt($target);
172
        $data{name} = StripDir($target);
173
        if ($type eq 'file')
174
        {
175
            $data{size} = (stat($source))[7];
176
            $data{md5sum} = $md5sum;
177
        }
178
    }
179
 
180
    $data{fullname} = $target;
181
    $data{type} = $type;
182
    $data{machtype} = $GBE_MACHTYPE;
183
    $data{host} = $GBE_HOSTNAME;
184
 
185
    # Put a nice '/' on the end of the patch elements
186
    $data{path} .= '/'
187
        if ( exists ($data{path}) && length($data{path}) > 0);
188
 
189
    push @fileList, \%data;
190
}
191
 
192
#-------------------------------------------------------------------------------
193
# Function        : writeFileInfo 
194
#
195
# Description     : Write out an XML file that contains this processes
196
#                   contribution to the output package 
197
#
198
# Inputs          : 
199
#
200
# Returns         : 
201
#
202
sub writeFileInfo
203
{
204
    my $data;
205
    $data->{file} = \@fileList;
206
 
207
    #
208
    #   Write out sections of XML
209
    #       Want control over the output order
210
    #       Use lots of attributes and only elements for arrays
211
    #       Save as one attribute per line - for readability
212
    #
213
    $opt_outfile = $opt_generic ? "$DPKG_DIR/built.files.generic.xml" : "$DPKG_DIR/built.files.$GBE_HOSTNAME.xml";
214
 
215
    LogFileOp ('Meta File', $opt_outfile);
216
    my $xs = XML::Simple->new( NoAttr =>0, AttrIndent => 1 );
217
 
218
    open (my $XML, '>', $opt_outfile) || Error ("Cannot create output file: $opt_outfile", $!);
219
    $xs->XMLout($data, 
220
                'RootName' => 'files', 
221
                'XMLDecl'  => '<?xml version="1.0" encoding="UTF-8"?>',
222
                'OutputFile' => $XML);
223
    close $XML;
224
 
225
}
226
 
227 dpurdie 227
#------------------------------------------------------------------------------
228
sub Init
229
#
230
# Description:
231
#     This function is used to process any command line arguements
232
#     and print the start banner.
233
#
234
#------------------------------------------------------------------------------
235
{
236
    # Process any command line arguements...
237
    my $result = GetOptions (
263 dpurdie 238
                "help:+"        => \$opt_help,              # flag, multiple use allowed
239
                "manual:3"      => \$opt_help,              # flag
240
                "verbose:+"     => \$opt_verbose,           # flag, multiple use allowed
4549 dpurdie 241
                "override!"     => \$opt_override,          # [no]flag (No longer used. Backward compat with build tool)
242
                "delete!"       => \$opt_delete,            # [no]flag
227 dpurdie 243
                "merge|m!"      => \$opt_merge,             # [no]flag.
244
                "archive=s"     => \$opt_archive,           # string
245
                "quiet+"        => \$opt_quiet,             # Flag
246
                "generic!"      => \$opt_generic,           # [no]Flag
247
                "pname=s"       => \$opt_pname,             # string
248
                "pversion=s"    => \$opt_pversion,          # string
249
                "test!"         => \$opt_test,              # [no]flag
263 dpurdie 250
                "md5!"          => \$opt_md5,               # [no]flag
4549 dpurdie 251
                "info!"         => \$opt_info,              # [no]flag
227 dpurdie 252
                );
253
 
254
 
255
    #
256
    #   Process help and manual options
257
    #
258
    pod2usage(-verbose => 0, -message => "Version: $VERSION")  if ($opt_help == 1  || ! $result);
259
    pod2usage(-verbose => 1)  if ($opt_help == 2 );
263 dpurdie 260
    pod2usage(-verbose => 2)  if ($opt_help > 2);
227 dpurdie 261
 
262
    #
263
    #   Init the error and message subsystem
264
    #
265
    ErrorConfig( 'name'    =>'CREATE_DPKG',
266
                 'verbose' => $opt_verbose,
267
                 'quiet'   => $opt_quiet );
268
 
269
    if ($opt_verbose)
270
    {
271
       Verbose ("Program: $PROGNAME");
272
       Verbose ("Version: $VERSION");
273
    }
274
 
275
    #
263 dpurdie 276
    #   Needed EnvVars
277
    #
278
    EnvImport ('GBE_MACHTYPE');
279 dpurdie 279
    EnvImport ('GBE_HOSTNAME');
280
    EnvImport ('USER' );
281
    EnvImportOptional ('GBE_ABT', "");
263 dpurdie 282
 
4003 dpurdie 283
    #
284
    #   Determine the target archive
285
    #   The default archive is GBE_DPKG, but this may be changed
286
    #
287
    $opt_archive = 'main' unless ( $opt_archive );
288
    my $archive_tag = $Archive2Var{$opt_archive};
289
    Error("Unknown archive specified: $opt_archive")
290
        unless ( $archive_tag );
291
    $DPKG_ROOT = $ENV{$archive_tag} || '';
292
    Verbose ("Archive Variable: $archive_tag" );
293
    Verbose2 ("Archive Path: $DPKG_ROOT" );
279 dpurdie 294
 
263 dpurdie 295
    #
4003 dpurdie 296
    #   Detect NoBuild marker
297
    #   This will bypass most of the operation of this package
298
    #
299
    if ( -f 'noBuild.gbe' )
300
    {
301
        Verbose ("No Build Marker file found");
302
        Error("Use of noBuild marker should only be done by a build daemon")
303
            unless ( $GBE_ABT );
304
 
305
        $SRC_ROOT = '';
306
        $DPKG_NAME = 'pkg';
307
        $DESCPKG_FILE = 'descpkg';
308
        $PKG_BASE =$CWD_DIR;
309
        return;
310
    }
311
 
312
    #
227 dpurdie 313
    #   Check for a "pkg" directory
314
    #   This may be in:
315
    #       1) The deploy directory (DEPLOY) build/deploy/descpkg
316
    #       2) The build directory (ANT)     build/pkg/descpkg
317
    #       3) The current directory (JATS)  pkg/xxxx/descpkg
318
    #
4003 dpurdie 319
    $PKG_BASE = "$CWD_DIR/build/deploy";
227 dpurdie 320
    Verbose2 ("Looking for descpkg: $PKG_BASE");
321
    if ( -f "$PKG_BASE/descpkg" )
322
    {
323
        #
324
        #   This is a deployment package.
325
        #   Force the use of the GBE_DPLY
326
        #
327
        $opt_archive = 'deploy' unless ( $opt_archive );
328
    }
329
    else
330
    {
331
        $PKG_BASE = "$CWD_DIR/build/pkg";
332
        Verbose ("Looking for descpkg: $PKG_BASE");
333
        if ( ! -f  "$PKG_BASE/descpkg" )
334
        {
335
            $PKG_BASE = "$CWD_DIR/pkg";
336
            Verbose ("Looking for descpkg: $PKG_BASE");
337
            Error("Failed to find a package to transfer. Looked in:",
338
                  "./build/deploy",
339
                  "./build/pkg",
340
                  "./pkg"
341
                  )
342
                unless( -d $PKG_BASE );
343
        }
344
    }
345
    Verbose("Package directory: $PKG_BASE");
346
 
347
    Error("Repository location not specified: $archive_tag")
348
        unless $DPKG_ROOT;
349
 
350
    Error("Failed to find Repository: $DPKG_ROOT")
351
        unless ( -d $DPKG_ROOT );
352
 
353
    #   Locate the package
354
    #   Packages are located by looking for a file called descpkg within the
355
    #   main package directory.
356
    #
357
    #   This installation process only handles one such file
358
    #
4549 dpurdie 359
    $descPkgCount = 0;
227 dpurdie 360
    File::Find::find( \&pkgFind, $PKG_BASE);
4549 dpurdie 361
 
362
    if ($descPkgCount > 1 )
363
    {
364
        Warning ("Package contains multiple ($descPkgCount) descpkg files");
365
    }
4003 dpurdie 366
}
227 dpurdie 367
 
4003 dpurdie 368
#-------------------------------------------------------------------------------
369
# Function        : CheckDescPkg
370
#
371
# Description     : Check the descpkg file
372
#
373
# Inputs          : Globals
374
#
375
# Returns         : Will not return on error
376
#
377
sub CheckDescPkg
378
{
227 dpurdie 379
    # Get the dpkg_archive version number we are  going to create.
380
    #
381
    Error("Descpkg file not found in package directory: $PKG_BASE")
382
        unless ( -f "$DESCPKG_FILE" );
383
 
384
    #
385
    #   Read in the package description and validate essential fields
386
    #
387
    GetDpkgArchiveVersion($DESCPKG_FILE);
388
    unless ( "$DPKG_VERSION" )
389
    {
390
        Error ("Incorrect descpkg content detected.",
391
               "Check JATS build.pl config.");
392
    }
393
 
394
    #
395
    #   Need to support two forms of pkg subdirectory
396
    #       1) packages are in a named subdir within 'pkg'
397
    #       2) package is within 'pkg' or 'deploy'
398
    #
399
    if ( $DPKG_NAME eq 'pkg' || $DPKG_NAME eq 'deploy' )
400
    {
401
        $DPKG_NAME = $DESC_NAME;
402
        unless ( $DESC_NAME )
403
        {
404
            Error ("Cannot determine package name",
405
                   "The packages 'descpkg' file is bad or missing");
406
        }
407
    }
408
    elsif ( $DESC_NAME ne $DPKG_NAME )
409
    {
410
        Error ("Package name MUST match package description",
411
               "Check build.pl and package.pl",
412
               "Package name: $DPKG_NAME",
413
               "Description : $DESC_NAME" );
414
    }
415
 
416
    #
417
    # lets just check to see if we have a version number before
418
    # we proceed.
419
    #
420
    unless ( $DPKG_VERSION )
421
    {
422
        Error("Cannot determine dpkg_archive version number.",
423
              "Check JATS build config.");
424
    }
425
 
426
    #
427
    #   Sanity test package name and version, if provided
428
    #
429
    if ( $opt_pname )
430
    {
431
        ReportError ("Package Name does not match expected name",
432
                     "Expected: '$opt_pname'",
433
                     "Descpkg : '$DPKG_NAME'") unless ( $DPKG_NAME eq $opt_pname );
434
    }
435
    if ( $opt_pversion )
436
    {
437
        ReportError ("Package Version does not match expected version",
438
                     "Expected: '$opt_pversion'",
439
                     "Descpkg : '$DPKG_VERSION'") unless ( $DPKG_VERSION eq $opt_pversion );
440
    }
441
    ErrorDoExit();
4003 dpurdie 442
}
227 dpurdie 443
 
4003 dpurdie 444
#-------------------------------------------------------------------------------
445
# Function        : ShowInfo
446
#
447
# Description     : Show info to the user
448
#
449
# Inputs          : 
450
#
451
# Returns         : 
452
#
453
sub ShowInfo
454
{
227 dpurdie 455
    #
456
    #   Set up the target directory path and name
457
    #   It will be created later
458
    #
459
    $DPKG_DIR = "$DPKG_ROOT/$DPKG_NAME/$DPKG_VERSION";
4003 dpurdie 460
 
227 dpurdie 461
    #
462
    #   Information for the user
463
    #
464
    Information ("---------------------------------------------------------------");
465
    Information ("Dpkg archive creation tool...");
466
    Information ("Version: $VERSION");
467
    Information ("");
468
    Information ("Information:");
469
    Information ("Working dir   = [$CWD_DIR]");
470
    Information ("Package Root  = [$SRC_ROOT]");
4549 dpurdie 471
    Information ("Repository    = [$DPKG_ROOT]");
472
    Information ("                *Non Standard archive") unless $opt_archive eq 'main';
227 dpurdie 473
    Information ("Target dir    = [$DPKG_DIR]");
474
    Information1("DPKG_NAME     = [$DPKG_NAME]");
475
    Information1("DPKG_VERSION  = [$DPKG_VERSION]");
476
    Information1("GBE_MACHTYPE  = [$GBE_MACHTYPE]");
279 dpurdie 477
    Information1("GBE_HOSTNAME  = [$GBE_HOSTNAME]");
478
    Information1("GBE_ABT       = [$GBE_ABT]");
479
    Information1("USER          = [$USER]");
4549 dpurdie 480
    Information ("")                                if ( $opt_merge || $opt_delete || $opt_info);
481
    Information ("Opt:Delete    = Enabled")         if ( $opt_delete );
227 dpurdie 482
    Information ("Opt:Merge     = Enabled")         if ( $opt_merge );
483
    Information ("Opt:TestMode  = Enabled. No Package Transferred") if ( $opt_test );
4549 dpurdie 484
    Information ("Opt:Info      = Enabled. No Package Transferred") if ( $opt_info );
369 dpurdie 485
    Warning     ("Sandbox Build = Yes") if ($ENV{GBE_DPKG_SBOX}) ;
227 dpurdie 486
    Information ("---------------------------------------------------------------");
487
 
369 dpurdie 488
    #
489
    #   If the environment variable GBE_DPKG_SBOX is defined then the package
490
    #   is being built within a development sandbox. In such a sandbox the
491
    #   version numbers of the packages are ignored. Publishing a package
492
    #   fromm such an environment is certainly not reproducible - so don't allow
493
    #   it to happen
494
    #
495
    #   Allow versions of 99.99.99 as these are known to be test versions
496
    #
497
    unless ( $opt_archive eq 'local' || $opt_archive eq 'sandbox' )
498
    {
499
        if ( $ENV{GBE_DPKG_SBOX} )
500
        {
501
            unless ( $DPKG_VERSION =~ /^99.99.99/ )
502
            {
503
                Error("Cannot not publish a package that has been generated",
504
                   "within a Sandbox as the version of dependent packages",
505
                   "is not guaranteed.",
506
                   "Only version 99.99.99 is allowed");
507
            }
508
        }
509
    }
4003 dpurdie 510
}
369 dpurdie 511
 
512
 
227 dpurdie 513
#------------------------------------------------------------------------------
514
sub pkgFind
515
#
516
# Description:
235 dpurdie 517
#     This subroutine is used to locate the FIRST descpkg file in
227 dpurdie 518
#     the local pkg dir.
519
#
520
#------------------------------------------------------------------------------
521
{
522
    my($item)= "$File::Find::name";
523
    my($file)= File::Basename::basename($item);
524
 
525
    # we get the absolute path from the find, but we only require
526
    # a relative path from the starting dir.
527
    # so our start dir.
528
 
529
    # we need to determine which file we are dealing with
235 dpurdie 530
    if ( ! -d $item && $file =~ /^descpkg$/ )
227 dpurdie 531
    {
4549 dpurdie 532
        $descPkgCount++;
533
 
235 dpurdie 534
        #
535
        #   Only grab the first one
536
        #
537
        if ( $DESCPKG_FILE )
538
        {
4549 dpurdie 539
            $item =~ s~$PKG_BASE/~~;
540
            Verbose ("Multiple descpkg files:", $item );
235 dpurdie 541
            return;
542
        }
543
 
227 dpurdie 544
        $DESCPKG_FILE = $item;
545
        my($dir)= File::Basename::dirname($item);
546
        $DPKG_NAME = File::Basename::basename($dir);
547
        $SRC_ROOT = $dir;
548
    }
549
}
550
 
551
 
552
#------------------------------------------------------------------------------
553
sub GetDpkgArchiveVersion
554
#
555
# Description:
556
#     This subroutine is used to determine the version of the dpkg_archive.
557
#     We assume that the version number is in the descpkg file.
558
#
559
#     Need to allow for two forms of descpkg. Some one decided that a Java
560
#     Manifest would be a good descpkg file - a long time after the rest of the
561
#     world had been using an existing file format.
562
#
563
#     Lines are tagged
564
#
565
#     Once the version number is determined we set the
566
#     global DPKG_VERSION variable.
567
#
568
#------------------------------------------------------------------------------
569
{
570
    my ($path) = @_;
571
    my $line;
572
    my $type;
573
 
574
    #
575
    #   Use a common routine to parse the package descriptor
576
    #   There are several forms that may need to be processed
577
    #
578
    my $pkg_data = ReadDescpkg( $path );
579
    Error("Failed to open file [$path].") unless $pkg_data;
580
 
581
    $DESC_NAME    = $pkg_data->{'NAME'};
582
    $DPKG_VERSION = $pkg_data->{'VERSION_FULL'};
583
}
584
 
585
#-------------------------------------------------------------------------------
586
# Function        : TransferDescpkg
587
#
588
# Description     : Copy and process the descpkg file to the target
589
#
590
# Inputs          :
591
#
592
# Returns         :
593
#
594
sub TransferDescpkg
595
{
596
    my $result = CopyDescpkg( @_ );
597
    Error("Transfer descpkg: $result") if ( $result );
598
}
599
 
600
#------------------------------------------------------------------------------
601
sub CreateDpkgArchive
602
#
603
# Description:
604
#     This subroutine is used to create the dpkg_archive in the $DPKG_ROOT
605
#     location 
606
#
4969 dpurdie 607
#     We use the global DPKG_ROOT, DPKG_DIR, DPKG_NAME, and DPKG_VERSION
227 dpurdie 608
#     to create the required directory structure.
609
#
610
#     If the dpkg_archive is new (ie not a new version) it is assumed the user
611
#     has access to create the top level dir for the new dpkg_archive.
612
#
613
#     The new dpkg_archive is created with the permission of the user 
614
#     executing this script.
615
#
616
#     If an error ocurs during the dpkg_archive creation the script
617
#     will terminate.
618
#
619
#------------------------------------------------------------------------------
620
{
263 dpurdie 621
    #
227 dpurdie 622
    # first we need to ensure we have the top level directory
623
    #
624
    if ( -d $DPKG_DIR )
625
    {
626
        Warning("Detected previous dpkg_archive [$DPKG_DIR]");
4549 dpurdie 627
        Error ("Package already exists and Package merging not selected")
628
            unless ( $opt_delete || $opt_merge );
263 dpurdie 629
 
630
        #
631
        #   Target exists
632
        #   Unless we are merging, we need to blow the entire tree away
633
        #
634
        unless ( $opt_merge )
635
        {
636
            LogFileOp("Remove Prev Pkg",$DPKG_DIR);
637
            rmtree($DPKG_DIR);
638
 
639
            #
640
            #   At this point the target directory 'should not' exist
641
            #   but it may. Some packges (like JATS) have Unix links within
642
            #   dpkg_archive filesystem. These cannot be deleted under windows
643
            #
644
            #   Not nice, but we live with it.
645
            #
646
            Warning ("Unable to delete previous instance of the package")
647
                if ( -d $DPKG_DIR );
648
        }
227 dpurdie 649
    }
650
    Information("");
651
 
652
    #
4969 dpurdie 653
    #   Create a file handle based in the Repository Root to allow
654
    #   locking while we create the package directory
655
    #   Note: Windows can't do a flock on a directory handle
656
    #         so we must create a file
657
    #
658
    my $dirLockfile = "$DPKG_ROOT/lockfile";
659
    Verbose ("Aquiring archive lock");
660
    open (ARCHIVE_LOCK, '>', $dirLockfile) || Error ("Cannot open archive root for locking: $!", $dirLockfile );
661
    flock ARCHIVE_LOCK, 2;
662
 
663
    #
227 dpurdie 664
    #   Create the top level directory
665
    #
666
    mkpath($DPKG_DIR, 0, 0775);
667
 
668
    #
4969 dpurdie 669
    #   Release the lock that we have in the target file system
670
    #   Create a lock on the package version directory
671
    #   This allows others to create new package versions
672
    #
673
    Verbose ("Releasing archive lock");
674
    close ARCHIVE_LOCK;
675
    unlink $dirLockfile;
676
 
677
    my $pkgLockfile = "$DPKG_DIR/lockfile";
678
    Verbose ("Aquiring packageVersion lock");
679
    open (ARCHIVE_LOCK, '>', $pkgLockfile) || Error ("Cannot open package directory for locking: $!", $pkgLockfile );
680
    flock ARCHIVE_LOCK, 2;
681
 
682
    #
4003 dpurdie 683
    #   Transfer source directory, unless this is a noBuild
684
    #
685
    if ( $SRC_ROOT ne '' )
227 dpurdie 686
    {
4003 dpurdie 687
        # Process the files
688
        #
689
        if ( -d $SRC_ROOT )
690
        {
691
            File::Find::find( \&pkgFind2, $SRC_ROOT );
263 dpurdie 692
 
4424 dpurdie 693
            if (@bad_symlinks)
694
            {
695
                my $msg = "Bad Symlinks: " . scalar @bad_symlinks;
696
                $opt_test ? ReportError($msg, @bad_symlinks) : Warning($msg, @bad_symlinks);
697
            }
698
 
4003 dpurdie 699
            if ( $bad_merge_count )
700
            {
701
                my $msg = "Merged files that differ: $bad_merge_count";
4424 dpurdie 702
                $opt_md5 ? ReportError($msg) : Warning($msg);
4003 dpurdie 703
            }
4424 dpurdie 704
            ErrorDoExit();
4003 dpurdie 705
        }
706
        else
263 dpurdie 707
        {
4003 dpurdie 708
            Error("Failed to find dir [$SRC_ROOT]",
709
                  "Check JATS config.");
263 dpurdie 710
        }
227 dpurdie 711
    }
712
 
713
    #
714
    #   Transfer of data is complete
4424 dpurdie 715
    #       Mark the archive with the build machine to indicate which parts of
716
    #       a multi-machine build have been performed
227 dpurdie 717
    #
345 dpurdie 718
    my $touchfile = $opt_generic ? "$DPKG_DIR/built.generic" : "$DPKG_DIR/built.$GBE_HOSTNAME";
279 dpurdie 719
 
227 dpurdie 720
    #
4003 dpurdie 721
    #   Create a string to be appended to the 'built' file
722
    #   Comma seperated list of (possibly) useful info
279 dpurdie 723
    #       Date-Time ( Local and GMT)
724
    #       machine type, machine name and the user
725
    #       GBE_ABT value
726
    #
727
    my $built_info = localtime() ."," . gmtime() . " GMT,$GBE_MACHTYPE,$GBE_HOSTNAME,$USER,$GBE_ABT";
227 dpurdie 728
    LogFileOp("Mark File",$touchfile);
279 dpurdie 729
    FileAppend ( $touchfile, $built_info );
4634 dpurdie 730
    addFile('file', $touchfile, $touchfile);
227 dpurdie 731
 
4634 dpurdie 732
 
227 dpurdie 733
    #
734
    #   If there is a .lnk file in the archive then remove it now that the
735
    #   archive has been transferred. The .lnk files are created in 'local'
736
    #   archives in order to simplify multi-package builds
737
    #
738
    my $link_file = "$DPKG_ROOT/$DPKG_NAME/$DPKG_VERSION.lnk";
739
    if ( -f $link_file )
740
    {
741
        LogFileOp("Removing Link",$link_file);
742
        unlink $link_file;
743
    }
744
 
4969 dpurdie 745
    #
746
    #   Release the file lock
747
    #
748
    Verbose ("Releasing packageVersion lock");
749
    close ARCHIVE_LOCK;
750
    unlink $pkgLockfile;
227 dpurdie 751
    return 1;
752
}
753
 
754
 
755
#------------------------------------------------------------------------------
756
sub pkgFind2
757
#
758
# Description:
759
#   This subroutine is used to locate all associated pkg files in
760
#   the local pkg dir.
761
#
762
#   This routine is called for each file and directory within the package
763
#   Some files and directories are treated in a special manner
764
#       - Top level directory is ignored
765
#
766
#
767
#
768
#------------------------------------------------------------------------------
769
{
770
    my $item = $File::Find::name;
771
    my $base = File::Basename::basename($item);
772
 
773
    #
774
    #   Calculate the target directory name
775
    #
776
    my $target = $item;
241 dpurdie 777
    $target = $DPKG_DIR . substr ( $item, length ($SRC_ROOT) );
227 dpurdie 778
 
779
    if ( -d $item )
780
    {
781
        #
782
        #   Ignore the top level directory
783
        #   It has already been created
784
        #
785
        return
786
            if ( $item eq $SRC_ROOT );
787
 
788
        #
789
        #   Directories are handled differently
790
        #       - Directories are created with nice permissions
263 dpurdie 791
        #       - If the directory already exists then it is being merged.
227 dpurdie 792
        #
793
        if ( ! -d "$target" )
794
        {
795
            LogFileOp("Creating Dir", $target);
796
            mkpath("$target", 0, 0775);
4549 dpurdie 797
            addFile('dir', $item , $target);
227 dpurdie 798
        }
799
    }
800
    else
801
    {
802
        #
803
        #   File copy
804
        #   If merging then do not overwrite an existing file
805
        #
806
        unless ( $opt_merge && -f $target )
807
        {
808
            if ( $item =~ m~/descpkg$~ )
809
            {
810
                LogFileOp("Rewrite File",$target);
4549 dpurdie 811
                TransferDescpkg( $item, $target );
363 dpurdie 812
                CORE::chmod oct("0664"), $target;
4549 dpurdie 813
                addFile('file', $item, $target);
227 dpurdie 814
            }
815
            else
816
            {
817
                #
818
                #   Copy file to destination
819
                #   If the file is a link, then duplicate the link contents
820
                #   Use: Unix libraries are created as two files:
821
                #        lib.xxxx.so -> libxxxx.so.vv.vv.vv
822
                #
823
                if ( -l $item )
824
                {
4424 dpurdie 825
                    if (-f $item)
227 dpurdie 826
                    {
4424 dpurdie 827
                        LogFileOp("Copying Link", $target);
828
                        my $link = readlink $item;
829
                        Verbose( "Link: $item, $link");
830
                        symlink ($link, $target );
831
                        unless ( $link && -l $target )
832
                        {
833
                            Error("Failed to copy link [$item] to [$target]: $!");
834
                        }
4549 dpurdie 835
                        addFile('link', $item , $target);
227 dpurdie 836
                    }
4424 dpurdie 837
                    else
838
                    {
839
                        # Don't copy broken Symlinks
840
                        # Perhaps this should be an error - but is will break escrow builds
841
                        #
842
                        LogFileOp("Broken SymLink", $target);
843
                        push @bad_symlinks, substr ( $item, 1+length ($SRC_ROOT) );
844
                    }
227 dpurdie 845
                }
846
                elsif (File::Copy::copy($item, $target))
847
                {
848
                    LogFileOp("Copying File",$target);
363 dpurdie 849
                    #
850
                    #   Mark the file as executable by all
851
                    #   Under windows, this is tricky
852
                    #
853
                    if ( $Win32 )
854
                    {
855
                        my %hash;
856
                        $hash{Everyone} = Win32::FileSecurity::MakeMask( qw( FULL  ) );
857
                        Win32::FileSecurity::Set( $target, \%hash );
858
                    }
859
                    else
860
                    {
861
                        CORE::chmod oct("0775"), $target;
862
                    }
4549 dpurdie 863
                    addFile('file', $item, $target);
227 dpurdie 864
                }
865
                else
866
                {
867
                    Error("Failed to copy file [$item] to [$target]: $!");
868
                }
869
            }
870
        }
871
        else
872
        {
873
            #
874
            #   Merging packages
875
            #   Ensure that the descpkg file is "touched" so that caches
876
            #   that use this file as a timestamp can be updated
877
            #
878
            if ( $item =~ m~/descpkg$~ )
879
            {
880
                LogFileOp("Touch File",$target);
881
                TouchFile( $target ) && Error ( "Failed to touch: $target" );
4549 dpurdie 882
                addFile('merge', $item, $target);
227 dpurdie 883
            }
884
            else
885
            {
263 dpurdie 886
                #
887
                #   MD5 digest the files that are being merged
888
                #   Ignore version_*.h files as these are generated
889
                #   and may contain different dates and line endings
890
                #
267 dpurdie 891
                #   Don't put the files into 'binmode'
892
                #   Need to handle some level of Unix/DOS file endings
893
                #
894
                #
263 dpurdie 895
                my $msg = "Merge Skip File";
896
                unless ( $target =~ m~/version[^/]*\.h$~ )
897
                {
898
                    $msg = "Merge Test File";
899
                    #
900
                    #   Compare the two files with an MD5
901
                    #
902
                    local *FILE;
903
                    open(FILE, $target) or Error ("Can't open '$target': $!");
267 dpurdie 904
                    binmode FILE, ':crlf';
263 dpurdie 905
                    my $target_md5 = Digest::MD5->new->addfile(*FILE)->hexdigest;
906
                    close FILE;
907
 
908
                    open(FILE, $item) or Error ("Can't open '$item': $!");
267 dpurdie 909
                    binmode FILE, ':crlf';
263 dpurdie 910
                    my $source_md5 = Digest::MD5->new->addfile(*FILE)->hexdigest;
911
                    close FILE;
912
 
913
                    unless ( $source_md5 eq $target_md5 )
914
                    {
915
                        $msg = "DIFF: Merge Test File";
916
                        $bad_merge_count ++;
917
                    }
4549 dpurdie 918
                    addFile('merge', $item, $target, $target_md5);
263 dpurdie 919
                }
920
                LogFileOp($msg,$target);
227 dpurdie 921
            }
922
        }
923
    }
924
}
925
 
926
#-------------------------------------------------------------------------------
927
# Function        : TestDpkgArchive
928
#
929
# Description     : Test the structure of the source achive
930
#                   Ensure that it has some files
931
#                   Warn if files are present in the root directory
932
#
933
# Inputs          : None
934
#
935
# Returns         : Warnings
936
#
937
my $test_dir_count = 0;
938
my $test_file_count = 0;
939
my @test_root_file = ();
940
sub TestDpkgArchive
941
{
942
    $test_dir_count = 0;
943
    $test_file_count = 0;
944
    @test_root_file = ();
945
 
4003 dpurdie 946
    if ( $SRC_ROOT ne '' )
947
    {
948
        Error("Failed to find dir [$SRC_ROOT]",
949
              "Check JATS config.") unless ( -d $SRC_ROOT );
950
 
951
 
952
        #
953
        #   Scan the package counting files and folders
954
        #
955
        File::Find::find( \&pkgFind3, $SRC_ROOT );
956
    }
957
 
227 dpurdie 958
    Information ("Package contains:",
959
                 "Files: $test_file_count",
960
                 "Dirs: $test_dir_count",
961
                 );
962
    #
963
    #   There shouldn't be any files in the root directory
964
    #   other than the descpkg and incpkg.
965
    #
966
    Warning ("Unexpected files in package root:", @test_root_file)
967
        if ( @test_root_file  );
4424 dpurdie 968
 
969
    Error ("Bad symbolic links found:", @bad_symlinks)
970
            if ( @bad_symlinks );
971
 
227 dpurdie 972
}
973
 
974
sub pkgFind3
975
{
976
 
977
    #
978
    #   Calculate the target directory name
979
    #
980
    my $target = $File::Find::dir;
263 dpurdie 981
    $target = substr ( $target, length ($SRC_ROOT) );
982
    $target =~ s~^.~~;
227 dpurdie 983
 
984
    if ( -d $_ ) {
985
        $test_dir_count++;
986
    } else {
987
        $test_file_count++;
988
        unless ( $target )
989
        {
241 dpurdie 990
            #
991
            #   Locate files in the package root directory that
992
            #   are not expected to be there.
993
            #
263 dpurdie 994
            unless ((  $_ eq 'descpkg' ) || ( $_ eq 'incpkg' ))
995
            {
996
                push @test_root_file, $_;
997
            }
227 dpurdie 998
        }
4424 dpurdie 999
        if (-l $_ && ! -f $_)
1000
        {
1001
            push @bad_symlinks, substr ( $File::Find::name, 1+length ($SRC_ROOT) );
1002
        }
227 dpurdie 1003
    }
1004
}
1005
 
1006
# ---------------------------------------------------------
1007
# ---------------------------------------------------------
1008
# Main
1009
# ---------------------------------------------------------
1010
# ---------------------------------------------------------
1011
 
1012
 
1013
# Initialise our world
1014
#
1015
Init();
4003 dpurdie 1016
CheckDescPkg();
1017
ShowInfo();
4549 dpurdie 1018
unless ($opt_info)
227 dpurdie 1019
{
4549 dpurdie 1020
    unless ( $opt_test )
227 dpurdie 1021
    {
4549 dpurdie 1022
        Information("Creating dpkg_archive package:", $DPKG_DIR);
1023
        CreateDpkgArchive();
1024
        writeFileInfo();
227 dpurdie 1025
    }
4549 dpurdie 1026
    else
1027
    {
1028
        Information("Testing user package.");
1029
        TestDpkgArchive();
1030
    }
227 dpurdie 1031
}
1032
 
1033
# Done
1034
#
1035
Information ("Done.");
1036
exit 0;
1037
 
1038
 
1039
#-------------------------------------------------------------------------------
1040
#   Documentation
1041
#
1042
 
1043
=pod
1044
 
361 dpurdie 1045
=for htmltoc    SYSUTIL::
1046
 
227 dpurdie 1047
=head1 NAME
1048
 
1049
create_dpkg - Create a dpkg_archive entry
1050
 
1051
=head1 SYNOPSIS
1052
 
1053
 jats create_dpkg [options]
1054
 
1055
 Options:
1056
    -help              - Brief help message
1057
    -help -help        - Detailed help message
1058
    -man               - Full documentation
1059
    -quiet             - Suppress progress messages, then warning messages
1060
    -verbose           - Display additional progress messages
4549 dpurdie 1061
    -override          - Deprecated option
1062
    -delete            - Delete any previous version of the package
263 dpurdie 1063
    -[no]merge         - merge with existing version of the package
227 dpurdie 1064
    -archive=name      - Specify archive (cache, local, main, store, sandbox, deploy)
1065
    -pname=name        - Ensure package is named correctly
1066
    -pversion=version  - Ensure package version is correct
1067
    -generic           - Create a built.generic file
4549 dpurdie 1068
    -[no]md5           - Use MD5 comparison of merged files(enabled)
263 dpurdie 1069
    -[no]test          - Test package. Do not transfer.
4549 dpurdie 1070
    -[no]info          - Display packaging info. Do not transfer.
227 dpurdie 1071
 
1072
=head1 OPTIONS
1073
 
1074
=over 8
1075
 
1076
=item B<-help>
1077
 
1078
Print a brief help message and exits.
1079
 
1080
=item B<-help -help>
1081
 
1082
Print a detailed help message with an explanation for each option.
1083
 
1084
=item B<-man>
1085
 
1086
Prints the manual page and exits.
1087
 
1088
=item B<-quiet>
1089
 
1090
This option will suppress almost all of the progress messages, except for a single
1091
copy message. It is intended to be used when the program is called from another
1092
script.
1093
 
1094
=item B<-override>
1095
 
4549 dpurdie 1096
If this option has been deprecated. It has no effect.
1097
 
1098
It is still present to preserve backward compatability with the automated 
1099
build system.
1100
 
1101
=item B<-delete>
1102
 
227 dpurdie 1103
If this option is enabled then any previous version of the target package will
4549 dpurdie 1104
be deleted.
227 dpurdie 1105
 
1106
=item B<-merge>
1107
 
1108
If this option is enabled then the package will be merged with any existing
4549 dpurdie 1109
package. This option is used by the auto build tool to assemble multi-machine 
1110
packages in dpkg_archive.
227 dpurdie 1111
 
1112
=item B<-archive=name>
1113
 
1114
This option specifies the destination archive to be used. The following names
1115
are supported:
1116
 
361 dpurdie 1117
=over 8
1118
 
1119
=item cache
1120
 
1121
The location of the target archive will be taken from C<GBE_DPKG_CACHE>.
1122
 
1123
=item local
1124
 
1125
The location of the target archive will be taken from C<GBE_DPKG_LOCAL>.
1126
 
1127
=item main (default)
1128
 
1129
The location of the target archive will be taken from C<GBE_DPKG>. This is the
1130
default target archive.
1131
 
1132
=item store
1133
 
1134
The location of the target archive will be taken from C<GBE_DPKG_STORE>.
1135
 
4688 dpurdie 1136
=item replica
1137
 
1138
The location of the target archive will be taken from C<GBE_DPKG_REPLICA>.
1139
 
361 dpurdie 1140
=item sandbox
1141
 
1142
The location of the target archive will be taken from C<GBE_DPKG_SBOX>.
1143
 
1144
=item deploy
1145
 
1146
The location of the target archive will be taken from C<GBE_DPLY>. This is the
1147
default target archive is a deployment package is detected.
1148
 
1149
=back
1150
 
227 dpurdie 1151
=item B<-pname=name>
1152
 
1153
If this option is provided, the utility will ensure that the package is named
1154
correctly.
1155
 
1156
=item B<-pversion=version>
1157
 
1158
If this option is provided, the utility will ensure that the package version is
1159
that expected.
1160
 
4549 dpurdie 1161
=item B<-generic>
227 dpurdie 1162
 
4549 dpurdie 1163
This option will create a built.generic file, instead of one based on the machine
1164
that actually built the package. This is used by the AutoBuilder toolchain.
227 dpurdie 1165
 
263 dpurdie 1166
=item B<-[no]md5>
1167
 
1168
If package builds are being merged then a validity check is performed using
1169
an MD5 digest over the current and the existing file.
1170
 
1171
By default, it is an error for the user file to differ from the merged file.
1172
 
1173
This option disabled the error. The test is still done and the results are
1174
reported.
1175
 
4549 dpurdie 1176
=item B<-test>
227 dpurdie 1177
 
4549 dpurdie 1178
If this option is enabled the utility will perform initial sanity testing, but
1179
it will not perform the copy.
227 dpurdie 1180
 
4549 dpurdie 1181
=item B<-[no]info>
1182
 
1183
This option will cause the program to display information about the packaging 
1184
process and then exit. 
1185
 
1186
No data will be transferred to the archive.
1187
 
227 dpurdie 1188
=back
1189
 
1190
=head1 DESCRIPTION
1191
 
1192
This utility program is used to transfer a package that has been built into
1193
dpkg_archive. The package is then available for general consumption.
1194
 
4549 dpurdie 1195
The utility will perform several operations in the transfer process. These incude:
1196
 
1197
=over 4
1198
 
1199
=item * 
1200
 
1201
Create a tag file to indicate the machine that has performed the transfer
1202
 
1203
=item * 
1204
 
1205
Create an XML file of files that have been transferred. This file contains information
1206
used by the build system when it releases the package, including: name, size and MD5SUM.
1207
 
1208
=item *
1209
 
1210
Detect file conflicts when different builds are merged into a single package. Header files are 
1211
allowed to differ in line ending style, but other files must not conflict. The package will not be 
1212
reproducible if file conflicts exist.
1213
 
1214
=item *
1215
 
1216
Suppress dead symbolic links. A valid symlink will be preserved, but invalid links will be 
1217
removed from the transferred image.
1218
 
1219
=back
1220
 
227 dpurdie 1221
=head2 PACKAGE LOCATION
1222
 
1223
The utility will locate a package by examining the following directores for
1224
the package description file(descpkg).
1225
 
1226
=over 8
1227
 
1228
=item ./build/deploy
1229
 
1230
This format is generated by the deployment builds. The default target archive
1231
will be taken from the environment variable GBE_DPLY.
1232
 
1233
=item ./pkg
1234
 
1235
This format is generated by JATS builds.
1236
 
1237
=item ./build/pkg
1238
 
1239
This format is generated by ANT builds.
1240
 
1241
=back
1242
 
1243
The program should be run in the same directory as the build control files as
1244
the package subdirectory will be created in that directory.
1245
 
1246
=head1 EXAMPLE
1247
 
1248
=head2 jats create_dpkg
1249
 
1250
This will locate a generated package and install it into the dpkg_archive repository.
1251
 
1252
=cut
1253