Subversion Repositories DevTools

Rev

Rev 6099 | Rev 6857 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
407 dpurdie 1
########################################################################
3921 dpurdie 2
# Copyright (C) 1998-2013 Vix Technology, All rights reserved
407 dpurdie 3
#
3921 dpurdie 4
# Module name   : DebianPackager.pl
407 dpurdie 5
# Module type   : Makefile system
3921 dpurdie 6
# Compiler(s)   : Perl
407 dpurdie 7
# Environment(s): jats
8
#
9
# Description   : This program is invoked by the MakeDebianPackage
10
#                 directive, that is a part of this package
11
#
12
#                 The program will use a user-provided script in order
13
#                 to create a Debian Package.
14
#
15
#                 The user script may call a number of directives in order to
16
#                 construct an image of the package being installed.
17
#
18
#                 The script specifies Debian configuration scaripts that
19
#                 will be embedded in the package.
20
#
21
#                 This program will:
22
#                   Construct a filesystem image under control of the directives
23
#                   within the user script
24
#
25
#                   Massage the Debian control file
26
#
27
#                   Create a Debian Package
28
#
29
#                   Transfer it to the users 'BIN' directory, where it is
30
#                   available to be packaged.
31
#
32
#                 Summary of directives available to the user-script:
6088 dpurdie 33
#                       Message                 - Display progress text
407 dpurdie 34
#                       AddInitScript           - Add an init script
35
#                       CatFile                 - Append to a file
4641 dpurdie 36
#                       ConvertFile             - Convert file(s) to Unix or Dos Text
407 dpurdie 37
#                       CopyDir                 - Copy directory tree
38
#                       CopyFile                - Copy a file
39
#                       CopyBinFile             - Copy an executable file
40
#                       CopyLibFile             - Copy a library file
6088 dpurdie 41
#                       CopyDebPackage          - Copy a Debian Package
407 dpurdie 42
#                       CreateDir               - Create a directory
43
#                       DebianFiles             - Specify control and script files
4636 dpurdie 44
#                       DebianControlFile       - Specify control and script files
45
#                       DebianDepends           - Add Depends entry to control file
407 dpurdie 46
#                       EchoFile                - Place text into a file
47
#                       MakeSymLink             - Create a symbolic link
48
#                       PackageDescription      - Specify the package description
4641 dpurdie 49
#                       ReplaceTags             - Replace Tags on target file
407 dpurdie 50
#                       SetFilePerms            - Set file permissions
51
#                       SetVerbose              - Control progress display
52
#                       IsProduct               - Flow control
53
#                       IsPlatform              - Flow control
54
#                       IsTarget                - Flow control
427 dpurdie 55
#                       IsVariant               - Flow control
6088 dpurdie 56
#                       IsAlias                 - Flow control
407 dpurdie 57
#
58
#                 Thoughts for expansion:
59
#                       SrcDir                  - Extend path for resolving local files
60
#
61
#                   Less used:
62
#                        ExpandLinkFiles        - Expand .LINK files
63
#
64
#                   Internal Use:
65
#                        FindFiles              - Find a file
66
#                        ResolveFile            - Resolve a 'local' source file
4641 dpurdie 67
#                        chmodItem              - Set file or directory permissions
407 dpurdie 68
#                        
69
#......................................................................#
70
 
411 dpurdie 71
require 5.006_001;
407 dpurdie 72
use strict;
73
use warnings;
74
 
75
use Getopt::Long;
76
use File::Path;
77
use File::Copy;
78
use File::Find;
79
use JatsSystem;
80
use FileUtils;
81
use JatsError;
4636 dpurdie 82
use JatsLocateFiles;
407 dpurdie 83
use ReadBuildConfig;
423 dpurdie 84
use JatsCopy ();                            # Don't import anything
407 dpurdie 85
 
86
#
87
#   Globals
88
#
425 dpurdie 89
my $DebianWorkDirBase;                      # Workspace
407 dpurdie 90
my $DebianWorkDir;                          # Dir to create file system image within
91
 
92
#
93
#   Command line options
94
#
95
my $opt_debug   = $ENV{'GBE_DEBUG'};        # Allow global debug
96
my $opt_verbose = $ENV{'GBE_VERBOSE'};      # Allow global verbose
97
my $opt_vargs;                              # Verbose arg
98
my $opt_help = 0;
99
my $opt_manual = 0;
100
my $opt_clean = 0;
101
my $opt_interfacedir;
102
my $opt_package_script;
103
my $opt_interfaceincdir;
104
my $opt_interfacelibdir;
105
my $opt_interfacebindir;
106
my $opt_libdir;
107
my $opt_bindir;
108
my $opt_localincdir;
109
my $opt_locallibdir;
110
my $opt_localbindir;
111
my $opt_pkgdir;
112
my $opt_pkglibdir;
113
my $opt_pkgbindir;
114
my $opt_pkgpkgdir;
115
my $opt_output;
4740 dpurdie 116
my $opt_tarFile;
5215 dpurdie 117
my $opt_noarch;
407 dpurdie 118
 
119
#
6099 dpurdie 120
#   Options marked as 'our' so that they are visible within the users script
121
#   Don't give the user too much
122
#
123
our $opt_platform;
124
our $opt_type;
125
our $opt_buildname;
126
our $opt_buildversion;
127
our $opt_target;
128
our $opt_product;
129
our $opt_name;
130
our $opt_variant;
131
our $opt_pkgarch;
132
 
133
#
407 dpurdie 134
#   Options derived from script directives
135
#
136
my $opt_description;
137
 
415 dpurdie 138
#
139
#   Globals
140
#
141
my @ResolveFileList;                    # Cached Package File List
142
my @ResolveBinFileList;                 # Cached PackageBin File List
6088 dpurdie 143
my @ResolveDebFileList;                 # Cached PackageDeb File List
415 dpurdie 144
my @ResolveLibFileList;                 # Cached PackageLib File List
4635 dpurdie 145
my %DebianControlFiles;                 # Control Files
146
my %DebianControlFileNames;             # Control Files by name
147
my @DependencyList;                     # Package Dependencies
4666 dpurdie 148
my @ConfigList;                         # Config Files
6092 dpurdie 149
my %opt_aliases;                        # Cached Alias Names
407 dpurdie 150
 
151
#-------------------------------------------------------------------------------
152
# Function        : Main Entry point
153
#
154
# Description     : This function will be called when the package is initialised
155
#                   Extract arguments from the users environment
156
#
157
#                   Done here to greatly simplify the user script
158
#                   There should be no junk in the user script - keep it simple
159
#
160
# Inputs          :
161
#
162
# Returns         : 
163
#
164
main();
165
sub main
166
{
167
    my $result = GetOptions (
168
                "verbose:s"         => \$opt_vargs,
169
                "clean"             => \$opt_clean,
170
                "Type=s"            => \$opt_type,
4105 dpurdie 171
                "BuildName=s"       => \$opt_buildname,                     # Raw Jats Package Name (Do not use)
172
                "Name=s"            => \$opt_name,                          # Massaged Debian Package Name
407 dpurdie 173
                "BuildVersion=s"    => \$opt_buildversion,
174
                "Platform=s"        => \$opt_platform,
175
                "Target=s"          => \$opt_target,
176
                "Product=s"         => \$opt_product,
177
                "DebianPackage=s"   => \$opt_package_script,
178
                "InterfaceDir=s"    => \$opt_interfacedir,
179
                "InterfaceIncDir=s" => \$opt_interfaceincdir,
180
                "InterfaceLibDir=s" => \$opt_interfacelibdir,
181
                "InterfaceBinDir=s" => \$opt_interfacebindir,
182
                "LibDir=s"          => \$opt_libdir,
183
                "BinDir=s"          => \$opt_bindir,
184
                "LocalIncDir=s"     => \$opt_localincdir,
185
                "LocalLibDir=s"     => \$opt_locallibdir,
186
                "LocalBinDir=s"     => \$opt_localbindir,
187
                "PackageDir=s"      => \$opt_pkgdir,
188
                "PackageLibDir=s"   => \$opt_pkglibdir,
189
                "PackageBinDir=s"   => \$opt_pkgbindir,
190
                "PackagePkgDir=s"   => \$opt_pkgpkgdir,
191
                "Output=s"          => \$opt_output,
4740 dpurdie 192
                "tarFile=s"         => \$opt_tarFile,
427 dpurdie 193
                "Variant:s"         => \$opt_variant,
3921 dpurdie 194
                "PkgArch:s"         => \$opt_pkgarch,
5215 dpurdie 195
                "NoArch"            => \$opt_noarch,
407 dpurdie 196
    );
197
    $opt_verbose++ unless ( $opt_vargs eq '@' );
198
 
199
    ErrorConfig( 'name'    => 'DebianUtils',
200
                 'verbose' => $opt_verbose,
201
                 'debug'   => $opt_debug );
202
 
203
    #
204
    #   Init the FileSystem Uiltity interface
205
    #
206
    InitFileUtils();
207
 
208
    #
209
    #   Ensure that we have all required options
210
    #
211
    Error ("Platform not set")                  unless ( $opt_platform );
212
    Error ("Type not set")                      unless ( $opt_type );
213
    Error ("BuildName not set")                 unless ( $opt_buildname );
4105 dpurdie 214
    Error ("Debian Package Name not set")       unless ( $opt_name );
407 dpurdie 215
    Error ("BuildVersion not set")              unless ( $opt_buildversion );
216
    Error ("InterfaceDir not set")              unless ( $opt_interfacedir );
217
    Error ("Target not set")                    unless ( $opt_target );
218
    Error ("Product not set")                   unless ( $opt_product );
219
    Error ("DebianPackage not set")             unless ( $opt_package_script );
220
    Error ("Ouput File not set")                unless ( $opt_output );
221
 
222
    #
223
    #   Read in relevent config information
224
    #
225
    ReadBuildConfig ($opt_interfacedir, $opt_platform, '--NoTest' );
226
 
227
    #
228
    #   Build the package image in a directory based on the target being created
229
    #
6088 dpurdie 230
    $DebianWorkDirBase = uc("$opt_platform$opt_type.image");
425 dpurdie 231
    $DebianWorkDir = "$DebianWorkDirBase/$opt_name";
407 dpurdie 232
 
233
    #
234
    #   Configure the System command to fail on any error
235
    #
236
    SystemConfig ( ExitOnError => 1 );
237
 
238
    #
5217 dpurdie 239
    #   Defaults
240
    #
241
    $opt_pkgarch = $opt_platform unless ( $opt_pkgarch );
242
    $opt_pkgarch = 'all' if ( $opt_noarch );
243
 
244
    #
407 dpurdie 245
    #   Display variables used
246
    #
4740 dpurdie 247
    Message    ("=Building Debian Package =============================================");
248
    Message    ("Build $opt_name");
249
    Message    ("       Package: $opt_buildname");
250
    Message    ("       Variant: $opt_variant") if ($opt_variant);
251
    Message    ("       Version: $opt_buildversion");
252
    Message    ("  Building for: $opt_platform, $opt_target");
253
    Message    ("       Product: $opt_product");
254
    Message    ("          Type: $opt_type");
255
    Message    ("      Pkg Arch: $opt_pkgarch") if ($opt_pkgarch);
256
    Verbose    ("       Verbose: $opt_verbose");
257
    Verbose    ("  InterfaceDir: $opt_interfacedir");
258
    Message    ("       Package: " . StripDirExt($opt_output));
259
    Message    ("       TarFile: " . StripDirExt($opt_tarFile)) if ($opt_tarFile);
260
    Message    ("======================================================================");
407 dpurdie 261
 
262
    #
263
    #   Perform Clean up
264
    #   Invoked during "make clean" or "make clobber"
265
    #
266
    if ( $opt_clean )
267
    {
268
        Message ("Remove packaging directory: $DebianWorkDir");
425 dpurdie 269
 
270
        #
271
        #   Remove the directory for this package
272
        #   Remove the general work dir - if all packages have been cleaned
273
        #
407 dpurdie 274
        rmtree( $DebianWorkDir );
425 dpurdie 275
        rmdir( $DebianWorkDirBase );
407 dpurdie 276
        rmtree ($opt_output) if ( -f $opt_output );
277
        exit;
278
    }
279
 
280
    #
5215 dpurdie 281
    #   NoArch sanity test
282
    #   MUST only build no-arch for production
283
    #   User MUST do this in the build.pl file
284
    #
285
    if ($opt_noarch && $opt_type ne 'P')
286
    {
287
        Error ("Debian Packages marked as NoArch (all) must be built ONLY for production",
288
               "This must be configured in the build.pl" );
289
    }
290
 
291
    #
407 dpurdie 292
    #   Clean  out the WORK directory
293
    #   Always start with a clean slate
294
    #
295
    #   Ensure that the base of the directory tree does not have 'setgid'
296
    #       This will upset the debian packager
297
    #       This may be an artifact from the users directory and not expected
298
    #
299
    rmtree( $DebianWorkDir );
300
    mkpath( $DebianWorkDir );
301
 
302
    my $perm = (stat $DebianWorkDir)[2] & 0777;
303
    chmod ( $perm & 0777, $DebianWorkDir );
304
 
305
    #
306
    #   Invoke the user script to do the hard work
307
    #
4139 dpurdie 308
    unless (my $return = do $opt_package_script) {
309
            Error ("Couldn't parse $opt_package_script: $@") if $@;
310
            Error ("Couldn't do $opt_package_script: $!")    unless defined $return;
311
        }
407 dpurdie 312
 
313
    #
314
    #   Complete the building of the package
315
    #
4740 dpurdie 316
    if ($opt_tarFile)
317
    {
318
        BuildTarFile();
319
        Message ("Created TGZ file");
320
    }
321
 
322
 
407 dpurdie 323
    BuildDebianPackage ();
324
    Message ("Created Debian Package");
325
}
326
 
