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
    #
5003 dpurdie 727
    #   flock the file in an attempt to flush it out across the network
728
    #   Having build issues where the file is not seen for a very long time
729
    #
279 dpurdie 730
    my $built_info = localtime() ."," . gmtime() . " GMT,$GBE_MACHTYPE,$GBE_HOSTNAME,$USER,$GBE_ABT";
227 dpurdie 731
    LogFileOp("Mark File",$touchfile);
279 dpurdie 732
    FileAppend ( $touchfile, $built_info );
5003 dpurdie 733
 
734
    open (TOUCH_LOCK, '>', $touchfile) || Error ("Cannot build marker for locking: $!", $touchfile );
735
    flock TOUCH_LOCK, 2;
736
    close TOUCH_LOCK;
737
 
4634 dpurdie 738
    addFile('file', $touchfile, $touchfile);
227 dpurdie 739
 
740
    #
741
    #   If there is a .lnk file in the archive then remove it now that the
742
    #   archive has been transferred. The .lnk files are created in 'local'
743
    #   archives in order to simplify multi-package builds
744
    #
745
    my $link_file = "$DPKG_ROOT/$DPKG_NAME/$DPKG_VERSION.lnk";
746
    if ( -f $link_file )
747
    {
748
        LogFileOp("Removing Link",$link_file);
749
        unlink $link_file;
750
    }
751
 
4969 dpurdie 752
    #
753
    #   Release the file lock
754
    #
755
    Verbose ("Releasing packageVersion lock");
756
    close ARCHIVE_LOCK;
757
    unlink $pkgLockfile;
227 dpurdie 758
    return 1;
759
}
760
 
761
 
