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