327
#-------------------------------------------------------------------------------
328
# Function        : BuildDebianPackage
329
#
330
# Description     : This function will create the Debian Package
331
#                   and transfer it to the target directory
332
#
333
# Inputs          : None
334
#
335
# Returns         : Nothing
336
#
337
sub BuildDebianPackage
338
{
339
    Error ("BuildDebianPackage: No Control File or Package Description")
4635 dpurdie 340
        unless ( exists($DebianControlFiles{'control'}) || $opt_description );
407 dpurdie 341
 
342
    #
343
    #   Convert the FileSystem Image into a Debian Package
344
    #       Insert Debian control files
345
    #
346
    Verbose ("Copy in the Debian Control Files");
347
    mkdir ( "$DebianWorkDir/DEBIAN" );
348
 
4635 dpurdie 349
    #
350
    #   Copy in all the named Debian Control files
351
    #       Ignore any control file. It will be done next
352
    #
353
    foreach my $key ( keys %DebianControlFiles )
354
    {
355
        next if ($key eq 'control');
4676 dpurdie 356
        CopyFile ( $DebianControlFiles{$key}, '/DEBIAN', $key  );
4635 dpurdie 357
    }
4666 dpurdie 358
 
359
    #
360
    #   Create 'conffiles'
361
    #       Append to any user provided file
362
    if ( @ConfigList )
363
    {
364
        my $conffiles = "$DebianWorkDir/DEBIAN/conffiles";
365
        Warning("Appending user specified entries to conffiles") if ( -f $conffiles);
366
        FileAppend( $conffiles, @ConfigList );
367
    }
4635 dpurdie 368
 
369
    #
370
    #   Massage the 'control' file
371
    #
372
    UpdateControlFile ($DebianControlFiles{'control'} );
407 dpurdie 373
 
4635 dpurdie 374
    #
375
    #   Mark all files in the debian folder as read-execute
376
    #
407 dpurdie 377
    System ( 'chmod', '-R', 'a+rx', "$DebianWorkDir/DEBIAN" );
378
    System ( 'build_dpkg.sh', '-b', $DebianWorkDir);
379
    System ( 'mv', '-f', "$DebianWorkDir.deb", $opt_output );
380
 
381
    System ("build_dpkg.sh", '-I', $opt_output) if (IsVerbose(1));
382
 
383
}
384
 
385
#-------------------------------------------------------------------------------
4740 dpurdie 386
# Function        : BuildTarFile 
387
#
388
# Description     : This function will create a TGZ file of the constructed package
389
#                   Not often used 
390
#
391
# Inputs          : None
392
#
393
# Returns         : Nothing
394
#
395
sub BuildTarFile
396
{
397
    Verbose ("Create TGZ file containing body of the package");
398
    System ('tar', 
399
            '--create',
400
            '--auto-compress',
401
            '--owner=0' ,
402
            '--group=0' ,
403
            '--one-file-system' ,
404
            '--exclude=./DEBIAN' ,
405
            '-C', $DebianWorkDir,  
406
            '--file', $opt_tarFile,
407
            '.'
408
            );
409
}
410
 
411
 
412
#-------------------------------------------------------------------------------
407 dpurdie 413
# Function        : UpdateControlFile
414
#
4188 dpurdie 415
# Description     : Update the Debian 'control' file to fix up various fields
407 dpurdie 416
#                   within the file.
417
#
418
#                   If the files has not been specified, then a basic control
419
#                   file will be provided.
420
#
421
#                   This routine knows where the control file will be placed
422
#                   within the output work space.
423
#
424
# Inputs          : $src            - Path to source file
425
#                   Uses global variables
426
#
427
# Returns         : Nothing
428
#
429
sub UpdateControlFile
430
{
431
    my($src) = @_;
432
    my $dst = "$DebianWorkDir/DEBIAN/control";
433
 
434
    unless ( $src )
435
    {
436
        CreateControlFile();
437
        return;
438
    }
439
 
4635 dpurdie 440
    #
441
    #   User has provided a control file
442
    #       Tweak the internals
443
    #
407 dpurdie 444
    Verbose ("UpdateControlFile: $dst" );
445
    $src = ResolveFile( 0, $src );
446
 
4635 dpurdie 447
    #   Calc depends line
4636 dpurdie 448
    my $depData = join (', ', @DependencyList );
4635 dpurdie 449
 
450
    open (SF, '<', $src) || Error ("UpdateControlFile: Cannot open:$src, $!");
451
    open (DF, '>', $dst) || Error ("UpdateControlFile: Cannot create:$dst, $!");
407 dpurdie 452
    while ( <SF> )
453
    {
454
        s~\s*$~~;
455
        if ( m~^Package:~ ) {
4105 dpurdie 456
            $_ = "Package: $opt_name";
407 dpurdie 457
 
458
        } elsif ( m~^Version:~ ) {
459
            $_ = "Version: $opt_buildversion";
460
 
461
        } elsif ( m~^Architecture:~ ) {
3921 dpurdie 462
            $_ = "Architecture: $opt_pkgarch";
407 dpurdie 463
 
464
        } elsif ( $opt_description && m~^Description:~ ) {
465
            $_ = "Description: $opt_description";
4635 dpurdie 466
 
467
        } elsif ( m~^Depends:~ ) {
468
            $_ = "Depends: $depData";
469
            $depData = '';
407 dpurdie 470
        }
471
        print DF $_ , "\n";
472
    }
4635 dpurdie 473
 
407 dpurdie 474
    close (SF);
475
    close (DF);
4635 dpurdie 476
 
477
    #
478
    #   Warn if Depends section is needed
479
    #
480
    Error ("No Depends section seen in user control file") 
481
        if ($depData);
407 dpurdie 482
}
483
 
484
#-------------------------------------------------------------------------------
485
# Function        : CreateControlFile
486
#
487
# Description     : Craete a basic debian control file
488
#
489
# Inputs          : Uses global variables
490
#
491
# Returns         : 
492
#
493
sub CreateControlFile
494
{
495
    my $dst = "$DebianWorkDir/DEBIAN/control";
496
 
497
    Verbose ("CreateControlFile: $dst" );
498
 
4636 dpurdie 499
    my $depData = join (', ', @DependencyList );
4635 dpurdie 500
 
407 dpurdie 501
    open (DF, '>', $dst) || Error ("CreateControlFile: Cannot create:$dst");
4105 dpurdie 502
    print DF "Package: $opt_name\n";
407 dpurdie 503
    print DF "Version: $opt_buildversion\n";
504
    print DF "Section: main\n";
505
    print DF "Priority: standard\n";
3921 dpurdie 506
    print DF "Architecture: $opt_pkgarch\n";
507
    print DF "Essential: No\n";
508
    print DF "Maintainer: Vix Technology\n";
407 dpurdie 509
    print DF "Description: $opt_description\n";
4635 dpurdie 510
    print DF "Depends: $depData\n" if ($depData);
511
 
407 dpurdie 512
    close (DF);
513
}
514
 
515
#-------------------------------------------------------------------------------
516
# Function        : SetVerbose
517
#
518
# Description     : Set the level of verbosity
519
#                   Display activity
520
#
521
# Inputs          : Verbosity level
522
#                       0 - Use makefile verbosity (Default)
523
#                       1..2
524
#
525
# Returns         : 
526
#
527
sub SetVerbose
528
{
529
    my ($level) = @_;
530
 
531
    $level = $opt_verbose unless ( $level );
532
    $opt_verbose = $level;
533
    ErrorConfig( 'verbose' => $level);
534
}
535
 
536
 
537
#-------------------------------------------------------------------------------
538
# Function        : DebianFiles
539
#
540
# Description     : Name Debian builder control files
541
#                   May be called multiple times
542
#
543
# Inputs          : Options
544
#                       --Control=file
545
#                       --PreRm=file
546
#                       --PostRm=file
547
#                       --PreInst=file
548
#                       --PostInst=file
4635 dpurdie 549
#                         
407 dpurdie 550
#
551
# Returns         : Nothing
552
#
553
sub DebianFiles
554
{
555
    #
4635 dpurdie 556
    #   Extract names
407 dpurdie 557
    #
558
    Verbose ("Specify Debian Control Files and Scripts");
559
    foreach  ( @_ )
560
    {
4635 dpurdie 561
        if ( m/^--Control=(.+)/i ) {
562
            DebianControlFile('control',$1)
407 dpurdie 563
 
4635 dpurdie 564
        } elsif ( m/^--PreRm=(.+)/i ) {
565
            DebianControlFile('prerm',$1)
407 dpurdie 566
 
4635 dpurdie 567
        } elsif ( m/^--PostRm=(.+)/i ) {
568
            DebianControlFile('postrm',$1)
407 dpurdie 569
 
4635 dpurdie 570
        } elsif ( m/^--PreInst=(.+)/i ) {
571
            DebianControlFile('preinst',$1)
407 dpurdie 572
 
4635 dpurdie 573
        } elsif ( m/^--PostInst=(.+)/i ) {
574
            DebianControlFile('postinst',$1)
407 dpurdie 575
 
576
        } else {
577
            Error ("DebianFiles: Unknown option: $_");
578
        }
579
    }
580
}
581
 
582
#-------------------------------------------------------------------------------
4635 dpurdie 583
# Function        : DebianControlFile 
584
#
585
# Description     : Add special control files to the Debian Installer 
586
#                   Not useful for embedded installers
587
#
588
#                   More general than DebianFiles()
589
#
590
# Inputs          : name            - Target Name
591
#                                     If the name starts with 'package.' then it will be replaced
592
#                                     with the name of the current package
593
#                   file            - Source File Name
4676 dpurdie 594
#                   options         - Options include
595
#                                       --FromPackage
4635 dpurdie 596
#
597
# Returns         : 
598
#
599
sub DebianControlFile
600
{
4676 dpurdie 601
    my ($name, $file, @options) = @_;
602
    my $fromPackage = 0;
4635 dpurdie 603
 
604
    #
4676 dpurdie 605
    #   Process options
606
    foreach ( @options)
607
    {
608
        if (m~^--FromPackage~) {
609
            $fromPackage = 1;
610
        }
611
        else  {
612
            ReportError(("DebianControlFile: Unknown argument: $_"));
613
        }
614
    }
615
    ErrorDoExit();
616
 
617
    #
4635 dpurdie 618
    #   Some control files need to have the package name prepended
619
    #
620
    $name =~ s~^package\.~$opt_name.~;
621
 
622
    #
623
    #   Only allow one file of each type
624
    #       Try to protect the user by testing for names by lowercase
625
    #
626
    my $simpleName = lc($name);
627
    Error("DebianControlFile: Multiple definitions for '$name' not allowed")
628
        if (exists $DebianControlFileNames{$simpleName});
629
 
4676 dpurdie 630
    my $filePath = ResolveFile($fromPackage, $file);
4635 dpurdie 631
 
632
    #
633
    #   Add info to data structures
634
    #
4676 dpurdie 635
    $DebianControlFiles{$name} = $filePath;
4635 dpurdie 636
    $DebianControlFileNames{$simpleName} = $name;
637
}
638
 
639
#-------------------------------------------------------------------------------
640
# Function        : DebianDepends 
641
#
642
# Description     : This directive allows simple dependency information to be  
643
#                   inserted into the control file
644
#
4636 dpurdie 645
#                   Not useful in embedded system
4635 dpurdie 646
#
647
# Inputs          : Entry             - A dependency entry
648
#                   ...               - More entries
649
#                   
650
#
651
# Returns         : Nothing
652
#
653
sub DebianDepends
654
{
655
    push @DependencyList, @_;
656
}
657
 
