2
# This Source Code Form is subject to the terms of the Mozilla Public
3
# License, v. 2.0. If a copy of the MPL was not distributed with this
4
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
20
use vars qw(@ISA @EXPORT);
22
# Package that generates a jar manifest from an input file
29
# initialize variables
30
my($saved_cwd) = cwd();
31
my($component) = ""; # current component being copied
32
my(@components) = (); # list of components to copy
33
my($components) = ""; # string version of @components
34
my($altdest) = ""; # alternate file destination
35
my($line) = ""; # line being processed
36
my($srcdir) = ""; # root directory being copied from
37
my($destdir) = ""; # root directory being copied to
38
my($package) = ""; # file listing files to copy
39
my($os) = ""; # os type (MSDOS, Unix)
40
my($lineno) = 0; # line # of package file for error text
41
my($debug) = 0; # controls amount of debug output
42
my($dirflag) = 0; # flag: are we copying a directory?
43
my($help) = 0; # flag: if set, print usage
44
my($fatal_warnings) = 0; # flag: whether package warnings (missing files or invalid entries) are fatal
45
my($flat) = 0; # copy everything into the package dir, not into separate
47
my($delayed_error) = 0; # flag: whether an error was found while reading the manifest but we still
48
# chose to finish reading it
52
# Loop over each line in the specified manifest, copying into $destdir
56
($srcdir, $destdir, $package, $os, $flat, $fatal_warnings, $help, $debug, @components) = @_;
65
open (MANIFEST,"<$package") ||
66
die "Error: couldn't open file $package for reading: $!. Exiting...\n";
68
LINE: while (<MANIFEST>) {
73
s/\\/\//g if ($os eq "MSDOS"); # Convert to posix path
74
s/\;.*//; # it's a comment, kill it.
75
s/^\s+//; # nuke leading whitespace
76
s/\s+$//; # nuke trailing whitespace
78
($debug >= 2) && print "\n";
79
($debug >= 8) && print "line $lineno:$_\n";
81
# it's a blank line, skip it.
83
($debug >= 10) && print "blank line.\n";
87
# it's a new component
89
($debug >= 10) && print "component.\n";
95
# if we find a file before we have a component and we are in flat mode,
96
# copy it - allows for flat only files (installed-chrome.txt)
97
if (( $component eq "" ) && ($components eq "" ) && (!$flat)) {
101
# skip line if we're only copying specific components and outside
103
if (( $component eq "" ) && ($components ne "" )) {
104
($debug >= 10) && print "Not in specifed component. Skipping $_\n";
108
$line = $_; # if $line not set, set it.
111
if ($os ne "MSDOS") { # hack - need to fix for dos
112
$line =~ s|^/||; # strip any leading path delimiter
115
# delete the file or directory following the '-'
117
$line =~ s/^-//; # strip leading '-'
118
($debug >= 10) && print "delete: $destdir/$component/$line\n";
119
do_delete ("$destdir", "$component", "$line");
123
# file/directory being copied to different target location
126
die "Error: multiple commas not allowed ($package, $lineno): $_.\n";
127
($line, $altdest) = split (/\s*\,\s*/, $line, 2);
128
$line =~ s|/*$||; # strip any trailing path delimiters
129
$altdest =~ s|/*$||; # strip any trailing delimiter
130
($debug >= 10) && print "relocate: $line => $altdest.\n";
133
# if it has wildcards, do recursive copy.
135
($debug >= 10) && print "wildcard copy.\n";
136
do_wildcard ("$srcdir/$line");
140
# if it's a single file, copy it.
141
( -f "$srcdir/$line" ) && do {
142
($debug >= 10) && print "file copy.\n";
147
# if it's a directory, do recursive copy.
148
(-d "$srcdir/$line") && do {
149
($debug >= 10) && print "directory copy.\n";
150
do_copydir ("$srcdir/$line");
154
# if we hit this, it's either a file in the package file that is
155
# not in the src directory, or it is not a valid entry.
156
delayed_die_or_warn("package error or possible missing or unnecessary file: $line ($package, $lineno).");
162
if ($delayed_error) {
163
die "Error: found error(s) while packaging, see above for details.\n"
168
# Delete the given file or directory
172
my ($targetpath) = $_[0];
173
my ($targetcomp) = $_[1];
174
my ($targetfile) = $_[2];
175
my ($target) = ($flat) ? "$targetpath/$targetfile" : "$targetpath/$targetcomp/$targetfile";
177
($debug >= 2) && print "do_delete():\n";
178
($debug >= 1) && print "-$targetfile\n";
182
die "Error: delete failed: $target not writeable ($package, $component, $lineno). Exiting...\n";
183
($debug >= 4) && print " unlink($target)\n";
185
die "Error: unlink() failed: $!. Exiting...\n";
186
} elsif ( -d $target ) {
188
die "Error: delete failed: $target not writeable ($package, $component, $lineno). Exiting...\n";
189
($debug >= 4) && print " rmtree($target)\n";
190
rmtree ($target, 0, 0) ||
191
die "Error: rmtree() failed: $!. Exiting...\n";
193
warn "Warning: delete failed: $target is not a file or directory ($package, $component, $lineno).\n";
199
# Copy an individual file from the srcdir to the destdir.
201
# This is called by both the individual and batch/recursive copy routines,
202
# using $dirflag to check if called from do_copydir. Batch copy can pass in
203
# directories, so be sure to check first and break if it isn't a file.
207
my ($destpath) = ""; # destination directory path
208
my ($destpathcomp) = ""; # ditto, but possibly including component dir
209
my ($destname) = ""; # destination file name
210
my ($destsuffix) = ""; # destination file name suffix
211
my ($altpath) = ""; # alternate destination directory path
212
my ($altname) = ""; # alternate destination file name
213
my ($altsuffix) = ""; # alternate destination file name suffix
214
my ($srcpath) = ""; # source file directory path
215
my ($srcname) = ""; # source file name
216
my ($srcsuffix) = ""; # source file name suffix
218
($debug >= 2) && print "do_copyfile():\n";
219
($debug >= 10) && print " cwd: " . getcwd() . "\n";
221
# set srcname correctly depending on how called
223
($srcname, $srcpath, $srcsuffix) = fileparse("$File::Find::name", '\..*?$');
225
($srcname, $srcpath, $srcsuffix) = fileparse("$srcdir/$line", '\..*?$');
228
($debug >= 4) && print " fileparse(src): '$srcpath $srcname $srcsuffix'\n";
230
# return if srcname is a directory from do_copydir
231
if ( -d "$srcpath$srcname$srcsuffix" ) {
232
($debug >= 10) && print " return: '$srcpath$srcname$srcsuffix' is a directory\n";
236
($debug >= 10) && print " '$srcpath$srcname$srcsuffix' is not a directory\n";
239
# set the destination path, if alternate destination given, use it.
241
# WebappRuntime has manifests that shouldn't be flattened, even though it
242
# gets packaged with Firefox, which does get flattened, so special-case it.
243
if ($srcsuffix eq ".manifest" && $srcpath =~ m'/(chrome|components)/$' &&
244
$component ne "WebappRuntime") {
246
if ($component eq "") {
247
die ("Manifest file was not part of a component.");
250
$destpathcomp = "$srcdir/manifests/$component/$subdir";
251
$altdest = "$srcname$srcsuffix";
253
elsif ($srcsuffix eq ".xpt" && $srcpath =~ m|/components/$|) {
254
if ($component eq "") {
255
die ("XPT file was not part of a component.");
258
$destpathcomp = "$srcdir/xpt/$component/components";
259
$altdest = "$srcname$srcsuffix";
262
$destpathcomp = "$destdir";
265
if ( $component ne "" ) {
266
$destpathcomp = "$destdir/$component";
269
$destpathcomp = "$destdir";
272
if ( $altdest ne "" ) {
273
if ( $dirflag ) { # directory copy to altdest
274
($destname, $destpath, $destsuffix) = fileparse("$destpathcomp/$altdest/$File::Find::name", '\..*?$');
275
# Todo: add MSDOS hack
276
$destpath =~ s|\Q$srcdir\E/$line/||; # rm info added by find
278
print " dir copy to altdest: $destpath $destname $destsuffix\n";
279
} else { # single file copy to altdest
280
($destname, $destpath, $destsuffix) = fileparse("$destpathcomp/$altdest", '\..*?$');
282
print " file copy to altdest: $destpath $destname $destsuffix\n";
285
if ( $dirflag ) { # directory copy, no altdest
286
my $destfile = $File::Find::name;
287
if ($os eq "MSDOS") {
288
$destfile =~ s|\\|/|;
290
$destfile =~ s|\Q$srcdir\E/||;
292
($destname, $destpath, $destsuffix) = fileparse("$destpathcomp/$destfile", '\..*?$');
295
print " dir copy w/o altdest: $destpath $destname $destsuffix\n";
296
} else { # single file copy, no altdest
297
($destname, $destpath, $destsuffix) = fileparse("$destpathcomp/$line", '\..*?$');
299
print " file copy w/o altdest: $destpath $destname $destsuffix\n";
304
$destpath =~ s|bin[/\\]||;
307
# create the destination path if it doesn't exist
308
if (! -d "$destpath" ) {
309
($debug >= 5) && print " mkpath($destpath)\n";
310
# For OS/2 - remove trailing '/'
312
mkpath ($destpath, 0, 0755) ||
313
die "Error: mkpath() failed: $!. Exiting...\n";
314
# Put delimiter back for copying...
315
$destpath = "$destpath/";
318
# path exists, source and destination known, time to copy
319
if ((-f "$srcpath$srcname$srcsuffix") && (-r "$srcpath$srcname$srcsuffix")) {
322
print "$destname$destsuffix\n"; # from unglob
324
print "$line\n"; # from single file
327
print " copy\t$srcpath$srcname$srcsuffix =>\n\t\t$destpath$destname$destsuffix\n";
331
if (stat("$destpath$destname$destsuffix") &&
332
stat("$srcpath$srcname$srcsuffix")->mtime < stat("$destpath$destname$destsuffix")->mtime) {
334
print "source file older than destination, do not copy\n";
339
unlink("$destpath$destname$destsuffix") if ( -e "$destpath$destname$destsuffix");
340
# If source is a symbolic link pointing in the same directory, create a
342
if ((-l "$srcpath$srcname$srcsuffix") && (readlink("$srcpath$srcname$srcsuffix") !~ /\//)) {
343
symlink(readlink("$srcpath$srcname$srcsuffix"), "$destpath$destname$destsuffix") ||
344
die "Error: copy of symbolic link $srcpath$srcname$srcsuffix failed ($package, $component, $lineno): $!. Exiting...\n";
347
copy ("$srcpath$srcname$srcsuffix", "$destpath$destname$destsuffix") ||
348
die "Error: copy of file $srcpath$srcname$srcsuffix failed ($package, $component, $lineno): $!. Exiting...\n";
350
# if this is unix, set the dest file permissions
352
my($st) = stat("$srcpath$srcname$srcsuffix") ||
353
die "Error: can't stat $srcpath$srcname$srcsuffix: $! Exiting...\n";
355
($debug >= 2) && print " chmod ".$st->mode." $destpath$destname$destsuffix\n";
356
chmod ($st->mode, "$destpath$destname$destsuffix") ||
357
warn "Warning: chmod of $destpath$destname$destsuffix failed: $!. Exiting...\n";
359
warn "Error: file $srcpath$srcname$srcsuffix is not a file or is not readable ($package, $component, $lineno).\n";
365
# Expand any wildcards and copy files and/or directories
367
# todo: pass individual files to do_copyfile, not do_copydir
375
($debug >= 2) && print "do_wildcard():\n";
377
if ( $entry =~ /(?:\*|\?)/ ) { # it's a wildcard,
378
@list = glob($entry); # expand it
379
($debug >= 4) && print " glob: $entry => @list\n";
381
foreach $item ( @list ) { # now copy each item in list
383
($debug >= 10) && print " do_copyfile: $item\n";
385
# glob adds full path to item like find() in copydir so
386
# take advantage of existing code in copyfile by using
387
# $dirflag and $File::Find::name.
389
$File::Find::name = $item;
393
$File::Find::name = "";
394
} elsif ( -d $item ) {
395
($debug >= 10) && print " do_copydir($item)\n";
398
warn "Warning: $item is not a file or directory ($package, $component, $lineno). Skipped...\n";
405
# Recursively copy directories specified.
411
$dirflag = 1; # flag indicating directory copy in progress
413
($debug >= 2) && print "do_copydir():\n";
415
if (! -d "$entry" ) {
416
warn "Warning: $entry is not a directory ($package, $component, $lineno). Skipped...\n";
419
($debug >= 4) && print " find($entry)\n";
421
find (\&do_copyfile, $entry);
428
# Handle new component
432
($debug >= 2) && print "do_component():\n";
434
( $component =~ /^\[.*(?:\s|\[|\])+.*\]/ ) && # no brackets or ws
435
die "Error: malformed component $component. Exiting...\n";
436
$component =~ s/^\[(.*)\]/$1/; # strip []
438
if ( $components ne "") {
439
if ( $components =~ /$component/ ) {
440
($debug >= 10) && print "Component $component is in $components.\n";
442
($debug >= 10) && print "Component $component not in $components.\n";
444
return; # named specific components and this isn't it
449
print "[$component]\n";
451
# create component directory
453
if ( -d "$destdir/$component" ) {
454
warn "Warning: component directory \"$component\" already exists in \"$destdir\".\n";
456
($debug >= 4) && print " mkdir $destdir/$component\n";
457
mkdir ("$destdir/$component", 0755) ||
458
die "Error: couldn't create component directory \"$component\": $!. Exiting...\n";
464
# Print error (and die later) or warn, based on whether $fatal_warnings is set.
466
sub delayed_die_or_warn
470
if ($fatal_warnings) {
471
warn "Error: $msg\n";
474
warn "Warning: $msg\n";
479
# Check that arguments to script are valid.
485
($debug >= 2) && print "check_arguments():\n";
487
# if --help print usage
493
# make sure required variables are set:
494
# check source directory
495
if ( $srcdir eq "" ) {
496
print "Error: source directory (--source) not specified.\n";
498
} elsif ((! -d $srcdir) || (! -r $srcdir)) {
499
print "Error: source directory \"$srcdir\" is not a directory or is unreadable.\n";
503
# check destination directory
504
if ( $destdir eq "" ) {
505
print "Error: destination directory (--destination) not specified.\n";
507
} elsif ((! -d $destdir) || (! -w $destdir)) {
508
print "Error: destination directory \"$destdir\" is not a directory or is not writeable.\n";
512
# check destdir not a subdir of srcdir
513
# hack - workaround for bug 14558 that should be fixed eventually.
514
if (0) { # todo - write test
515
print "Error: destination directory must not be subdirectory of the source directory.\n";
520
if ( $package eq "" ) {
521
print "Error: package file (--file) not specified.\n";
523
} elsif (!(-f $package) || !(-r $package)) {
524
print "Error: package file \"$package\" is not a file or is unreadable.\n";
528
# check OS == {unix|dos}
530
print "Error: OS type (--os) not specified.\n";
532
} elsif ( $os =~ /dos/i ) {
534
fileparse_set_fstype ($os);
535
} elsif ( $os =~ /unix/i ) {
536
$os = "Unix"; # can be anything but MSDOS
537
fileparse_set_fstype ($os);
539
print "Error: OS type \"$os\" unknown.\n";
543
# turn components array into a string for regexp
544
if ( @components > 0 ) {
545
$components = join (",",@components);
551
print ("source dir:\t$srcdir\ndest dir:\t$destdir\npackage:\t$package\nOS:\t$os\ncomponents:\t$components\n");
555
print "See \'$0 --help\' for more information.\n";
556
print "Exiting...\n";
565
# display usage information
569
($debug >= 2) && print "print_usage():\n";
574
Copy files from the source directory to component directories
575
in the destination directory as specified by the package file.
578
-s, --source <source directory>
579
Specifies the directory from which to copy the files
580
specified in the file passed via --file.
583
-d, --destination <destination directory>
584
Specifies the directory in which to create the component
585
directories and copy the files specified in the file passed
589
NOTE: Source and destination directories must be absolute paths.
590
Relative paths will NOT work. Also, the destination directory
591
must NOT be a subdirectory of the source directory.
593
-f, --file <package file>
594
Specifies the file listing the components to be created in
595
the destination directory and the files to copy from the
596
source directory to each component directory in the
597
destination directory.
601
Specifies which type of system this is. Used for parsing
602
file specifications from the package file.
605
-c, --component <component name>
606
Specifies a specific component in the package file to copy
607
rather than copying all the components in the package file.
608
Can be used more than once for multiple components (e.g.
609
"-c browser -c mail" to copy mail and news only).
613
Suppresses creation of components dirs, but stuffes everything
614
directly into the package destination dir. This is useful
615
for creating tarballs.
618
Prints this information.
622
Controls verbosity of debugging output, 10 being most verbose.
623
1 : same as --verbose.
624
2 : includes function calls.
625
3 : includes source and destination for each copy.
629
Print component names and files copied/deleted.
635
$0 --os unix --source /builds/mozilla/dist --destination /h/lithium/install --file packages-win --os unix --verbose
637
Note: options can be specified by either a leading '--' or '-'.