762
#------------------------------------------------------------------------------
763
sub pkgFind2
764
#
765
# Description:
766
#   This subroutine is used to locate all associated pkg files in
767
#   the local pkg dir.
768
#
769
#   This routine is called for each file and directory within the package
770
#   Some files and directories are treated in a special manner
771
#       - Top level directory is ignored
772
#
773
#
774
#
775
#------------------------------------------------------------------------------
776
{
777
    my $item = $File::Find::name;
778
    my $base = File::Basename::basename($item);
779
 
780
    #
781
    #   Calculate the target directory name
782
    #
783
    my $target = $item;
241 dpurdie 784
    $target = $DPKG_DIR . substr ( $item, length ($SRC_ROOT) );
227 dpurdie 785
 
786
    if ( -d $item )
787
    {
788
        #
789
        #   Ignore the top level directory
790
        #   It has already been created
791
        #
792
        return
793
            if ( $item eq $SRC_ROOT );
794
 
795
        #
796
        #   Directories are handled differently
797
        #       - Directories are created with nice permissions
263 dpurdie 798
        #       - If the directory already exists then it is being merged.
227 dpurdie 799
        #
800
        if ( ! -d "$target" )
801
        {
802
            LogFileOp("Creating Dir", $target);
803
            mkpath("$target", 0, 0775);
4549 dpurdie 804
            addFile('dir', $item , $target);
227 dpurdie 805
        }
806
    }
807
    else
808
    {
809
        #
810
        #   File copy
811
        #   If merging then do not overwrite an existing file
812
        #
813
        unless ( $opt_merge && -f $target )
814
        {
815
            if ( $item =~ m~/descpkg$~ )
816
            {
817
                LogFileOp("Rewrite File",$target);
4549 dpurdie 818
                TransferDescpkg( $item, $target );
363 dpurdie 819
                CORE::chmod oct("0664"), $target;
4549 dpurdie 820
                addFile('file', $item, $target);
227 dpurdie 821
            }
822
            else
823
            {
824
                #
825
                #   Copy file to destination
826
                #   If the file is a link, then duplicate the link contents
827
                #   Use: Unix libraries are created as two files:
828
                #        lib.xxxx.so -> libxxxx.so.vv.vv.vv
829
                #
830
                if ( -l $item )
831
                {
4424 dpurdie 832
                    if (-f $item)
227 dpurdie 833
                    {
4424 dpurdie 834
                        LogFileOp("Copying Link", $target);
835
                        my $link = readlink $item;
836
                        Verbose( "Link: $item, $link");
837
                        symlink ($link, $target );
838
                        unless ( $link && -l $target )
839
                        {
840
                            Error("Failed to copy link [$item] to [$target]: $!");
841
                        }
4549 dpurdie 842
                        addFile('link', $item , $target);
227 dpurdie 843
                    }
4424 dpurdie 844
                    else
845
                    {
846
                        # Don't copy broken Symlinks
847
                        # Perhaps this should be an error - but is will break escrow builds
848
                        #
849
                        LogFileOp("Broken SymLink", $target);
850
                        push @bad_symlinks, substr ( $item, 1+length ($SRC_ROOT) );
851
                    }
227 dpurdie 852
                }
853
                elsif (File::Copy::copy($item, $target))
854
                {
855
                    LogFileOp("Copying File",$target);
363 dpurdie 856
                    #
857
                    #   Mark the file as executable by all
858
                    #   Under windows, this is tricky
859
                    #
860
                    if ( $Win32 )
861
                    {
862
                        my %hash;
863
                        $hash{Everyone} = Win32::FileSecurity::MakeMask( qw( FULL  ) );
864
                        Win32::FileSecurity::Set( $target, \%hash );
865
                    }
866
                    else
867
                    {
868
                        CORE::chmod oct("0775"), $target;
869
                    }
4549 dpurdie 870
                    addFile('file', $item, $target);
227 dpurdie 871
                }
872
                else
873
                {
874
                    Error("Failed to copy file [$item] to [$target]: $!");
875
                }
876
            }
877
        }
878
        else
879
        {
880
            #
881
            #   Merging packages
882
            #   Ensure that the descpkg file is "touched" so that caches
883
            #   that use this file as a timestamp can be updated
884
            #
885
            if ( $item =~ m~/descpkg$~ )
886
            {
887
                LogFileOp("Touch File",$target);
888
                TouchFile( $target ) && Error ( "Failed to touch: $target" );
4549 dpurdie 889
                addFile('merge', $item, $target);
227 dpurdie 890
            }
891
            else
892
            {
263 dpurdie 893
                #
894
                #   MD5 digest the files that are being merged
895
                #   Ignore version_*.h files as these are generated
896
                #   and may contain different dates and line endings
897
                #
267 dpurdie 898
                #   Don't put the files into 'binmode'
899
                #   Need to handle some level of Unix/DOS file endings
900
                #
901
                #
263 dpurdie 902
                my $msg = "Merge Skip File";
903
                unless ( $target =~ m~/version[^/]*\.h$~ )
904
                {
905
                    $msg = "Merge Test File";
906
                    #
907
                    #   Compare the two files with an MD5
908
                    #
909
                    local *FILE;
910
                    open(FILE, $target) or Error ("Can't open '$target': $!");
267 dpurdie 911
                    binmode FILE, ':crlf';
263 dpurdie 912
                    my $target_md5 = Digest::MD5->new->addfile(*FILE)->hexdigest;
913
                    close FILE;
914
 
915
                    open(FILE, $item) or Error ("Can't open '$item': $!");
267 dpurdie 916
                    binmode FILE, ':crlf';
263 dpurdie 917
                    my $source_md5 = Digest::MD5->new->addfile(*FILE)->hexdigest;
918
                    close FILE;
919
 
920
                    unless ( $source_md5 eq $target_md5 )
921
                    {
922
                        $msg = "DIFF: Merge Test File";
923
                        $bad_merge_count ++;
924
                    }
4549 dpurdie 925
                    addFile('merge', $item, $target, $target_md5);
263 dpurdie 926
                }
927
                LogFileOp($msg,$target);
227 dpurdie 928
            }
929
        }
930
    }
931
}
932
 
933
#-------------------------------------------------------------------------------
934
# Function        : TestDpkgArchive
935
#
936
# Description     : Test the structure of the source achive
937
#                   Ensure that it has some files
938
#                   Warn if files are present in the root directory
939
#
940
# Inputs          : None
941
#
942
# Returns         : Warnings
943
#
944
my $test_dir_count = 0;
945
my $test_file_count = 0;
946
my @test_root_file = ();
947
sub TestDpkgArchive
948
{
949
    $test_dir_count = 0;
950
    $test_file_count = 0;
951
    @test_root_file = ();
952
 
4003 dpurdie 953
    if ( $SRC_ROOT ne '' )
954
    {
955
        Error("Failed to find dir [$SRC_ROOT]",
956
              "Check JATS config.") unless ( -d $SRC_ROOT );
957
 
958
 
959
        #
960
        #   Scan the package counting files and folders
961
        #
962
        File::Find::find( \&pkgFind3, $SRC_ROOT );
963
    }
964
 
227 dpurdie 965
    Information ("Package contains:",
966
                 "Files: $test_file_count",
967
                 "Dirs: $test_dir_count",
968
                 );
969
    #
970
    #   There shouldn't be any files in the root directory
971
    #   other than the descpkg and incpkg.
972
    #
973
    Warning ("Unexpected files in package root:", @test_root_file)
974
        if ( @test_root_file  );
4424 dpurdie 975
 
976
    Error ("Bad symbolic links found:", @bad_symlinks)
977
            if ( @bad_symlinks );
978
 
227 dpurdie 979
}
980
 
