Subversion Repositories DevTools

Rev

Details | Last modification | View Log | RSS feed

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