658
 
659
#-------------------------------------------------------------------------------
407 dpurdie 660
# Function        : PackageDescription
661
#
662
# Description     : Specify the Package Description
663
#                   Keep it short
664
#
665
# Inputs          : $description
666
#
667
# Returns         : 
668
#
669
sub PackageDescription
670
{
671
    ($opt_description) = @_;
672
}
673
 
674
#-------------------------------------------------------------------------------
675
# Function        : MakeSymLink
676
#
677
# Description     : Create a symlink - with error detection
678
#
679
# Inputs          : old_file    - Link Target
680
#                                 Path to the link target
681
#                                 If an ABS path is provided, the routine will
682
#                                 attempt to create a relative link.
683
#                   new_file    - Relative to the output work space
684
#                                 Path to where the 'link' file will be created
685
#                   Options     - Must be last
686
#                                 --NoClean         - Don't play with links
687
#                                 --NoDotDot        - Don't create symlinks with ..
688
#
689
# Returns         : Nothing
690
#
691
sub MakeSymLink
692
{
693
    my $no_clean;
694
    my $no_dot;
695
    my @args;
696
 
697
    #
698
    #   Extract options
699
    #
700
    foreach ( @_ )
701
    {
702
        if ( m/^--NoClean/i ) {
703
            $no_clean = 1;
704
 
705
        } elsif ( m/^--NoDotDot/i ) {
706
            $no_dot = 1;
707
 
708
        } elsif ( m/^--/ ) {
709
            Error ("MakeSymLink: Unknown option: $_");
710
 
711
        } else {
712
            push @args, $_;
713
        }
714
    }
715
 
716
    my ($old_file, $new_file) = @args;
717
 
718
    my $tfile = $DebianWorkDir . '/' . $new_file;
719
    $tfile =~ s~//~/~;
720
    Verbose ("Symlink $old_file -> $new_file" );
721
 
722
    #
723
    #   Create the directory in which the link will be placed
724
    #   Remove any existing file of the same name
725
    #
726
    my $dir = StripFileExt( $tfile );
727
    mkpath( $dir) unless -d $dir;
728
    unlink $tfile;
729
 
730
    #
731
    #   Determine a good name of the link
732
    #   Convert to a relative link in an attempt to prune them
733
    #
734
    my $sfile = $old_file;
735
    unless ( $no_clean )
736
    {
737
        $sfile = CalcRelPath( StripFileExt( $new_file ), $old_file );
738
        $sfile = $old_file if ( $no_dot && $sfile =~ m~^../~ );
739
    }
740
 
741
    my $result = symlink $sfile, $tfile;
742
    Error ("Cannot create symlink. $old_file -> $new_file") unless ( $result );
743
}
744
 
745
#-------------------------------------------------------------------------------
746
# Function        : CopyFile
747
#
748
# Description     : Copy a file to a target dir
749
#                   Used for text files, or files with fixed names
750
#
751
# Inputs          : $src
752
#                   $dst_dir    - Within the output workspace
753
#                   $dst_name   - Output Name [Optional]
754
#                   Options     - Common Copy Options
755
#
756
# Returns         : Full path to destination file
757
#
758
sub CopyFile
759
{
760
    CopyFileCommon( \&ResolveFile, @_ );
761
}
762
 
763
#-------------------------------------------------------------------------------
764
# Function        : CopyBinFile
765
#
766
# Description     : Copy a file to a target dir
767
#                   Used for executable programs. Will look in places where
768
#                   programs are stored.
769
#
770
# Inputs          : $src
771
#                   $dst_dir    - Within the output workspace
772
#                   $dst_name   - Output Name [Optional]
773
#
774
#                   Options:
775
#                       --FromPackage
776
#                       --SoftLink=xxxx
777
#                       --LinkFile=xxxx
778
#
779
#
780
# Returns         : Full path to destination file
781
#
782
sub CopyBinFile
783
{
784
    CopyFileCommon( \&ResolveBinFile, @_ );
785
}
786
 
787
#-------------------------------------------------------------------------------
788
# Function        : CopyLibFile
789
#
790
# Description     : Copy a file to a target dir
791
#                   Used for shared programs. Will look in places where
792
#                   shared libraries are stored.
793
#
794
# Inputs          : $src        - Base for 'realname' (no lib, no extension)
795
#                   $dst_dir    - Within the output workspace
796
#                   $dst_name   - Output Name [Optional, but not suggested]
797
#
798
# Returns         : Full path to destination file
799
#
800
# Notes           : Copying 'lib' files
801
#                   These are 'shared libaries. There is no provision for copying
802
#                   static libraries.
803
#
804
#                   The tool will attempt to copy a well-formed 'realname' library
805
#                   The soname of the library should be constructed on the target
806
#                   platform using ldconfig.
807
#                   There is no provision to copy the 'linker' name
808
#
809
#                   Given a request to copy a library called 'fred', then the
810
#                   well formed 'realname' will be:
811
#                           libfred[P|D|]].so.nnnnn
812
#                   where:
813
#                           nnnn is the library version
814
#                           [P|D|] indicates Production, Debug or None
815
#
816
#                   The 'soname' is held within the realname form of the library
817
#                   and will be created by lsconfig.
818
#
819
#                   The 'linkername' would be libfred[P|D|].so. This is only
820
#                   needed when linking against the library.
821
#
822
#
823
#                   The routine will also recognize Windows DLLs
824
#                   These are of the form fred[P|D|].nnnnn.dll
825
#
826
sub CopyLibFile
827
{
828
    CopyFileCommon( \&ResolveLibFile, @_ );
829
}
830
 
831
#-------------------------------------------------------------------------------
6088 dpurdie 832
# Function        : CopyDebianPackage
833
#
834
# Description     : Copy a Debian Package to a target dir
835
#                   Will look in places where Debian Packages are stored.
836
#
837
# Inputs          : $src        - BaseName for 'Debian Package' (no version, no extension)
838
#                   $dst_dir    - Within the output workspace
839
#                   Optional arguments embedded into the BaseName
840
#                   --Arch=XXXX         - Architecture - if not current
841
#                   --Product=XXXX      - Product - if required
842
#                   --Debug             - If not the current type
843
#                   --Prod              - If not the current type
844
#
845
# Returns         : Full path to destination file
846
#
847
# Notes           : Copying Debian Packages from external packages
848
#
849
#                   The tool will attempt to copy a well-formed debian packages
850
#                   These are:
851
#                   
852
#                       "BaseName_VersionString[_Product]_Arch${PkgType}.deb";
853
#                   
854
#                   Where 'Product' is optional (and rare)
855
#                   Where 'PkgType' is P or D or nothing
856
#                   Where 'Arch' may be 'all'
857
#                   
858
#                   The routine will locate Debian packages in
859
#                       - The root of the package
860
#                       - bin/TARGET[P|D/]
861
#                       - bin/Arch[P|D]
862
#
863
#
864
sub CopyDebianPackage
865
{
6092 dpurdie 866
    CopyFileCommon( \&ResolveDebPackage, '--FromPackage', @_ );
6088 dpurdie 867
}
868
 
869
#-------------------------------------------------------------------------------
407 dpurdie 870
# Function        : CopyFileCommon
871
#
872
# Description     : Common ( internal File Copy )
873
#
874
# Inputs          : $resolver           - Ref to function to resolve source file
875
#                   $src                - Source File Name
876
#                   $dst_dir            - Target Dir
877
#                   $dst_name           - Target Name (optional)
878
#                   Options
879
#                   Options:
880
#                       --FromPackage
6092 dpurdie 881
#                       --FromBuild
407 dpurdie 882
#                       --SoftLink=xxxx
883
#                       --LinkFile=xxxx
4666 dpurdie 884
#                       --ConfigFile
407 dpurdie 885
#
886
# Returns         : 
887
#
888
sub CopyFileCommon
889
{
890
    my $from_package = 0;
891
    my $isa_linkfile = 0;
4666 dpurdie 892
    my $isa_configFile = 0;
407 dpurdie 893
    my @llist;
894
    my @args;
895
 
896
    #
897
    #   Parse options
898
    #
899
    foreach ( @_ )
900
    {
901
        if ( m/^--FromPackage/ ) {
902
            $from_package = 1;
903
 
6092 dpurdie 904
        } elsif ( m/^--FromBuild/ ) {
905
            $from_package = 0;
906
 
407 dpurdie 907
        } elsif ( m/^--LinkFile/ ) {
908
            $isa_linkfile = 1;
909
 
4666 dpurdie 910
        } elsif ( m/^--ConfFile/i ) {
911
            $isa_configFile = 1;
912
 
407 dpurdie 913
        } elsif ( m/^--SoftLink=(.+)/ ) {
914
            push @llist, $1;
915
 
916
        } elsif ( m/^--/ ) {
917
            Error ("FileCopy: Unknown option: $_");
918
 
919
        } else {
920
            push @args, $_;
921
        }
922
    }
923
 
924
    #
925
    #   Extract non-options.
926
    #   These are the bits that are left over
927
    #
928
    my ($resolver, $src, $dst_dir, $dst_name ) = @args;
929
 
930
    #
931
    #   Clean up dest_dir. Must start with a / and not end with one
932
    #
933
    $dst_dir = "/$dst_dir/";
934
    $dst_dir =~ s~/+~/~g;
935
    $dst_dir =~ s~/$~~;
936
 
937
    Verbose ("CopyFile: $src, $dst_dir, " . ($dst_name || ''));
938
    foreach $src ( &$resolver( $from_package, $src ) )
939
    {
940
        my $dst_fname = $dst_name ? $dst_name : StripDir($src);
941
        my $dst_file = "$dst_dir/$dst_fname";
942
        Verbose ("CopyFile: Copy $src, $dst_file" );
943
 
944
 
945
        #
946
        #   LinkFiles are special
947
        #   They get concatenated to any existing LINKS File
948
        #
949
        if ( $isa_linkfile )
950
        {
951
            CatFile ( $src, "$dst_dir/.LINKS" );
952
        }
953
        else
954
        {
955
            mkpath( "$DebianWorkDir$dst_dir", 0, 0775);
956
            unlink ("$DebianWorkDir$dst_file");
957
            System ('cp','-f', $src, "$DebianWorkDir$dst_file" );
958
 
959
            foreach my $lname ( @llist )
960
            {
961
                $lname = $dst_dir . '/' . $lname unless ( $lname =~ m ~^/~ );
962
                MakeSymLink( $dst_file ,$lname);
963
            }
964
        }
4666 dpurdie 965
 
966
        #
967
        #   ConfigFiles are marked so that they can be handled by the debain installer
968
        #
969
        if ($isa_configFile)
970
        {
971
            push @ConfigList, $dst_file;
972
        }
407 dpurdie 973
    }
974
}
975
 