981
sub pkgFind3
982
{
983
 
984
    #
985
    #   Calculate the target directory name
986
    #
987
    my $target = $File::Find::dir;
263 dpurdie 988
    $target = substr ( $target, length ($SRC_ROOT) );
989
    $target =~ s~^.~~;
227 dpurdie 990
 
991
    if ( -d $_ ) {
992
        $test_dir_count++;
993
    } else {
994
        $test_file_count++;
995
        unless ( $target )
996
        {
241 dpurdie 997
            #
998
            #   Locate files in the package root directory that
999
            #   are not expected to be there.
1000
            #
263 dpurdie 1001
            unless ((  $_ eq 'descpkg' ) || ( $_ eq 'incpkg' ))
1002
            {
1003
                push @test_root_file, $_;
1004
            }
227 dpurdie 1005
        }
4424 dpurdie 1006
        if (-l $_ && ! -f $_)
1007
        {
1008
            push @bad_symlinks, substr ( $File::Find::name, 1+length ($SRC_ROOT) );
1009
        }
227 dpurdie 1010
    }
1011
}
1012
 
1013
# ---------------------------------------------------------
1014
# ---------------------------------------------------------
1015
# Main
1016
# ---------------------------------------------------------
1017
# ---------------------------------------------------------
1018
 
1019
 
1020
# Initialise our world
1021
#
1022
Init();
4003 dpurdie 1023
CheckDescPkg();
1024
ShowInfo();
4549 dpurdie 1025
unless ($opt_info)
227 dpurdie 1026
{
4549 dpurdie 1027
    unless ( $opt_test )
227 dpurdie 1028
    {
4549 dpurdie 1029
        Information("Creating dpkg_archive package:", $DPKG_DIR);
1030
        CreateDpkgArchive();
1031
        writeFileInfo();
227 dpurdie 1032
    }
4549 dpurdie 1033
    else
1034
    {
1035
        Information("Testing user package.");
1036
        TestDpkgArchive();
1037
    }
227 dpurdie 1038
}
1039
 
1040
# Done
1041
#
1042
Information ("Done.");
1043
exit 0;
1044
 
1045
 
1046
#-------------------------------------------------------------------------------
1047
#   Documentation
1048
#
1049
 
1050
=pod
1051
 
361 dpurdie 1052
=for htmltoc    SYSUTIL::
1053
 
227 dpurdie 1054
=head1 NAME
1055
 
1056
create_dpkg - Create a dpkg_archive entry
1057
 
1058
=head1 SYNOPSIS
1059
 
1060
 jats create_dpkg [options]
1061
 
1062
 Options:
1063
    -help              - Brief help message
1064
    -help -help        - Detailed help message
1065
    -man               - Full documentation
1066
    -quiet             - Suppress progress messages, then warning messages
1067
    -verbose           - Display additional progress messages
4549 dpurdie 1068
    -override          - Deprecated option
1069
    -delete            - Delete any previous version of the package
263 dpurdie 1070
    -[no]merge         - merge with existing version of the package
227 dpurdie 1071
    -archive=name      - Specify archive (cache, local, main, store, sandbox, deploy)
1072
    -pname=name        - Ensure package is named correctly
1073
    -pversion=version  - Ensure package version is correct
1074
    -generic           - Create a built.generic file
4549 dpurdie 1075
    -[no]md5           - Use MD5 comparison of merged files(enabled)
263 dpurdie 1076
    -[no]test          - Test package. Do not transfer.
4549 dpurdie 1077
    -[no]info          - Display packaging info. Do not transfer.
227 dpurdie 1078
 
1079
=head1 OPTIONS
1080
 
1081
=over 8
1082
 
1083
=item B<-help>
1084
 
1085
Print a brief help message and exits.
1086
 
1087
=item B<-help -help>
1088
 
1089
Print a detailed help message with an explanation for each option.
1090
 
1091
=item B<-man>
1092
 
1093
Prints the manual page and exits.
1094
 
1095
=item B<-quiet>
1096
 
1097
This option will suppress almost all of the progress messages, except for a single
1098
copy message. It is intended to be used when the program is called from another
1099
script.
1100
 
1101
=item B<-override>
1102
 
4549 dpurdie 1103
If this option has been deprecated. It has no effect.
1104
 
1105
It is still present to preserve backward compatability with the automated 
1106
build system.
1107
 
1108
=item B<-delete>
1109
 
227 dpurdie 1110
If this option is enabled then any previous version of the target package will
4549 dpurdie 1111
be deleted.
227 dpurdie 1112
 
1113
=item B<-merge>
1114
 
