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