976
#-------------------------------------------------------------------------------
977
# Function        : CopyDir
978
#
979
# Description     : Copy a directory to a target dir
980
#
981
# Inputs          : $src_dir    - Local to the user
982
#                                 Symbolic Name
983
#                   $dst_dir    - Within the output workspace
984
#                   Options
4714 dpurdie 985
#                       --Merge                 - Don't delete first
986
#                       --Source=Name           - Source via Symbolic Name
987
#                       --FromPackage           - Source via package roots
988
#                       --NoIgnoreDbgFiles      - Do not ignore .dbg and .debug files in dir copy
989
#                       --IfPresent             - Not an error if the path cannot be found
990
#                       --ConfFile              - Mark transferred files as config files
991
#                       --Flatten               - Copy all to one directory
992
#                       --FilterOut=xxx         - Ignore files. DOS Wildcard
993
#                       --FilterOutRe=xxx       - Ignore files. Regular expression name
994
#                       --FilterOutDir=xxx      - Ignore directories. DOS Wilcard
995
#                       --FilterOutDirRe=xxx    - Ignore directories. Regular expression name
996
#                       --SkipTLF               - Ignore files in the Top Level Directory
997
#                       --NoRecurse             - Only process files in the Top Level Directory
998
#                       --FilterIn=xxx          - Include files. DOS Wildcard
999
#                       --FilterInRe=xxx        - Include files. Regular expression name
1000
#                       --FilterInDir=xxx       - Include directories. DOS Wilcard
1001
#                       --FilterInDirRe=xxx     - Include directories. Regular expression name
407 dpurdie 1002
#
1003
# Returns         :
1004
#
1005
sub CopyDir
1006
{
1007
    my ($src_dir, $dst_dir, @opts) = @_;
1008
    my $opt_base;
411 dpurdie 1009
    my $from_interface = 0;
4152 dpurdie 1010
    my $ignoreDbg = 1;
1011
    my $ignoreNoDir;
1012
    my $user_src_dir = $src_dir;
1013
    my $opt_source;
1014
    my $opt_package;
4714 dpurdie 1015
    my @fileList;
1016
    my $isFiltered;
407 dpurdie 1017
 
4714 dpurdie 1018
    #
1019
    #   Setup the basic copy options
1020
    #       May be altered as we parse user options
1021
    #
1022
    my %copyOpts;
1023
    $copyOpts{'IgnoreDirs'} = ['.svn', '.git', '.cvs', '.hg'];
1024
    $copyOpts{'Ignore'} = ['.gbedir', '_gbedir'];
1025
    $copyOpts{'Log'} = 1 if ( $opt_verbose > 1 );
4740 dpurdie 1026
    $copyOpts{'DeleteFirst'} = 1;
4714 dpurdie 1027
 
407 dpurdie 1028
    $dst_dir = $DebianWorkDir . '/' . $dst_dir;
1029
    $dst_dir =~ s~//~/~;
1030
 
1031
    #
4152 dpurdie 1032
    #   Scan and collect user options
407 dpurdie 1033
    #
1034
    foreach  ( @opts )
1035
    {
4152 dpurdie 1036
        Verbose2 ("CopyDir: $_");
407 dpurdie 1037
        if ( m/^--Merge/ ) {
4740 dpurdie 1038
            $copyOpts{'DeleteFirst'} = 0;
4152 dpurdie 1039
 
407 dpurdie 1040
        } elsif ( m/^--Source=(.+)/ ) {
425 dpurdie 1041
            Error ("Source directory can only be specified once")
4152 dpurdie 1042
                if ( defined $opt_source );
1043
            $opt_source = $1;
425 dpurdie 1044
 
4152 dpurdie 1045
        } elsif ( m/^--FromPackage/ ) {
1046
            Error ("FromPackage can only be specified once")
1047
                if ( defined $opt_package );
1048
            $opt_package = 1;
1049
 
1050
        } elsif ( m/^--NoIgnoreDbgFiles/ ) {
1051
            $ignoreDbg = 0;
1052
 
1053
        } elsif ( m/^--IfPresent/ ) {
1054
            $ignoreNoDir = 1;
407 dpurdie 1055
 
4714 dpurdie 1056
        } elsif ( m/^--ConfFile/i ) {
1057
            $copyOpts{'FileList'} = \@fileList;
1058
 
1059
        } elsif ( m/^--Flatten/i ) {
1060
            $copyOpts{'Flatten'} = 1;
1061
 
1062
        } elsif ( m/^--FilterOut=(.+)/i ) {
1063
            push (@{$copyOpts{'Ignore'}}, $1);
1064
            $isFiltered = 1;
1065
 
1066
        } elsif ( m/^--FilterOutRe=(.+)/i ) {
1067
            push (@{$copyOpts{'IgnoreRE'}}, $1);
1068
            $isFiltered = 1;
1069
 
1070
        } elsif ( m/^--FilterOutDir=(.+)/i ) {
1071
            push (@{$copyOpts{'IgnoreDirs'}}, $1);
1072
            $isFiltered = 1;
1073
 
1074
        } elsif ( m/^--FilterOutDirRe=(.+)/i ) {
1075
            push (@{$copyOpts{'IgnoreDirsRE'}}, $1);
1076
            $isFiltered = 1;
1077
 
1078
        } elsif ( m/^--FilterIn=(.+)/i ) {
1079
            push (@{$copyOpts{'Match'}}, $1);
1080
            $isFiltered = 1;
1081
 
1082
        } elsif ( m/^--FilterInRe=(.+)/i ) {
1083
            push (@{$copyOpts{'MatchRE'}}, $1);
1084
            $isFiltered = 1;
1085
 
1086
        } elsif ( m/^--FilterInDir=(.+)/i ) {
1087
            push (@{$copyOpts{'MatchDirs'}}, $1);
1088
            $isFiltered = 1;
1089
 
1090
        } elsif ( m/^--FilterInDirRe=(.+)/i ) {
1091
            push (@{$copyOpts{'MatchDirsRE'}}, $1);
1092
            $isFiltered = 1;
1093
 
1094
        } elsif ( m/^--SkipTLF$/i ) {
1095
            $copyOpts{'SkipTLF'} = 1;
1096
 
1097
        } elsif ( m/^--NoRecurse$/i ) {
1098
            $copyOpts{'NoSubDirs'} = 1;
1099
 
4152 dpurdie 1100
        } else {
1101
            Error ("CopyDir: Unknown option: $_" );
1102
        }
1103
    }
411 dpurdie 1104
 
4152 dpurdie 1105
    #
1106
    #   All options have been gathered. Now process some of them
1107
    #
1108
    Error ("CopyDir: Cannot use both --Source and --FromPackage: $src_dir") if ($opt_source && $opt_package);
425 dpurdie 1109
 
4152 dpurdie 1110
    #
1111
    #   Convert a symbolic path into a physical path
1112
    #
1113
    if ($opt_source)
1114
    {
1115
        Verbose2 ("CopyDir: Determine Source: $opt_source");
425 dpurdie 1116
 
4152 dpurdie 1117
        $opt_source = lc($opt_source);
1118
        my %CopyDirSymbolic = (
1119
            'interfaceincdir'   => $opt_interfaceincdir,
1120
            'interfacelibdir'   => $opt_interfacelibdir,
1121
            'interfacebindir'   => $opt_interfacebindir,
1122
            'libdir'            => $opt_libdir,
1123
            'bindir'            => $opt_bindir,
1124
            'localincdir'       => $opt_localincdir,
1125
            'locallibdir'       => $opt_locallibdir,
1126
            'localbindir'       => $opt_localbindir,
1127
            'packagebindir'     => $opt_pkgbindir,
1128
            'packagelibdir'     => $opt_pkglibdir,
1129
            'packagepkgdir'     => $opt_pkgpkgdir,
1130
            'packagedir'        => $opt_pkgdir,
1131
        );
425 dpurdie 1132
 
4152 dpurdie 1133
        if ( exists $CopyDirSymbolic{$opt_source} )
1134
        {
1135
            $opt_base = $CopyDirSymbolic{$opt_source};
425 dpurdie 1136
 
1137
            #
4152 dpurdie 1138
            #   If sourceing from interface, then follow
1139
            #   symlinks in the copy. All files will be links anyway
425 dpurdie 1140
            #
1141
            $from_interface = 1
4152 dpurdie 1142
                if ( $opt_source =~ m~^interface~ );
1143
        }
1144
        else
1145
        {
1146
            DebugDumpData ("CopyDirSymbolic", \%CopyDirSymbolic);
1147
            Error ("CopyDir: Unknown Source Name: $opt_source" );
1148
        }
1149
    }
425 dpurdie 1150
 
4152 dpurdie 1151
    #
1152
    #   Locate the path within an external package
1153
    #
1154
    if ($opt_package)
1155
    {
1156
        Verbose2 ("CopyDir: FromPackage: $src_dir");
4147 dpurdie 1157
 
4152 dpurdie 1158
        my @path;
1159
        foreach my $entry ( getPackageList() )
1160
        {
1161
            my $base = $entry->getBase(3);
1162
            next unless ( defined $base );
1163
            if ( -d $base . '/' . $src_dir )
1164
            {
1165
                push @path, $base;
1166
                $from_interface = 1
1167
                    if ( $entry->{'TYPE'} eq 'interface' );
1168
            }
407 dpurdie 1169
        }
4152 dpurdie 1170
 
1171
        if ( $#path < 0 )
1172
        {
1173
            Error ("CopyDir: Cannot find source dir in any package: $user_src_dir") unless ($ignoreNoDir);
1174
            Message ("CopyDir: Optional path not found: $user_src_dir");
1175
            return;
1176
        }
1177
 
1178
        Error ("CopyDir: Requested path found in mutiple packages: $user_src_dir",
1179
                @path ) if ( $#path > 0 );
1180
        $opt_base = pop @path;
1181
 
1182
        #
1183
        #   If sourceing from interface, then follow symlinks in the copy.
1184
        #   All files will be links anyway
1185
        #
1186
        #   This is a very ugly test for 'interface'
1187
        #
1188
        $from_interface = 1
1189
            if ( $opt_base =~ m~/interface/~ );
1190
 
407 dpurdie 1191
    }
1192
 
4152 dpurdie 1193
    #
1194
    #   Create the full source path
4714 dpurdie 1195
    #   May be: from a package, from a known directory, from a local directory
4152 dpurdie 1196
    #
1197
 
407 dpurdie 1198
    $src_dir = $opt_base . '/' . $src_dir if ( $opt_base );
1199
    $src_dir =~ s~//~/~g;
1200
    $src_dir =~ s~/$~~;
1201
 
1202
    Verbose ("CopyDir: $src_dir, $dst_dir");
4152 dpurdie 1203
    unless ( -d $src_dir )
1204
    {
1205
        Error ("CopyDir: Directory not found: $user_src_dir") unless ($ignoreNoDir);
1206
        Message ("CopyDir: Optional path not found: $user_src_dir");
1207
        return;
1208
    }
407 dpurdie 1209
 
1210
    #
4714 dpurdie 1211
    #   Continue to configure the copy options
407 dpurdie 1212
    #
4147 dpurdie 1213
    push (@{$copyOpts{'Ignore'}}, '*.debug', '*.dbg') if $ignoreDbg;
423 dpurdie 1214
    $copyOpts{'DuplicateLinks'} = 1 unless ( $from_interface );
4714 dpurdie 1215
    $copyOpts{'EmptyDirs'} = 1 unless ($isFiltered);
407 dpurdie 1216
 
1217
    #
423 dpurdie 1218
    #   Transfer the directory
407 dpurdie 1219
    #
423 dpurdie 1220
    JatsCopy::CopyDir ( $src_dir, $dst_dir, \%copyOpts );
407 dpurdie 1221
 
1222
    #
4714 dpurdie 1223
    #   If requested, mark files as config files
1224
    #   Must remove the DebianWorkDir prefix
1225
    #
1226
    if(@fileList)
1227
    {
1228
        Verbose ("Mark all transfered files as ConfFiles");
1229
        my $removePrefix = length ($DebianWorkDir);
1230
        foreach my $file (@fileList)
1231
        {
1232
            push @ConfigList, substr($file, $removePrefix);
1233
        }
1234
    }
1235
 
1236
    #
407 dpurdie 1237
    #   Expand link files that may have been copied in
1238
    #
1239
    Verbose ("Locate LINKFILES in $DebianWorkDir");
1240
    ExpandLinkFiles();
1241
}
1242
 
1243
#-------------------------------------------------------------------------------
1244
# Function        : AddInitScript
1245
#
1246
# Description     : Add an Init Script to the target
1247
#                   Optionally create start and stop links
1248
#
1249
# Inputs          : $script     - Name of the init script
1250
#                   $start      - Start Number
1251
#                   $stop       - Stop Number
1252
#                   Options:
1253
#                       --NoCopy        - Don't copy the script, just add links
1254
#                       --Afc           - Place in AFC init area
1255
#                       --FromPackage   - Source is in a package
1256
#
1257
# Returns         : 
1258
#
1259
sub AddInitScript
1260
{
1261
    my $no_copy;
1262
    my $basedir = "";
1263
    my @args;
1264
    my $from_package = 0;
1265
 
4302 dpurdie 1266
    # This directive is only available on the VIX platforms
1267
    #   Kludgey test - at the moment
407 dpurdie 1268
    #
4302 dpurdie 1269
    if ($opt_pkgarch =~ m~i386~)
1270
    {
1271
        Error ("AddInitScript is not supported on this platform"); 
1272
    }
1273
 
1274
    #
407 dpurdie 1275
    #   Process and Remove options
1276
    #
1277
    foreach  ( @_ )
1278
    {
1279
        if ( m/^--NoCopy/ ) {
1280
            $no_copy = 1;
1281
 
1282
        } elsif ( m/^--Afc/ ) {
1283
            $basedir = "/afc";
1284
 
1285
        } elsif ( m/^--FromPackage/ ) {
1286
            $from_package = 1;
1287
 
1288
        } elsif ( m/^--/ ) {
1289
            Error ("AddInitScript: Unknown option: $_");
1290
 
1291
        } else {
1292
            push @args, $_;
1293
 
1294
        }
1295
    }
1296
 
1297
    my( $script, $start, $stop ) = @args;
1298
    Error ("No script file specified") unless ( $script );
1299
    Warning("AddInitScript: No start or stop index specified") unless ( $start || $stop );
1300
    Verbose ("AddInitScript: $script, " . ($start || 'No Start') . ", " . ($stop || 'No Stop'));
1301
    $script = ResolveFile($from_package, $script );
1302
 
1303
    my $tdir = $basedir . "/etc/init.d/init.d";
1304
    my $base = StripDir($script);
1305
 
1306
    CopyFile( $script, $tdir ) unless $no_copy;
1307
 
1308
    my $link;
1309
    if ( $start )
1310
    {
1311
        $link = sprintf ("${basedir}/etc/init.d/S%2.2d%s", $start, $base );
1312
        MakeSymLink( "$tdir/$base", $link);
1313
    }
1314
 
1315
    if ( $stop )
1316
    {
1317
        $link = sprintf ("${basedir}/etc/init.d/K%2.2d%s", $stop, $base );
1318
        MakeSymLink( "$tdir/$base", $link);
1319
    }
1320
}
1321
 
1322
#-------------------------------------------------------------------------------
1323
# Function        : CatFile
1324
#
1325
# Description     : Copy a file to the end of a file
1326
#
1327
# Inputs          : $src
1328
#                   $dst    - Within the output workspace
1329
#
1330
# Returns         :
1331
#
1332
sub CatFile
1333
{
1334
    my ($src, $dst) = @_;
1335
 
1336
    $dst = $DebianWorkDir . '/' . $dst;
1337
    $dst =~ s~//~/~;
1338
    Verbose ("CatFile: $src, $dst");
1339
    $src = ResolveFile(0, $src );
1340
 
1341
    open (SF, '<', $src)  || Error ("CatFile: Cannot open $src");
1342
    open (DF, '>>', $dst) || Error ("CatFile: Cannot create:$dst");
1343
    while ( <SF> )
1344
    {
1345
        print DF $_;
1346
    }
1347
    close (SF);
1348
    close (DF);
1349
}
1350
 
1351
#-------------------------------------------------------------------------------
1352
# Function        : EchoFile
1353
#
1354
# Description     : Echo simple text to a file
1355
#
1356
# Inputs          : $file   - Within the output workspace
1357
#                   $text
1358
#
1359
# Returns         : 
1360
#
1361
sub EchoFile
1362
{
1363
    my ($file, $text) = @_;
1364
    Verbose ("EchoFile: $file");
1365
 
1366
    $file = $DebianWorkDir . '/' . $file;
1367
    $file =~ s~//~/~;
1368
 
1369
    unlink $file;
1370
    open (DT, ">", $file ) || Error ("Cannot create $file");
1371
    print DT  $text || Error ("Cannot print to $file");
1372
    close DT;
1373
}
1374
 
1375
#-------------------------------------------------------------------------------
4640 dpurdie 1376
# Function        : ConvertFiles
1377
#
1378
# Description     : This sub-routine is used to remove all carrage return\line
1379
#                   feeds from a line and replace them with the platform
1380
#                   specific equivalent chars.
1381
#
1382
#                   We let PERL determine what characters are written to the
1383
#                   file base on the  platform you are running on.
1384
#
1385
#                   i.e. LF    for unix
1386
#                        CR\LF for win32
1387
#
1388
# Inputs          : outPath                 - Output directory
1389
#                   flist                   - List of files in that directory
1390
#                   or
1391
#                   SearchOptions           - Search options to find files
1392
#                                           --Recurse
1393
#                                           --NoRecurse
1394
#                                           --FilterIn=xxx
1395
#                                           --FilterInRe=xxx
1396
#                                           --FilterOut=xxx
1397
#                                           --FilterOutRe=xxx
1398
#                   Common options
1399
#                                           --Dos
1400
#                                           --Unix
1401
#
1402
#
1403
# Returns         : 1
1404
#
1405
sub ConvertFiles
1406
{
1407
    my @uargs;
1408
    my $lineEnding = "\n";
1409
    my ($dosSet, $unixSet);
1410
    my $search =  JatsLocateFiles->new( '--NoRecurse' );
1411
 
1412
    #
1413
    #   Process user arguments extracting options
1414
    #
1415
    foreach  ( @_ )
1416
    {
1417
        if ( m~^--Recurse~ ) {
1418
            $search->recurse(1);
1419
 
1420
        } elsif ( m~^--NoRecurse~) {
1421
            $search->recurse(0);
1422
 
1423
        } elsif ( /^--FilterOut=(.*)/ ) {
1424
            $search->filter_out($1);
1425
 
1426
        } elsif ( /^--FilterOutRe=(.*)/ ) {
1427
            $search->filter_out_re($1);
1428
 
1429
        } elsif ( /^--FilterIn=(.*)/ ) {
1430
            $search->filter_in($1);
1431
 
1432
        } elsif ( /^--FilterInRe=(.*)/ ) {
1433
            $search->filter_in_re($1);
1434
 
1435
        } elsif ( m~^--Dos~) {
1436
            $lineEnding = "\r\n";
1437
            $dosSet = 1;
1438
 
1439
        } elsif ( m~^--Unix~) {
1440
            $lineEnding = "\n";
1441
            $unixSet = 1;
1442
 
1443
        } elsif ( m~^--~) {
4641 dpurdie 1444
            Error ("ConvertFiles: Unknown option: $_");
4640 dpurdie 1445
 
1446
        } else {
1447
            push @uargs, $_;
1448
        }
1449
    }
1450
 
1451
    #
1452
    #   Process non-option arguments
1453
    #       - Base dir
1454
    #       - List of files
1455
    #
1456
    my ($outPath, @flist) = @uargs;
1457
    Error ("ConvertFiles: Target Dir must be specified" ) unless ( $outPath );
1458
 
1459
    #
1460
    #   Sanity Tests
1461
    #
1462
    Error ("ConvertFiles: --Dos and --Unix are mutually exclusive" ) if ( $dosSet && $unixSet );
1463
 
1464
 
1465
    #
1466
    # Convert output path to physical path
1467
    #
1468
    my $topDir = catdir($DebianWorkDir, $outPath);
1469
    Verbose("ConvertFiles: topDir: $topDir");
1470
    Error ("ConvertFiles: Path does not exist", $topDir) unless ( -e $topDir );
1471
    Error ("ConvertFiles: Path is not a directory", $topDir) unless ( -d $topDir );
1472
 
1473
    #
1474
    #   Need to determine if we are searching or simply using a file list
1475
    #   There are two forms of the functions. If any of the search options have
1476
    #   been used then we assume that we are searchine
1477
    #
1478
    if ( $search->has_filter() )
1479
    {
1480
        Error ("ConvertFiles: Cannot mix search options with named files") if ( @flist );
1481
        @flist = $search->search($topDir);
1482
    }
1483
    Error ("ConvertFiles: No files specified") unless ( @flist );
1484
 
1485
    #
1486
    #   Process all named files
1487
    #
1488
    foreach my $file ( @flist )
1489
    {
1490
 
1491
        # this is our file that we want to clean.
1492
        my ($ifileLoc) = "$topDir/$file";
1493
        my ($tfileLoc) = "$topDir/$file\.tmp";
4641 dpurdie 1494
        Verbose("ConvertFiles: $file");
4640 dpurdie 1495
 
1496
        # we will check to see if the file exists.
1497
        #
1498
        my $ifile;
1499
        my $tfile;
1500
        if ( -f "$ifileLoc" )
1501
        {
1502
            open ($ifile, "< $ifileLoc" ) or
1503
                Error("Failed to open file [$ifileLoc] : $!");
1504
 
1505
            open ($tfile, "> $tfileLoc" ) or
1506
                Error("Failed to open file [$tfileLoc] : $!");
1507
            binmode $tfile;
1508
 
1509
            while ( <$ifile> ) 
1510
            {
1511
                s~[\n\r]+$~~;               # Chomp
1512
                print $tfile "$_" . $lineEnding;
1513
            }
1514
        }
1515
        else
1516
        {
1517
            Error("ConvertFiles [$ifileLoc] does not exist.");
1518
        }
1519
 
1520
        close $ifile;
1521
        close $tfile;
1522
 
1523
 
1524
        # lets replace our original file with the new one
1525
        #
1526
        if(File::Copy::move("$tfileLoc", "$ifileLoc"))
1527
        {
1528
            Verbose2("ConvertFiles: Renamed [$tfileLoc] to [$ifileLoc] ...");
1529
        }
1530
        else
1531
        {
1532
            Error("ConvertFiles: Failed to rename file [$tfileLoc] to [$ifileLoc]: $!");
1533
        }
1534
    }
1535
 
1536
    return 1;
1537
}
1538
 
4641 dpurdie 1539
#----------------------------------------------------------------------------
1540
# Function        : ReplaceTags
1541
#
1542
# Description     : This sub-routine is used to replace Tags in one or more files
1543
#
1544
# Inputs          : outPath                 - Output directory
1545
#                   flist                   - List of files in that directory
1546
#                   or
1547
#                   SearchOptions           - Search options to find files
1548
#                                           --Recurse
1549
#                                           --NoRecurse
1550
#                                           --FilterIn=xxx
1551
#                                           --FilterInRe=xxx
1552
#                                           --FilterOut=xxx
1553
#                                           --FilterOutRe=xxx
1554
#                   Common options
1555
#                                           --Tag=Tag,Replace
1556
#                                           
1557
#
1558
# Returns         : 1
1559
#
1560
sub ReplaceTags
1561
{
1562
    my @uargs;
1563
    my $search =  JatsLocateFiles->new( '--NoRecurse' );
1564
    my @tagsList;
1565
    my $tagSep = ',';
1566
    my @tagOrder;
1567
    my %tagData;
1568
 
1569
    #
1570
    #   Process user arguments extracting options
1571
    #
1572
    foreach  ( @_ )
1573
    {
1574
        if ( m~^--Recurse~ ) {
1575
            $search->recurse(1);
1576
 
1577
        } elsif ( m~^--NoRecurse~) {
1578
            $search->recurse(0);
1579
 
1580
        } elsif ( /^--FilterOut=(.*)/ ) {
1581
            $search->filter_out($1);
1582
 
1583
        } elsif ( /^--FilterOutRe=(.*)/ ) {
1584
            $search->filter_out_re($1);
1585
 
1586
        } elsif ( /^--FilterIn=(.*)/ ) {
1587
            $search->filter_in($1);
1588
 
1589
        } elsif ( /^--FilterInRe=(.*)/ ) {
1590
            $search->filter_in_re($1);
1591
 
1592
        } elsif ( m~^--Tag=(.*)~) {
1593
            push @tagsList, $1;
1594
 
1595
        } elsif ( m~^--~) {
1596
            Error ("ReplaceTags: Unknown option: $_");
1597
 
1598
        } else {
1599
            push @uargs, $_;
1600
        }
1601
    }
1602
 
1603
    #
1604
    #   Process non-option arguments
1605
    #       - Base dir
1606
    #       - List of files
1607
    #
1608
    my ($outPath, @flist) = @uargs;
1609
    Error ("ReplaceTags: Target Dir must be specified" ) unless ( $outPath );
1610
 
1611
    #
1612
    #   Sanity Tests
1613
    #
1614
    Error ("ReplaceTags: No tags specified" ) unless ( @tagsList );
1615
 
1616
    #
1617
    # Convert output path to physical path
1618
    #
1619
    my $topDir = catdir($DebianWorkDir, $outPath);
1620
    Verbose("ReplaceTags: topDir: $topDir");
1621
    Error ("ReplaceTags: Path does not exist", $topDir) unless ( -e $topDir );
1622
    Error ("ReplaceTags: Path is not a directory", $topDir) unless ( -d $topDir );
1623
 
1624
    #
1625
    #   Convert Tags into pairs for latter use
1626
    #
1627
    my $sep = quotemeta ($tagSep );
1628
    foreach my $tag ( @tagsList )
1629
    {
4714 dpurdie 1630
        my ($tname,$tvalue) = split ( $sep, $tag, 2 );
4641 dpurdie 1631
        Error ("No tag value in: $tag" ) unless ( defined $tvalue );
1632
        Error ("Duplicate Tag: $tname" ) if ( exists $tagData{$tname} );
1633
        Verbose ("Tag: $tname :: $tvalue");
1634
        push @tagOrder, $tname;
1635
        $tagData{$tname} = $tvalue;
1636
    }
1637
 
1638
    #
1639
    #   Need to determine if we are searching or simply using a file list
1640
    #   There are two forms of the functions. If any of the search options have
1641
    #   been used then we assume that we are searchine
1642
    #
1643
    if ( $search->has_filter() )
1644
    {
1645
        Error ("ReplaceTags: Cannot mix search options with named files") if ( @flist );
1646
        @flist = $search->search($topDir);
1647
    }
1648
    Error ("ReplaceTags: No files specified") unless ( @flist );
1649
 
1650
    #
1651
    #   Process all named files
1652
    #
1653
    foreach my $file ( @flist )
1654
    {
1655
 
1656
        # this is our file that we want to clean.
1657
        my ($ifileLoc) = "$topDir/$file";
1658
        my ($tfileLoc) = "$topDir/$file\.tmp";
1659
        Verbose("ReplaceTags: $file");
1660
 
1661
        # we will check to see if the file exists.
1662
        #
1663
        my $ifile;
1664
        my $tfile;
1665
        if ( -f "$ifileLoc" )
1666
        {
1667
            open ($ifile, "< $ifileLoc" ) or
1668
                Error("Failed to open file [$ifileLoc] : $!");
1669
 
1670
            open ($tfile, "> $tfileLoc" ) or
1671
                Error("Failed to open file [$tfileLoc] : $!");
1672
 
1673
            while ( <$ifile> ) 
1674
            {
1675
                s~[\n\r]+$~~;               # Chomp
1676
 
1677
                #
1678
                #   Perform tag replacement
1679
                #
1680
                foreach my $tag ( @tagOrder )
1681
                {
1682
                    my $value = $tagData{$tag};
1683
                    if ( s~$tag~$value~g )
1684
                    {
1685
                        Verbose2("Replaced: $tag with $value");
1686
                    }
1687
                }
1688
 
1689
                print $tfile "$_\n";
1690
            }
1691
        }
1692
        else
1693
        {
1694
            Error("ReplaceTags [$ifileLoc] does not exist.");
1695
        }
1696
 
1697
        close $ifile;
1698
        close $tfile;
1699
 
1700
 
1701
        # lets replace our original file with the new one
1702
        #
1703
        if(File::Copy::move("$tfileLoc", "$ifileLoc"))
1704
        {
1705
            Verbose2("ReplaceTags: Renamed [$tfileLoc] to [$ifileLoc] ...");
1706
        }
1707
        else
1708
        {
1709
            Error("ReplaceTags: Failed to rename file [$tfileLoc] to [$ifileLoc]: $!");
1710
        }
1711
    }
1712
 
1713
    return 1;
1714
}
1715
 
4640 dpurdie 1716
#-------------------------------------------------------------------------------
407 dpurdie 1717
# Function        : SetFilePerms
1718
#
1719
# Description     : Set file permissions on one or more files or directories
1720
#
1721
# Inputs          : $perm           - Perm Mask
1722
#                   @paths          - List of paths/files to process
1723
#                   Options
1724
#                       --Recurse   - Recurse subdirs
1725
#
1726
# Returns         : 
1727
#
1728
sub SetFilePerms
1729
{
1730
 
1731
    my @args;
1732
    my $perms;
1733
    my $recurse = 0;
1734
 
1735
    #
1736
    #   Process and Remove options
1737
    #
1738
    foreach  ( @_ )
1739
    {
1740
        if ( m/^--Recurse/ ) {
1741
            $recurse = 1;
1742
 
1743
        } elsif ( m/^--/ ) {
1744
            Error ("SetFilePerms: Unknown option: $_");
1745
 
1746
        } else {
1747
            push @args, $_;
1748
 
1749
        }
1750
    }
1751
 
1752
    $perms = shift @args;
1753
    Error ("SetFilePerms: No Permissions" ) unless ( $perms );
1754
 
1755
    foreach my $path ( @args )
1756
    {
1757
        Verbose ("Set permissions; $perms, $path");
1758
        my $full_path = $DebianWorkDir . '/' . $path;
1759
        if ( -f $full_path )
1760
        {
1761
            System ('chmod', $perms, $full_path );
1762
        }
1763
        elsif ( -d $full_path )
1764
        {
1765
            System ('chmod', '-R', $perms, $full_path ) if ($recurse);
1766
            System ('chmod', $perms, $full_path ) unless ($recurse);
1767
        }
1768
        else
1769
        {
1770
            Warning("SetFilePerms: Path not found: $path");
1771
        }
1772
    }
1773
}
1774
 
1775
#-------------------------------------------------------------------------------
4636 dpurdie 1776
# Function        : SetPermissions 
1777
#
1778
# Description     : Called to set permissions of files/dirs in a directory structure.
1779
#                   With no options sets DirTag and all files/dirs in it to perms
1780
#   
1781
# Inputs          : path        - The directory tag to start setting permissions on
1782
#                   Options     - See below
1783
#       
1784
#   Required Options:
1785
#       One or both of
1786
#               --FilePerms=    Sets the permissions of files to this permission.
1787
#                               If not supplied then no files have their permissions changed
1788
#               --DirPerms=     Sets the permissions of directories to this permission
1789
#                               If not supplied then no directories have their permissions changed
1790
#       OR
1791
#               --Perms=        Sets the permissions of both files and directories to this permissions
1792
#                               Equivalent to supplying both --FilePerms=X && --DirPerms=X
1793
#               
1794
#   Options:                    
1795
#               --RootOnly      Only sets the permissions on the 'path' directory/file, 
1796
#                               all other options ignored
1797
#               --SkipRoot      Does not set permissions on the 'path' directory/file, 
1798
#                               obviously mutually exlusive with --RootOnly
1799
#   
1800
#       Any option supported by JatsLocateFiles. 
1801
#       Some of these include:
1802
#               
1803
#               --Recurse       Recurse the directory tree.  Does a depth first recurse so that all 
1804
#                               dir entries are processed before the dir itself (default)
1805
#               --NoRecurse     Dont recurse
1806
#               --FilterIn=     Apply permissions to files/directories that matches this value.
1807
#               --FilterInRe=   Perl RE's can be used (Not Shell wildcards) and this option
1808
#                               can be supplied mulitple times
1809
#               --FilterOut=    Dont apply permissions to any files/directories matching this value
1810
#               --FilterOutRe=  Perl RE's can be used (Not Shell wildcards) and this option
1811
#                               can be supplied mulitple times
1812
#               
1813
#                               FilterIn is applied before FilterOut.  If Recurse is specified 
1814
#                               the directory will be recursed regardless of these filters, however
1815
#                               the filter will be applied when it comes time to chmod the dir 
1816
#
1817
#------------------------------------------------------------------------------
1818
sub SetPermissions
1819
{
1820
    my ( $path, $filePerms, $dirPerms, $someDone );
1821
    my ( $rootOnly, $skipRoot ) = ( 0, 0 );
1822
 
1823
    my $search =  JatsLocateFiles->new( '--Recurse', '--DirsToo' );
1824
 
1825
    foreach ( @_ )
1826
    {
1827
        if ( m/^--Perms=(.*)/ ) {
1828
            $filePerms = $1;
1829
            $dirPerms = $1;
1830
 
1831
        } elsif (m/^--FilePerms=(.*)/ )  {
1832
            $filePerms = $1;
1833
 
1834
        } elsif ( m/^--DirPerms=(.*)/ )  {
1835
            $dirPerms = $1;
1836
 
1837
        }  elsif ( m/^--RootOnly/ ) {
1838
            $rootOnly = 1;
1839
 
1840
        } elsif ( m/^--SkipRoot/ )  {
1841
            $skipRoot = 1;
1842
 
1843
        } elsif ( m/^--Filter/ && $search->option( $_ ) ) {
1844
            Verbose2 ("Search Option: $_" );
1845
 
1846
        } elsif ( m/^--Recurse|--NoRecurse/ && $search->option( $_ ) ) {
1847
            Verbose2 ("Search Option: $_" );
1848
 
1849
        } elsif (m/^--/ ) {
1850
            Error ("SetPermissions: Unknown option: $_");
1851
 
1852
        } else  {
1853
            Error("SetPermissions 'path' already set", "Path: $_") if ( $path );
1854
            $path = $_;
1855
        }
1856
    }
1857
 
1858
    #
1859
    #   Sanity test
1860
    #
1861
    Error("SetPermissions called with out a 'path' parameter") if ( !defined($path) );
1862
    Error("SetPermissions called with out any Permissions specified") if ( !defined($filePerms) && !defined($dirPerms) );
1863
    Error("SetPermissions: Options --RootOnly & --SkipRoot are mutually exclusive" ) if ( $rootOnly && $skipRoot );
1864
 
1865
 
1866
    #   Convert the target directory name into a physical path
1867
    #   User specifies '/' as the root of the image
1868
    #   User specifies 'name' as relateve to the root of the image
1869
    #
1870
    my $topDir = $DebianWorkDir . '/' . $path;
1871
    $topDir =~ s~/+$~~;
1872
 
1873
    Verbose("SetPermissions: Called with options " . join(", ", @_));
1874
 
1875
    #
1876
    #   Only set perms on the root directory
1877
    #       This is a trivial operation
1878
    #
1879
    if ( $rootOnly )
1880
    {
1881
        $someDone += chmodItem( $topDir, $filePerms, $dirPerms );
1882
    }
1883
    else
1884
    {
1885
        #
1886
        #   Create a list of files/dirs to process
1887
        #
1888
        my @elements = $search->search( $topDir );
1889
 
1890
        foreach my $dirEntry ( @elements )
1891
        {
1892
            my $fullPath = "$topDir/$dirEntry";
1893
 
1894
            # A dir and we dont have dirperms, so skip
1895
            if ( -d $fullPath && !defined($dirPerms) )
1896
            {
1897
                Verbose2("SetPermissions: Skipping dir $fullPath as we have no dir permissions");
1898
                next;
1899
            }
1900
 
1901
            # A file and we dont have fileperms, so skip
1902
            if ( -f $fullPath && !defined($filePerms) )
1903
            {
1904
                Verbose2("SetPermissions: Skipping file $fullPath as we have no file permissions");
1905
                next;
1906
            }
1907
 
1908
            # a file or a dir and have the right permissions and we are not recursing
1909
            if ( -f $fullPath || -d $fullPath )
1910
            {
1911
                $someDone += chmodItem( $fullPath, $filePerms, $dirPerms );
1912
            }
1913
            else
1914
            {
1915
                Warning("SetPermissions: Skipping $fullPath as its not a file or directory");
1916
            }
1917
        }
1918
 
1919
        #
1920
        #   Process the topDir
1921
        #   May not be modified if --SkipRoot has been requested
1922
        #
1923
        if ( !$skipRoot && -e $topDir )
1924
        {
1925
            $someDone += chmodItem( $topDir, $filePerms, $dirPerms );
1926
        }
1927
    }
1928
 
1929
    #   Final warning
1930
    #
1931
    Warning ("SetPermissions: No files located", "Args: @_") unless ( $someDone );
1932
}
1933
 
4676 dpurdie 1934
#************ INTERNAL USE ONLY  **********************************************
4636 dpurdie 1935
# Function        : chmodItem 
1936
#
1937
# Description     : Internal
1938
#                   chmod a file or a folder
1939
#
1940
# Inputs          : item                        - Item to mod
1941
#                   filePerms                   - File perms
1942
#                   dirPerms                    - dire perms
1943
#
1944
# Returns         : 1   - Item modified
1945
#                   0   - Item not modified
1946
#
4676 dpurdie 1947
#************ INTERNAL USE ONLY  **********************************************
4636 dpurdie 1948
sub chmodItem
1949
{
1950
    my ($item, $filePerms, $dirPerms) = @_;
1951
 
1952
    if ( -d $item && defined $dirPerms)
1953
    {
1954
        Verbose("SetPermissions: $dirPerms : $item");
1955
        System ('chmod', $dirPerms, $item );
1956
        return 1;
1957
    }
1958
 
1959
    if ( -f $item  && defined $filePerms)
1960
    {
1961
        Verbose("SetPermissions: $filePerms : $item");
1962
        System ('chmod', $filePerms, $item );
1963
        return 1;
1964
    }
1965
 
1966
    return 0;
1967
}
1968
 
1969
 
1970
#-------------------------------------------------------------------------------
407 dpurdie 1971
# Function        : CreateDir
1972
#
1973
# Description     : Create a directory within the target workspace
1974
#
1975
# Inputs          : $path           - Name of the target directory
1976
#
1977
# Returns         : Nothing
1978
#
1979
sub CreateDir
1980
{
1981
    my ($path) = @_;
1982
 
1983
    Verbose ("Create Dir: $path");
1984
    mkpath( $DebianWorkDir . '/' . $path );
1985
}
1986
 
1987
#-------------------------------------------------------------------------------
1988
# Function        : IsProduct
1989
#                   IsPlatform
1990
#                   IsTarget
427 dpurdie 1991
#                   IsVariant
6088 dpurdie 1992
#                   IsAlias
407 dpurdie 1993
#
1994
# Description     : This function allows some level of control in the
1995
#                   packaging scripts. It will return true if the current
1996
#                   product is listed.
1997
#
1998
#                   Ugly after thought
1999
#
2000
#                   Intended use:
2001
#                       Xxxxxx(...) if (IsProduct( 'aaa',bbb' );
2002
#
2003
# Inputs          : products    - a list of products to compare against
2004
#
2005
# Returns         : True if the current build is for one of the listed products
2006
#
2007
sub IsProduct
2008
{
2009
    foreach ( @_ )
2010
    {
2011
        return 1 if ( $opt_product eq $_ );
2012
    }
2013
    return 0;
2014
}
2015
 
2016
sub IsPlatform
2017
{
2018
    foreach ( @_ )
2019
    {
2020
        return 1 if ( $opt_platform eq $_ );
2021
    }
2022
    return 0;
2023
}
2024
 
2025
sub IsTarget
2026
{
2027
    foreach ( @_ )
2028
    {
2029
        return 1 if ( $opt_target eq $_ );
2030
    }
2031
    return 0;
2032
}
2033
 
427 dpurdie 2034
sub IsVariant
2035
{
2036
    foreach ( @_ )
2037
    {
2038
        return 1 if ( $opt_variant eq $_ );
2039
    }
2040
    return 0;
2041
}
407 dpurdie 2042
 
6088 dpurdie 2043
sub IsAlias
2044
{
2045
 
2046
    #
2047
    #   Get the aliases from the build info
2048
    #   This function was introduced late so its not always available
2049
    #
2050
    Error("IsAlias not supported in this version of JATS")
2051
        unless (defined &ReadBuildConfig::getAliases);
2052
    #
2053
    #   Create an hash of aliases to simplify testing
2054
    #   Do it onceand cache the results
2055
    #
2056
    unless (%opt_aliases) {
2057
        %opt_aliases = map { $_ => 1 } getAliases();
2058
    }
2059
 
2060
    foreach ( @_ )
2061
    {
2062
        return 1 if ( exists $opt_aliases{$_} );
2063
    }
2064
    return 0;
2065
}
2066
 
2067
 
4676 dpurdie 2068
#************ INTERNAL USE ONLY  **********************************************
407 dpurdie 2069
# Function        : FindFiles
2070
#
2071
# Description     : Locate files within a given dir tree
2072
#
2073
# Inputs          : $root           - Base of the search
2074
#                   $match          - Re to match
2075
#
2076
# Returns         : A list of files that match
2077
#
4676 dpurdie 2078
#************ INTERNAL USE ONLY  **********************************************
407 dpurdie 2079
my @FIND_LIST;
2080
my $FIND_NAME;
2081
 
2082
sub FindFiles
2083
{
2084
    my ($root, $match ) = @_;
2085
    Verbose2("FindFiles: Root: $root, Match: $match");
2086
 
2087
    #
2088
    #   Becareful of closure, Must use globals
2089
    #
2090
    @FIND_LIST = ();
2091
    $FIND_NAME = $match;
2092
    File::Find::find( \&find_files, $root);
2093
 
2094
    #
2095
    #   Find callback program
2096
    #
2097
    sub find_files
2098
    {
2099
        my $item =  $File::Find::name;
2100
 
2101
        return if ( -d $File::Find::name );
2102
        return unless ( $_ =~ m~$FIND_NAME~ );
2103
        push @FIND_LIST, $item;
2104
    }
2105
    return @FIND_LIST;
2106
}
2107
 
2108
#-------------------------------------------------------------------------------
2109
# Function        : CalcRelPath
2110
#
2111
# Description     : Return the relative path to the current working directory
2112
#                   as provided in $Cwd
2113
#
2114
# Inputs          : $Cwd - Base dir
2115
#                   $base - Path to convert
2116
#
2117
# Returns         : Relative path from the $Cwd
2118
#
2119
sub CalcRelPath
2120
{
2121
    my ($Cwd, $base) = @_;
2122
 
2123
    my @base = split ('/', $base );
2124
    my @here = split ('/', $Cwd );
2125
    my $result;
2126
 
2127
    Debug("RelPath: Source: $base");
2128
 
2129
    return $base unless ( $base =~ m~^/~ );
2130
 
2131
    #
2132
    #   Remove common bits from the head of both lists
2133
    #
2134
    while ( $#base >= 0 && $#here >= 0 && $base[0] eq $here[0] )
2135
    {
2136
        shift @base;
2137
        shift @here;
2138
    }
2139
 
2140
    #
2141
    #   Need to go up some directories from here and then down into base
2142
    #
2143
    $result = '../' x ($#here + 1);
2144
    $result .= join ( '/', @base);
2145
    $result = '.' unless ( $result );
2146
    $result =~ s~//~/~g;
2147
    $result =~ s~/$~~;
2148
 
2149
    Debug("RelPath: Result: $result");
2150
    return $result;
2151
}
2152
 
2153
#-------------------------------------------------------------------------------
2154
# Function        : ExpandLinkFiles
2155
#
2156
# Description     : Look for .LINK files in the output image and expand
2157
#                   the links into softlinks
2158
#
2159
# Inputs          : None
2160
#                   The rouine works on the $DebianWorkDir directory tree
2161
#
2162
# Returns         : Nothing
2163
#                   Will remove .LINKS files that are processed
2164
#
2165
sub ExpandLinkFiles
2166
{
2167
    foreach my $linkfile ( FindFiles( $DebianWorkDir, ".LINKS" ))
2168
    {
2169
        next if ( $linkfile =~ m~/\.svn/~ );
2170
        my $BASEDIR = StripFileExt( $linkfile );
2171
        $BASEDIR =~ s~^$DebianWorkDir/~~;
2172
        Verbose "Expand links: $BASEDIR";
2173
 
2174
        open (LF, "<", $linkfile ) || Error ("Cannot open link file: $linkfile" );
2175
        while ( <LF> )
2176
        {
2177
            chomp;
2178
            next if ( m~^#~ );
2179
            next unless ( $_ );
2180
            my ($link, $file) = split;
2181
 
2182
            MakeSymLink($file ,"$BASEDIR/$link", '--NoDotDot' );
2183
        }
2184
        close (LF);
2185
        unlink $linkfile;
2186
    }
2187
}
2188
 
4676 dpurdie 2189
#************ INTERNAL USE ONLY  **********************************************
407 dpurdie 2190
# Function        : ResolveFile
2191
#
2192
# Description     : Determine where the source for a file is
415 dpurdie 2193
#                   Will look in (default):
407 dpurdie 2194
#                       Local directory
2195
#                       Local Include
415 dpurdie 2196
#                   Or  (FromPackage)
2197
#                       Our Package directory
2198
#                       Interface directory (BuildPkgArchives)
2199
#                       Packages (LinkPkgArchive)
2200
#
407 dpurdie 2201
#                   Will scan 'parts' subdirs
2202
#
2203
# Inputs          : $from_package       - 0 - Local File
2204
#                   $file
2205
#
2206
# Returns         : Path
2207
#
4676 dpurdie 2208
#************ INTERNAL USE ONLY  **********************************************
407 dpurdie 2209
sub ResolveFile
2210
{
2211
    my ($from_package, $file) = @_;
2212
    my $wildcard = ($file =~ /[*?]/);
415 dpurdie 2213
    my @path;
407 dpurdie 2214
 
2215
    #
415 dpurdie 2216
    #   Determine the paths to search
2217
    #
2218
    if ( $from_package )
2219
    {
2220
        unless ( @ResolveFileList )
2221
        {
2222
            push @ResolveFileList, $opt_pkgdir;
2223
            foreach my $entry ( getPackageList() )
2224
            {
2225
                push @ResolveFileList, $entry->getBase(3);
2226
            }
2227
        }
2228
        @path = @ResolveFileList;
2229
    }
2230
    else
2231
    {
2232
        @path = ('.', $opt_localincdir);
2233
    }
2234
 
2235
    #
407 dpurdie 2236
    #   Determine a full list of 'parts' to search
2237
    #   This is provided within the build information
2238
    #
2239
    my @parts = getPlatformParts ();
2240
    push @parts, '';
2241
 
2242
    my @done;
2243
    foreach my $root (  @path )
2244
    {
2245
        foreach my $subdir ( @parts )
2246
        {
2247
            my $sfile;
415 dpurdie 2248
            $sfile = "$root/$subdir/$file";
2249
            $sfile =~ s~//~/~g;
2250
            $sfile =~ s~^./~~g;
2251
            Verbose2("LocateFile: $sfile, $root, $subdir");
2252
            if ( $wildcard )
2253
            {
2254
                push @done, glob ( $sfile );
2255
            }
2256
            else
2257
            {
2258
                push @done, $sfile if ( -f $sfile || -l $sfile )
2259
            }
407 dpurdie 2260
        }
2261
    }
2262
 
415 dpurdie 2263
    Error ("ResolveFile: File not found: $file", "Search Path:", @path)
407 dpurdie 2264
        unless ( @done );
2265
 
2266
    Warning ("ResolveFile: Multiple instances of file found. Only first is used", @done)
2267
        if ( $#done > 0 && ! $wildcard && !wantarray );
2268
 
2269
    return wantarray ? @done : $done[0];
2270
}
2271
 
2272
#-------------------------------------------------------------------------------
2273
# Function        : ResolveBinFile
2274
#
415 dpurdie 2275
# Description     : Determine where the source for a BIN file is
2276
#                   Will look in (default):
2277
#                       Local directory
2278
#                       Local Include
2279
#                   Or  (FromPackage)
2280
#                       Our Package directory
2281
#                       Interface directory (BuildPkgArchives)
2282
#                       Packages (LinkPkgArchive)
407 dpurdie 2283
#                   Will scan 'parts' subdirs
2284
#
2285
# Inputs          : $from_package       - 0 - Local File
415 dpurdie 2286
#                   $file
407 dpurdie 2287
#
2288
# Returns         : Path
2289
#
2290
sub ResolveBinFile
2291
{
2292
    my ($from_package, $file) = @_;
2293
    my @path;
2294
    my @types;
2295
    my $wildcard = ($file =~ /[*?]/);
2296
 
415 dpurdie 2297
    #
2298
    #   Determine the paths to search
2299
    #
407 dpurdie 2300
    if ( $from_package )
2301
    {
415 dpurdie 2302
        unless ( @ResolveBinFileList )
2303
        {
2304
            push @ResolveBinFileList, $opt_pkgdir . '/bin';
2305
            foreach my $entry ( getPackageList() )
2306
            {
2307
                if ( my $path = $entry->getBase(3) )
2308
                {
2309
                    $path .= '/bin';
2310
                    push @ResolveBinFileList, $path if ( -d $path );
2311
                }
2312
            }
2313
        }
2314
        @path = @ResolveBinFileList;
407 dpurdie 2315
        @types = ($opt_type, '');
2316
    }
2317
    else
2318
    {
2319
        @path = ($opt_bindir, $opt_localbindir);
2320
        @types = '';
2321
    }
2322
 
2323
    #
2324
    #   Determine a full list of 'parts' to search
2325
    #   This is provided within the build information
2326
    #
2327
    my @parts = getPlatformParts ();
2328
    push @parts, '';
2329
 
2330
    my @done;
2331
    foreach my $root (  @path )
2332
    {
2333
        foreach my $subdir ( @parts )
2334
        {
2335
            foreach my $type ( @types )
2336
            {
2337
                my $sfile;
2338
                $sfile = "$root/$subdir$type/$file";
2339
                $sfile =~ s~//~/~g;
2340
                Verbose2("LocateBinFile: $sfile");
2341
                if ( $wildcard )
2342
                {
429 dpurdie 2343
                    foreach  ( glob ( $sfile ) )
2344
                    {
4143 dpurdie 2345
                        # Ignore .dbg (vix) and .debug (qt) files.
429 dpurdie 2346
                        next if ( m~\.dbg$~ );
4143 dpurdie 2347
                        next if ( m~\.debug$~ );
429 dpurdie 2348
                        push @done, $_;
2349
                    }
407 dpurdie 2350
                }
2351
                else
2352
                {
415 dpurdie 2353
                    push @done, $sfile if ( -f $sfile || -l $sfile )
407 dpurdie 2354
                }
2355
            }
2356
        }
2357
    }
2358
 
415 dpurdie 2359
    Error ("ResolveBinFile: File not found: $file", "Search Path:", @path)
407 dpurdie 2360
        unless ( @done );
2361
 
5026 dpurdie 2362
    if ( $#done > 0 && ! $wildcard )
2363
    {
2364
        Warning ("ResolveBinFile: Multiple instances of file found. Only first is used", @done);
2365
        splice (@done, 1);
2366
    }
2367
 
407 dpurdie 2368
    return wantarray ? @done : $done[0];
2369
}
2370
 
2371
#-------------------------------------------------------------------------------
2372
# Function        : ResolveLibFile
2373
#
415 dpurdie 2374
# Description     : Determine where the source for a LIB file is
2375
#                   Will look in (default):
2376
#                       Local directory
2377
#                       Local Include
2378
#                   Or  (FromPackage)
2379
#                       Our Package directory
2380
#                       Interface directory (BuildPkgArchives)
2381
#                       Packages (LinkPkgArchive)
407 dpurdie 2382
#                   Will scan 'parts' subdirs
2383
#
4672 dpurdie 2384
# Inputs          : $from_package   - 0:Local File
2385
#                   $file           - Basename for a 'realname'
2386
#                                     Do not provide 'lib' or '.so' or version info
2387
#                                     May contain embedded options
2388
#                                       --Dll           - Use Windows style versioned DLL
2389
#                                       --VersionDll    - Use the versioned DLL
2390
#                                       --3rdParty      - Use exact name provided
407 dpurdie 2391
#
2392
# Returns         : Path
2393
#
2394
sub ResolveLibFile
2395
{
2396
    my ($from_package, $file) = @_;
2397
    my $wildcard = ($file =~ /[*?]/);
2398
    my @options;
2399
    my $num_dll;
415 dpurdie 2400
    my @path;
407 dpurdie 2401
    #
2402
    #   Extract options from file
2403
    #
409 alewis 2404
    $num_dll = 0;
407 dpurdie 2405
    ($file, @options) = split ( ',', $file);
2406
    foreach ( @options )
2407
    {
2408
        if ( m/^--Dll/ ) {
2409
            $num_dll = 1;
2410
        } elsif ( m/^--VersionDll/ ) {
2411
            $num_dll = 2;
4672 dpurdie 2412
        } elsif ( m/^--3rdParty/ ) {
2413
            $num_dll = 3;
407 dpurdie 2414
        } else {
2415
            Error ("Unknown suboption to ResolveLibFile: $_" );
2416
        }
2417
    }
2418
 
2419
    #
415 dpurdie 2420
    #   Determine the paths to search
2421
    #
2422
    if ( $from_package )
2423
    {
2424
        unless ( @ResolveLibFileList )
2425
        {
2426
            push @ResolveLibFileList, $opt_pkgdir . '/lib';
2427
            foreach my $entry ( getPackageList() )
2428
            {
2429
                push @ResolveLibFileList, $entry->getLibDirs(3);
2430
            }
2431
        }
2432
        @path = @ResolveLibFileList;
2433
    }
2434
    else
2435
    {
2436
        @path = ($opt_libdir, $opt_locallibdir);
2437
    }
2438
 
2439
    #
407 dpurdie 2440
    #   Determine a full list of 'parts' to search
2441
    #   This is provided within the build information
2442
    #
2443
    my @parts = getPlatformParts ();
2444
    push @parts, '';
2445
 
2446
    my @done;
2447
    foreach my $root (  @path )
2448
    {
2449
        foreach my $type ( $opt_type, '' )
2450
        {
2451
            foreach my $subdir ( @parts )
2452
            {
2453
                my $sfile;
2454
                my $exact;
2455
                if ( $num_dll == 2 ) {
2456
                    $sfile = $file . $type . '.*.dll' ;
2457
                } elsif ( $num_dll == 1 ) {
2458
                    $sfile = $file . $type . '.dll' ;
2459
                    $exact = 1;
4672 dpurdie 2460
                } elsif ( $num_dll == 3 ) {
2461
                    $sfile = $file;
2462
                    $exact = 1;
407 dpurdie 2463
                } else {
2464
                    $sfile = "lib" . $file . $type . '.so.*';
2465
                }
2466
 
2467
                $sfile = "$root/$subdir/$sfile";
2468
                $sfile =~ s~//~/~g;
2469
                Verbose2("LocateLibFile: $sfile");
2470
                if ( $exact )
2471
                {
415 dpurdie 2472
                    push @done, $sfile if ( -f $sfile || -l $sfile );
407 dpurdie 2473
                }
419 dpurdie 2474
                elsif ($num_dll)
407 dpurdie 2475
                {
2476
                    push @done, glob ( $sfile );
2477
                }
419 dpurdie 2478
                else
2479
                {
2480
                    #
2481
                    #   Looking for .so files
2482
                    #   Filter out the soname so files
2483
                    #   Assume that the soname is shorter than the realname
4143 dpurdie 2484
                    #       Ignore .dbg (vix) and .debug (qt) files.
419 dpurdie 2485
                    #
2486
                    my %sieve;
2487
                    foreach ( glob ( $sfile )  )
2488
                    {
429 dpurdie 2489
                        next if ( m~\.dbg$~ );
4143 dpurdie 2490
                        next if ( m~\.debug$~ );
421 alewis 2491
                        m~(.*\.so\.)([\d\.]*\d)$~;
2492
                        if ( $1 )
2493
                        {
2494
                            my $file = $1;
2495
                            my $len = exists $sieve{$file} ? length($sieve{$file}) : 0;
2496
                            $sieve{$file} = $_
2497
                                if ( $len == 0 || length($_) > $len );
2498
                        }                                
419 dpurdie 2499
                    }
2500
 
2501
                    push @done, values %sieve;
2502
                }
407 dpurdie 2503
            }
2504
        }
2505
    }
2506
 
415 dpurdie 2507
    Error ("ResolveLibFile: File not found: $file", "Search Path:", @path)
407 dpurdie 2508
        unless ( @done );
2509
 
5026 dpurdie 2510
    if ( $#done > 0 && ! $wildcard )
2511
    {
2512
        Warning ("ResolveLibFile: Multiple instances of file found. Only first is used", @done);
2513
        splice (@done, 1);
2514
    }
407 dpurdie 2515
    return wantarray ? @done : $done[0];
2516
}
2517
 
6088 dpurdie 2518
#-------------------------------------------------------------------------------
2519
# Function        : ResolveDebPackage
2520
#
2521
# Description     : Determine where the source for a Debian Package is
2522
#                   Will look in (default):
2523
#                       Local directory
2524
#                       Local Include
2525
#                   Or  (FromPackage)
2526
#                       Our Package directory
2527
#                       Interface directory (BuildPkgArchives)
2528
#                       Packages (LinkPkgArchive)
2529
#
2530
# Inputs          : $from_package   - 0:Local File
2531
#                   $baseName       - Basename for a 'DebianPackage'
2532
#                                     Do not provide version info, architecture or suffix
2533
#                                     May contain embedded options
2534
#                                       --Arch=XXX      - Specify alternate architcute
2535
#                                       --Product=YYYY  - Specify product family
2536
#                                       --Debug         - Use alternate build type
2537
#                                       --Prod          - Use alternate build type
2538
#
2539
# Returns         : Path
2540
#
2541
sub ResolveDebPackage
2542
{
2543
    my ($from_package, $file) = @_;
2544
    my @path;
2545
    my $arch;
2546
    my $product;
2547
    my $buildType;
2548
    my @types;
2549
    my $baseName;
2550
    my @options;
407 dpurdie 2551
 
6088 dpurdie 2552
    #
2553
    #   Extract options from file
2554
    #
2555
    ($baseName, @options) = split ( ',', $file);
2556
    foreach ( @options )
2557
    {
2558
        if ( m/^--Arch=(.+)/ ) {
2559
            $arch=$1;
2560
        } elsif ( m/^--Product=(.+)/ ) {
2561
            $product = $1;
2562
        } elsif ( m/^--Debug/ ) {
2563
            Error ("ResolveDebPackage: Cannot specify --Prod and --Debug") if defined $buildType;
2564
            $buildType = 'D';
2565
        } elsif ( m/^--Prod/ ) {
2566
            Error ("ResolveDebPackage: Cannot specify --Prod and --Debug") if defined $buildType;
2567
            $buildType = 'P';
2568
        } else {
2569
            Error ("Unknown suboption to ResolveDebPackage: $_" );
2570
        }
2571
    }
2572
 
2573
    #
2574
    #   Insert defaults
2575
    #
2576
    $buildType = $opt_type unless ($buildType);
2577
    $arch = $opt_target unless ($arch);
2578
 
2579
    #
2580
    #   Determine the paths to search
2581
    #
2582
    if ( $from_package )
2583
    {
2584
        unless ( @ResolveDebFileList )
2585
        {
2586
            push @ResolveDebFileList,  $opt_pkgdir, $opt_pkgdir . '/bin';
2587
            foreach my $entry ( getPackageList() )
2588
            {
2589
                if ( my $path = $entry->getBase(3) )
2590
                {
2591
                    push @ResolveDebFileList, $path if ( -d $path );
2592
 
2593
                    $path .= '/bin';
2594
                    push @ResolveDebFileList, $path if ( -d $path );
2595
                }
2596
            }
2597
        }
2598
        @path = @ResolveDebFileList;
6092 dpurdie 2599
        @types = ($buildType, '');
6088 dpurdie 2600
    }
2601
    else
2602
    {
2603
        @path = ($opt_bindir, $opt_localbindir);
6092 dpurdie 2604
        @types = ($buildType, '');
6088 dpurdie 2605
    }
2606
 
2607
    #
2608
    #   The debian  package name is
2609
    #   In packages BIN dir
2610
    #       (BaseName)_VersionString(_Product)(_Arch).deb
2611
    #       
2612
    #   In root of package
2613
    #       (BaseName)_VersionString(_Product)(_Arch)(_Type).deb
2614
    #
2615
    #       
2616
    #   The package may be found in
2617
    #       Package Root
2618
    #       Package bin directory
2619
    #       
2620
    $file = $baseName . '_*';
2621
    if (defined $product) {
2622
        $file .= ( '_' . $product)
2623
        }
2624
    $file .= '_' . $arch;
2625
 
2626
    #
2627
    #   Determine a full list of 'parts' to search
2628
    #   This is provided within the build information
2629
    #
2630
    my @parts = getPlatformParts ();
2631
    push @parts, '';
2632
 
2633
    my @done;
2634
    foreach my $root (  @path )
2635
    {
2636
        foreach my $subdir ( @parts )
2637
        {
2638
            foreach my $type ( @types )
2639
            {
2640
                my $sfile;
2641
                $sfile = "$root/$subdir$type/$file";
2642
                $sfile =~ s~//~/~g;
2643
                foreach my $type2 ( @types )
2644
                {
2645
                    my $tfile = $sfile;
2646
                    $tfile .= '_' . $type2 if $type2;
2647
                    $tfile .= '.deb';
2648
                    Verbose2("ResolveDebPackage: $tfile");
2649
                    foreach  ( glob ( $tfile ) )
2650
                    {
2651
                        push @done, $_;
2652
                    }
2653
                }
2654
            }
2655
        }
2656
    }
2657
 
2658
    Error ("ResolveDebPackage: Package not found: $file", "Search Path:", @path)
2659
        unless ( @done );
2660
 
2661
    if ( $#done > 0 )
2662
    {
2663
        Error ("ResolveDebPackage: Multiple instances of Package found.", @done);
2664
    }
2665
    return wantarray ? @done : $done[0];
2666
}
2667
 
2668
 
2669
 
407 dpurdie 2670
#-------------------------------------------------------------------------------
2671
# Function        : AUTOLOAD
2672
#
2673
# Description     : Intercept bad user directives and issue a nice error message
2674
#                   This is a simple routine to report unknown user directives
2675
#                   It does not attempt to distinguish between user errors and
2676
#                   programming errors. It assumes that the program has been
2677
#                   tested. The function simply report filename and line number
2678
#                   of the bad directive.
2679
#
2680
# Inputs          : Original function arguments ( not used )
2681
#
2682
# Returns         : This function does not return
2683
#
2684
our $AUTOLOAD;
2685
sub AUTOLOAD
2686
{
2687
    my $fname = $AUTOLOAD;
2688
    $fname =~ s~^main::~~;
2689
    my ($package, $filename, $line) = caller;
2690
 
2691
    Error ("Directive not known or not allowed in this context: $fname",
2692
           "Directive: $fname( @_ );",
2693
           "File: $filename, Line: $line" );
2694
}
2695
 
2696
 
2697
1;
2698