1115
If this option is enabled then the package will be merged with any existing
4549 dpurdie 1116
package. This option is used by the auto build tool to assemble multi-machine 
1117
packages in dpkg_archive.
227 dpurdie 1118
 
1119
=item B<-archive=name>
1120
 
1121
This option specifies the destination archive to be used. The following names
1122
are supported:
1123
 
361 dpurdie 1124
=over 8
1125
 
1126
=item cache
1127
 
1128
The location of the target archive will be taken from C<GBE_DPKG_CACHE>.
1129
 
1130
=item local
1131
 
1132
The location of the target archive will be taken from C<GBE_DPKG_LOCAL>.
1133
 
1134
=item main (default)
1135
 
1136
The location of the target archive will be taken from C<GBE_DPKG>. This is the
1137
default target archive.
1138
 
1139
=item store
1140
 
1141
The location of the target archive will be taken from C<GBE_DPKG_STORE>.
1142
 
4688 dpurdie 1143
=item replica
1144
 
1145
The location of the target archive will be taken from C<GBE_DPKG_REPLICA>.
1146
 
361 dpurdie 1147
=item sandbox
1148
 
1149
The location of the target archive will be taken from C<GBE_DPKG_SBOX>.
1150
 
1151
=item deploy
1152
 
1153
The location of the target archive will be taken from C<GBE_DPLY>. This is the
1154
default target archive is a deployment package is detected.
1155
 
1156
=back
1157
 
227 dpurdie 1158
=item B<-pname=name>
1159
 
1160
If this option is provided, the utility will ensure that the package is named
1161
correctly.
1162
 
1163
=item B<-pversion=version>
1164
 
1165
If this option is provided, the utility will ensure that the package version is
1166
that expected.
1167
 
4549 dpurdie 1168
=item B<-generic>
227 dpurdie 1169
 
4549 dpurdie 1170
This option will create a built.generic file, instead of one based on the machine
1171
that actually built the package. This is used by the AutoBuilder toolchain.
227 dpurdie 1172
 
263 dpurdie 1173
=item B<-[no]md5>
1174
 
1175
If package builds are being merged then a validity check is performed using
1176
an MD5 digest over the current and the existing file.
1177
 
1178
By default, it is an error for the user file to differ from the merged file.
1179
 
1180
This option disabled the error. The test is still done and the results are
1181
reported.
1182
 
4549 dpurdie 1183
=item B<-test>
227 dpurdie 1184
 
4549 dpurdie 1185
If this option is enabled the utility will perform initial sanity testing, but
1186
it will not perform the copy.
227 dpurdie 1187
 
4549 dpurdie 1188
=item B<-[no]info>
1189
 
1190
This option will cause the program to display information about the packaging 
1191
process and then exit. 
1192
 
1193
No data will be transferred to the archive.
1194
 
227 dpurdie 1195
=back
1196
 
1197
=head1 DESCRIPTION
1198
 
1199
This utility program is used to transfer a package that has been built into
1200
dpkg_archive. The package is then available for general consumption.
1201
 
4549 dpurdie 1202
The utility will perform several operations in the transfer process. These incude:
1203
 
1204
=over 4
1205
 
1206
=item * 
1207
 
1208
Create a tag file to indicate the machine that has performed the transfer
1209
 
1210
=item * 
1211
 
1212
Create an XML file of files that have been transferred. This file contains information
1213
used by the build system when it releases the package, including: name, size and MD5SUM.
1214
 
1215
=item *
1216
 
1217
Detect file conflicts when different builds are merged into a single package. Header files are 
1218
allowed to differ in line ending style, but other files must not conflict. The package will not be 
1219
reproducible if file conflicts exist.
1220
 
1221
=item *
1222
 
1223
Suppress dead symbolic links. A valid symlink will be preserved, but invalid links will be 
1224
removed from the transferred image.
1225
 
1226
=back
1227
 
227 dpurdie 1228
=head2 PACKAGE LOCATION
1229
 
1230
The utility will locate a package by examining the following directores for
1231
the package description file(descpkg).
1232
 
1233
=over 8
1234
 
1235
=item ./build/deploy
1236
 
1237
This format is generated by the deployment builds. The default target archive
1238
will be taken from the environment variable GBE_DPLY.
1239
 
1240
=item ./pkg
1241
 
1242
This format is generated by JATS builds.
1243
 
1244
=item ./build/pkg
1245
 
1246
This format is generated by ANT builds.
1247
 
1248
=back
1249
 
1250
The program should be run in the same directory as the build control files as
1251
the package subdirectory will be created in that directory.
1252
 
1253
=head1 EXAMPLE
1254
 
1255
=head2 jats create_dpkg
1256
 
1257
This will locate a generated package and install it into the dpkg_archive repository.
1258
 
1259
=cut
1260