2 eval 'exec perl -wS $0 ${1+"$@"}'
4 #*************************************************************************
6 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
8 # Copyright 2008 by Sun Microsystems, Inc.
10 # OpenOffice.org - a multi-platform office productivity suite
12 # $RCSfile: deliver.pl,v $
16 # This file is part of OpenOffice.org.
18 # OpenOffice.org is free software: you can redistribute it and/or modify
19 # it under the terms of the GNU Lesser General Public License version 3
20 # only, as published by the Free Software Foundation.
22 # OpenOffice.org is distributed in the hope that it will be useful,
23 # but WITHOUT ANY WARRANTY; without even the implied warranty of
24 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 # GNU Lesser General Public License version 3 for more details
26 # (a copy is included in the LICENSE file that accompanied this code).
28 # You should have received a copy of the GNU Lesser General Public License
29 # version 3 along with OpenOffice.org. If not, see
30 # <http://www.openoffice.org/license.html>
31 # for a copy of the LGPLv3 License.
33 #*************************************************************************
36 # deliver.pl - copy from module output tree to solver
42 use File
::DosGlob
'glob';
48 ( $script_name = $0 ) =~ s/^.*\b(\w+)\.pl$/$1/;
50 $id_str = ' $Revision$ ';
51 $id_str =~ /Revision:\s+(\S+)\s+\$/
52 ?
($script_rev = $1) : ($script_rev = "-");
58 # if you add a action 'foo', than add 'foo' to this list and
59 # implement 'do_foo()' in the implemented actions area
60 @action_list = ( # valid actions
70 # copy filter: files matching these patterns won't be copied by
72 @copy_filter_patterns = (
79 $module = 0; # module name
80 $base_dir = 0; # path to module base directory
81 $dlst_file = 0; # path to d.lst
82 $ilst_ext = 'ilst'; # extension of image lists
83 $umask = 22; # default file/directory creation mask
84 $dest = 0; # optional destination path
85 $common_build = 0; # do we have common trees?
86 $common_dest = 0; # common tree on solver
88 @action_data = (); # LoL with all action data
89 @macros = (); # d.lst macros
90 @addincpath_list = (); # files which have to be filtered through addincpath
91 @dirlist = (); # List of 'mkdir' targets
92 @zip_list = (); # files which have to be zipped
93 @common_zip_list = (); # common files which have to be zipped
94 @log_list = (); # LoL for logging all copy and link actions
95 @common_log_list = (); # LoL for logging all copy and link actions in common_dest
96 $logfiledate = 0; # Make log file as old as newest delivered file
97 $commonlogfiledate = 0; # Make log file as old as newest delivered file
99 $files_copied = 0; # statistics
100 $files_unchanged = 0; # statistics
102 $opt_force = 0; # option force copy
103 $opt_minor = 0; # option deliver in minor
104 $opt_check = 0; # do actually execute any action
105 $opt_zip = 0; # create an additional zip file
106 $opt_silent = 0; # be silent, only report errors
107 $opt_verbose = 0; # be verbose (former default behaviour)
108 $opt_log = 1; # create an additional log file
109 $opt_link = 0; # hard link files into the solver to save disk space
110 $opt_deloutput = 0; # delete the output tree for the project once successfully delivered
112 $delete_common = 1; # for "-delete": if defined delete files from common tree also
114 if ($^O
ne 'cygwin') { # iz59477 - cygwin needes a dot "." at the end of filenames to disable
115 $maybedot = ''; # some .exe transformation magic.
120 ($gui = lc($ENV{GUI
})) || die "Can't determine 'GUI'. Please set environment.\n";
123 # zip is default for RE master builds
124 $opt_zip = 1 if ( defined($ENV{DELIVER_TO_ZIP
}) && uc($ENV{DELIVER_TO_ZIP
}) eq 'TRUE' && ! defined($ENV{CWS_WORK_STAMP
}));
126 $has_symlinks = 0; # system supports symlinks
132 # trap normal signals (HUP, INT, PIPE, TERM)
133 # for clean up on unexpected termination
134 use sigtrap
'handler' => \
&cleanup_and_die
, 'normal-signals';
140 print "$script_name -- version: $script_rev\n" if !$opt_silent;
142 if ( ! $opt_delete ) {
143 if ( $ENV{GUI
} eq 'WNT' ) {
144 if ($ENV{COM
} eq 'GCC') {
153 push_default_actions
();
155 check_dlst
() if $opt_checkdlst;
157 walk_addincpath_list
();
158 write_log
() if $opt_log;
159 zip_files
() if $opt_zip;
160 cleanup
() if $opt_delete;
161 delete_output
() if $opt_deloutput;
166 #### implemented actions #####
170 # We need to copy two times:
171 # from the platform dependent output tree
172 # and from the common output tree
173 my ($dependent, $common, $from, $to, $file_list);
177 $dependent = expand_macros
($line);
178 ($from, $to) = split(' ', $dependent);
179 print "copy dependent: from: $from, to: $to\n" if $is_debug;
180 glob_and_copy
($from, $to, $touch);
182 if ($delete_common && $common_build && ( $line !~ /%COMMON_OUTDIR%/ ) ) {
183 $line =~ s/%__SRC%/%COMMON_OUTDIR%/ig;
184 if ( $line =~ /%COMMON_OUTDIR%/ ) {
185 $line =~ s/%_DEST%/%COMMON_DEST%/ig;
186 $common = expand_macros
($line);
187 ($from, $to) = split(' ', $common);
188 print "copy common: from: $from, to: $to\n" if $is_debug;
189 glob_and_copy
($from, $to, $touch);
198 my $command = expand_macros
($line);
200 print "DOS: $command\n";
203 # HACK: remove MACOSX stuff which is wrongly labled with dos
204 # better: fix broken d.lst
205 return if ( $command =~ /MACOSX/ );
206 $command =~ s
#/#\\#g if $^O eq 'MSWin32';
213 # just collect all addincpath files, actual filtering is done later
216 my @globbed_files = ();
218 $line = expand_macros
($line);
219 ($from, $to) = split(' ', $line);
221 push( @addincpath_list, @
{glob_line
($from, $to)});
226 my ($lib_base, $lib_major,$from_dir, $to_dir);
228 my @globbed_files = ();
229 my %globbed_hash = ();
231 print "linklib: $lib\n" if $is_debug;
232 print "has symlinks\n" if ( $has_symlinks && $is_debug );
234 return unless $has_symlinks;
236 $from_dir = expand_macros
('../%__SRC%/lib');
237 $to_dir = expand_macros
('%_DEST%/lib%_EXT%');
239 @globbed_files = glob("$from_dir/$lib");
241 if ( $#globbed_files == -1 ) {
245 foreach $lib (@globbed_files) {
246 $lib = basename
($lib);
247 if ( $lib =~ /^(lib\S+(\.so|\.dylib))\.(\d+)\.(\d+)(\.(\d+))?$/
248 || $lib =~ /^(lib\S+(\.so|\.dylib))\.(\d+)$/ )
250 push(@
{$globbed_hash{$1}}, $lib);
253 print_warning
("invalid library name: $lib");
257 foreach $lib_base ( sort keys %globbed_hash ) {
258 $lib = get_latest_patchlevel
(@
{$globbed_hash{$lib_base}});
260 if ( $lib =~ /^(lib\S+(\.so|\.dylib))\.(\d+)\.(\d+)(\.(\d+))?$/ )
262 $lib_major = "$lib_base.$3";
267 # $lib =~ /^(lib[\w-]+(\.so|\.dylib))\.(\d+)$/;
273 print "REMOVE: $to_dir/$lib_major\n" if $long;
274 print "REMOVE: $to_dir/$lib_base\n";
277 print "LINKLIB: $to_dir/$lib -> $to_dir/$lib_major\n" if $long;
278 print "LINKLIB: $to_dir/$lib -> $to_dir/$lib_base\n";
283 print "REMOVE: $to_dir/$lib_major\n" if ($long && $opt_verbose);
284 print "REMOVE: $to_dir/$lib_base\n" if $opt_verbose;
285 unlink "$to_dir/$lib_major" if $long;
286 unlink "$to_dir/$lib_base";
288 push_on_ziplist
("$to_dir/$lib_major") if $long;
289 push_on_ziplist
("$to_dir/$lib_base");
297 @symlibs = ("$to_dir/$lib_major", "$to_dir/$lib_base");
301 @symlibs = ("$to_dir/$lib_base");
303 # remove old symlinks
305 foreach $symlib (@symlibs) {
306 print "LINKLIB: $lib -> $symlib\n" if $opt_verbose;
307 if ( !symlink("$lib", "$symlib") ) {
308 print_error
("can't symlink $lib -> $symlib: $!",0);
311 push_on_ziplist
($symlib) if $opt_zip;
312 push_on_loglist
("LINK", "$lib", "$symlib") if $opt_log;
321 my $path = expand_macros
(shift);
322 # strip whitespaces from path name
324 if (( ! $opt_delete ) && ( ! -d
$path )) {
326 print "MKDIR: $path\n";
328 mkpath
($path, 0, 0777-$umask);
330 print_error
("mkdir: could not create directory '$path'", 0);
340 $line = expand_macros
($line);
341 ($from, $to) = split(' ',$line);
342 my $fullfrom = $from;
343 if ( dirname
($from) eq dirname
($to) ) {
344 $from = basename
($from);
346 elsif ( dirname
($from) eq '.' ) {
350 print_error
("symlink: link must be in the same directory as file",0);
354 print "symlink: $from, to: $to\n" if $is_debug;
356 return unless $has_symlinks;
360 print "REMOVE: $to\n";
363 print "SYMLINK $from -> $to\n";
367 print "REMOVE: $to\n" if $opt_verbose;
370 push_on_ziplist
($to) if $opt_zip;
373 return unless -e
$fullfrom;
374 print "SYMLIB: $from -> $to\n" if $opt_verbose;
375 if ( !symlink("$from", "$to") ) {
376 print_error
("can't symlink $from -> $to: $!",0);
379 push_on_ziplist
($to) if $opt_zip;
380 push_on_loglist
("LINK", "$from", "$to") if $opt_log;
391 $line = expand_macros
($line);
392 ($from, $to) = split(' ', $line);
393 print "touch: $from, to: $to\n" if $is_debug;
394 glob_and_copy
($from, $to, $touch);
397 #### subroutines #####
402 my $dontdeletecommon = 0;
403 $opt_silent = 1 if ( !defined $ENV{VERBOSE
} || (defined $ENV{VERBOSE
} && $ENV{VERBOSE
} eq 'FALSE'));
404 $opt_verbose = 1 if ( defined $ENV{VERBOSE
} && $ENV{VERBOSE
} eq 'TRUE');
405 while ( $arg = shift @ARGV ) {
406 $arg =~ /^-force$/ and $opt_force = 1 and next;
407 $arg =~ /^-minor$/ and $opt_minor = 1 and next;
408 $arg =~ /^-check$/ and $opt_check = 1 and $opt_verbose = 1 and next;
409 $arg =~ /^-quiet$/ and $opt_silent = 1 and next;
410 $arg =~ /^-verbose$/ and $opt_verbose = 1 and next;
411 $arg =~ /^-zip$/ and $opt_zip = 1 and next;
412 $arg =~ /^-delete$/ and $opt_delete = 1 and next;
413 $arg =~ /^-dontdeletecommon$/ and $dontdeletecommon = 1 and next;
414 $arg =~ /^-help$/ and $opt_help = 1 and $arg = '';
415 $arg =~ /^-link$/ and $ENV{GUI
} ne 'WNT' and $opt_link = 1 and next;
416 $arg =~ /^-deloutput$/ and $opt_deloutput = 1 and next;
417 $arg =~ /^-debug$/ and $is_debug = 1 and next;
418 $arg =~ /^-checkdlst$/ and $opt_checkdlst = 1 and next;
419 print_error
("invalid option $arg") if ( $arg =~ /^-/ );
420 if ( $arg =~ /^-/ || $opt_help || $#ARGV > -1 ) {
425 # $dest and $opt_zip or $opt_delete are mutually exclusive
426 if ( $dest and ($opt_zip || $opt_delete) ) {
429 # $opt_silent and $opt_check or $opt_verbose are mutually exclusive
430 if ( ($opt_check or $opt_verbose) and $opt_silent ) {
431 print STDERR
"Error on command line: options '-check' and '-quiet' are mutually exclusive.\n";
434 if ($dontdeletecommon) {
440 # $opt_delete implies $opt_force
441 $opt_force = 1 if $opt_delete;
447 ($module, $base_dir, $dlst_file) = get_base
();
450 $module =~ s/\.lnk$//;
452 print "Module=$module, Base_Dir=$base_dir, d.lst=$dlst_file\n" if $is_debug;
455 if ( !defined($umask) ) {
459 my $build_sosl = $ENV{'BUILD_SOSL'};
460 my $common_outdir = $ENV{'COMMON_OUTDIR'};
461 my $inpath = $ENV{'INPATH'};
462 my $solarversion = $ENV{'SOLARVERSION'};
463 my $updater = $ENV{'UPDATER'};
464 my $updminor = $ENV{'UPDMINOR'};
465 my $work_stamp = $ENV{'WORK_STAMP'};
467 # special security check for release engineers
468 if ( defined($updater) && !defined($build_sosl) && !$opt_force) {
470 if ( $path !~ /$work_stamp/io ) {
471 print_error
("can't deliver from local directory to SOLARVERSION");
472 print STDERR
"\nDANGER! Release Engineer:\n";
473 print STDERR
"do you really want to deliver from $path to SOLARVERSION?\n";
474 print STDERR
"If so, please use the -force switch\n\n";
479 # do we have a valid environment?
480 if ( !defined($inpath) ) {
481 print_error
("no environment", 0);
486 if ( ($opt_minor || $updminor) && !$dest ) {
491 print_error
("can't determine UPDMINOR", 0);
496 # Do we have common trees?
497 if ( defined($ENV{'common_build'}) && $ENV{'common_build'} eq 'TRUE' ) {
499 if ((defined $common_outdir) && ($common_outdir ne "")) {
500 $common_outdir = $common_outdir . ".pro" if $inpath =~ /\.pro$/;
502 $common_dest = $dest;
504 $common_dest = "$solarversion/$common_outdir";
505 $dest = "$solarversion/$inpath";
508 print_error
("common_build defined without common_outdir", 0);
512 $common_outdir = $inpath;
513 $dest = "$solarversion/$inpath" if ( !$dest );
514 $common_dest = $dest;
517 $common_dest =~ s
#\\#/#g;
519 # the following macros are obsolete, will be flagged as error
532 [ '%__PRJROOT%', $base_dir ],
533 [ '%__SRC%', $inpath ],
534 [ '%_DEST%', $dest ],
536 [ '%COMMON_OUTDIR%', $common_outdir ],
537 [ '%COMMON_DEST%', $common_dest ],
541 # find out if the system supports symlinks
542 $has_symlinks = eval { symlink("",""); 1 };
547 # a module base dir contains a subdir 'prj'
548 # which in turn contains a file 'd.lst'
549 my (@field, $base, $dlst);
552 @field = split(/\//, $path);
554 while ( $#field != -1 ) {
555 $base = join('/', @field);
556 $dlst = $base . '/prj/d.lst';
561 if ( $#field == -1 ) {
562 print_error
("can't determine module");
566 return ($field[-1], $base, $dlst);
573 open(DLST
, "<$dlst_file") or die "can't open d.lst";
579 if (!$delete_common && /%COMMON_DEST%/) {
580 # Just ignore all lines with %COMMON_DEST%
583 if ( /^\s*(\w+?):\s+(.*)$/ ) {
584 if ( !exists $action_hash{$1} ) {
585 print_error
("unknown action: \'$1\'", $line_cnt);
588 push(@action_data, [$1, $2]);
591 if ( /^\s*%(COMMON)?_DEST%\\/ ) {
592 # only copy from source dir to solver, not from solver to solver
593 print_warning
("illegal copy action, ignored: \'$_\'", $line_cnt);
596 push(@action_data, ['copy', $_]);
597 # for each ressource file (.res) copy its image list (.ilst)
600 $imagelist =~ s/\.res/\.$ilst_ext/g;
601 $imagelist =~ s/\\bin%_EXT%\\/\\res%_EXT%\\img\\/;
602 push(@action_data, ['copy', $imagelist]);
605 # call expand_macros()just to find any undefined macros early
606 # real expansion is done later
607 expand_macros
($_, $line_cnt);
614 # expand all macros and change backslashes to slashes
616 my $line_cnt = shift;
619 for ($i=0; $i<=$#macros; $i++) {
620 $line =~ s/$macros[$i][0]/$macros[$i][1]/gi
622 if ( $line =~ /(%\w+%)/ ) {
623 if ( $1 ne '%OS%' ) { # %OS% looks like a macro but is not ...
624 print_error
("unknown/obsolete macro: \'$1\'", $line_cnt);
633 # all actions have to be excuted relative to the prj directory
634 chdir("$base_dir/prj");
635 # dispatch depending on action type
636 for (my $i=0; $i <= $#action_data; $i++) {
637 &{"do_".$action_data[$i][0]}($action_data[$i][1]);
638 if ( $action_data[$i][0] eq 'mkdir' ) {
639 # fill array with (possibly) created directories in
640 # revers order for removal in 'cleanup'
641 unshift @dirlist, $action_data[$i][1];
652 my @globbed_files = ();
654 if ( ! ( $from && $to ) ) {
655 print_warning
("Error in d.lst? source: '$from' destination: '$to'");
656 return \
@globbed_files;
659 if ( $to =~ /[\*\?\[\]]/ ) {
661 ($to_fname, $to_dir) = fileparse
($to);
665 if ( $from =~ /[\*\?\[\]]/ ) {
666 # globbing necessary, no renaming possible
668 my @file_list = glob($from);
670 foreach $file ( @file_list ) {
671 my ($fname, $dir) = fileparse
($file);
672 my $copy = ($replace) ?
$to_dir . $fname : $to . '/' . $fname;
673 push(@globbed_files, [$file, $copy]);
677 # no globbing but renaming possible
678 push(@globbed_files, [$from, $to]);
680 if ( $opt_checkdlst ) {
681 my $outtree = expand_macros
("%__SRC%");
682 my $commonouttree = expand_macros
("%COMMON_OUTDIR%");
683 if (( $from !~ /\Q$outtree\E/ ) && ( $from !~ /\Q$commonouttree\E/ )) {
684 print_warning
("'$from' does not match any file") if ( $#globbed_files == -1 );
687 return \
@globbed_files;
697 my @copy_files = @
{glob_line
($from, $to)};
699 for (my $i = 0; $i <= $#copy_files; $i++) {
700 next if filter_out
($copy_files[$i][0]); # apply copy filter
701 copy_if_newer
($copy_files[$i][0], $copy_files[$i][1], $touch)
702 ?
$files_copied++ : $files_unchanged++;
707 my $file_name = shift;
710 if (-f
$file_name.$maybedot) {
711 my $file_type = `file $file_name`;
712 # OS X file command doesn't know if a file is stripped or not
713 if (($file_type =~ /not stripped/o) || ($file_type =~ /Mach-O/o) ||
714 (($file_type =~ /PE/o) && ($ENV{GUI
} eq 'WNT') &&
715 ($nm_output = `nm $file_name 2>&1`) && $nm_output &&
716 !($nm_output =~ /no symbols/i) && !($nm_output =~ /not recognized/i))) {
717 return '1' if ($file_name =~ /\.bin$/o);
718 return '1' if ($file_name =~ /\.so\.*/o);
719 return '1' if ($file_name =~ /\.dylib\.*/o);
720 return '1' if ($file_name =~ /\.com\.*/o);
721 return '1' if ($file_name =~ /\.dll\.*/o);
722 return '1' if ($file_name =~ /\.exe\.*/o);
723 return '1' if (basename
($file_name) !~ /\./o);
729 sub initialize_strip
{
730 if ((!defined $ENV{DISABLE_STRIP
}) || ($ENV{DISABLE_STRIP
} eq "")) {
731 $strip .= 'guw ' if ($^O
eq 'cygwin');
733 $strip .= " -x" if ($ENV{OS
} eq 'MACOSX');
734 $strip .= " -R '.comment' -s" if ($ENV{OS
} eq 'LINUX');
739 my $file_name = shift;
741 if (-f
$file_name && (( `file $file_name` ) =~ /Zip archive/o)) {
742 return '1' if ($file_name =~ /\.jar\.*/o);
749 if (system($command)) {
750 print_error
("Failed to execute $command");
757 my $temp_file = shift;
758 $temp_file =~ s/\/{2,}/\
//g;
759 my $rc = copy
($file, $temp_file);
760 execute_system
("$strip $temp_file");
766 # return 0 if file is unchanged ( for whatever reason )
767 # return 1 if file has been copied
774 print "testing $from, $to\n" if $is_debug;
775 push_on_ziplist
($to) if $opt_zip;
776 push_on_loglist
("COPY", "$from", "$to") if $opt_log;
777 return 0 unless ($from_stat_ref = is_newer
($from, $to, $touch));
780 print "REMOVE: $to\n" if $opt_verbose;
781 $rc = unlink($to) unless $opt_check;
782 # handle special packaging of *.dylib files for Mac OS X
783 if ( $to =~ s/\.dylib$/.jnilib/ ) {
784 print "REMOVE: $to\n" if $opt_verbose;
785 $rc += unlink "$to" unless $opt_check;
787 return 1 if $opt_check;
791 if( !$opt_check && $opt_link ) {
792 # hard link if possible
793 if( link($from, $to) ){
794 print "LINK: $from -> $to\n" if $opt_verbose;
800 print "TOUCH: $from -> $to\n" if $opt_verbose;
803 print "COPY: $from -> $to\n" if $opt_verbose;
806 return 1 if( $opt_check );
809 # copy to temporary file first and rename later
810 # to minimize the possibility for race conditions
811 local $temp_file = sprintf('%s.%d-%d', $to, $$, time());
813 if (($strip ne '') && (defined $ENV{PROEXT
}) && (is_unstripped
($from))) {
814 $rc = strip_target
($from, $temp_file);
816 $rc = copy
($from, $temp_file);
819 if ( is_newer
($temp_file, $from, 0) ) {
820 $rc = utime($$from_stat_ref[9], $$from_stat_ref[9], $temp_file);
822 print_warning
("can't update temporary file modification time '$temp_file': $!\n
823 Check file permissions of '$from'.",0);
826 fix_file_permissions
($$from_stat_ref[2], $temp_file);
829 $rc = unlink($to); # YD OS/2 can't rename if $to exists!
831 # Ugly hack: on windows file locking(?) sometimes prevents renaming.
832 # Until we've found and fixed the real reason try it repeatedly :-(
835 $maxtries = 5 if ( $^O
eq 'MSWin32' );
837 while ( $try < $maxtries && ! $success ) {
840 $success = rename($temp_file, $to);
843 # handle special packaging of *.dylib files for Mac OS X
844 if ( $^O
eq 'darwin' )
846 if ( $to =~ /\.dylib/ ) {
847 system("macosx-create-bundle", $to);
849 $bundlelib =~ s/\.dylib$//;
850 $bundlelib .= ".jnilib";
852 print "REMOVE: $bundlelib\n" if $opt_verbose;
853 unlink "$bundlelib" unless $opt_check;
855 push_on_ziplist
($bundlelib) if $opt_zip;
856 push_on_loglist
("LINK", basename
($to), "$bundlelib") if $opt_log;
859 system("macosx-create-bundle", "$to=$from.app") if ( -d
"$from.app" );
860 system("ranlib", "$to" ) if ( $to =~ /\.a/ );
863 print_warning
("File '$to' temporarily locked. Dependency bug?");
868 print_error
("can't rename temporary file to $to: $!",0);
872 print_error
("can't copy $from: $!",0);
873 my $destdir = dirname
($to);
874 if ( ! -d
$destdir ) {
875 print_error
("directory '$destdir' does not exist", 0);
884 # returns whole stat buffer if newer
888 my (@from_stat, @to_stat);
890 @from_stat = stat($from.$maybedot);
891 if ( $opt_checkdlst ) {
892 my $outtree = expand_macros
("%__SRC%");
893 my $commonouttree = expand_macros
("%COMMON_OUTDIR%");
894 if ( $from !~ /$outtree/ ) {
895 if ( $from !~ /$commonouttree/ ) {
896 print_warning
("'$from' does not exist") unless -e _
;
900 return 0 unless -f _
;
903 $from_stat[9] = time();
905 # adjust timestamps to even seconds
906 # this is necessary since NT platforms have a
907 # 2s modified time granularity while the timestamps
908 # on Samba volumes have a 1s granularity
910 $from_stat[9]-- if $from_stat[9] % 2;
912 if ( $to =~ /^\Q$dest\E/ ) {
913 if ( $from_stat[9] > $logfiledate ) {
914 $logfiledate = $from_stat[9];
916 } elsif ( $common_build && ( $to =~ /^\Q$common_dest\E/ ) ) {
917 if ( $from_stat[9] > $commonlogfiledate ) {
918 $commonlogfiledate = $from_stat[9];
922 @to_stat = stat($to.$maybedot);
923 return \
@from_stat unless -f _
;
929 return ($from_stat[9] > $to_stat[9]) ? \
@from_stat : 0;
937 foreach my $pattern ( @copy_filter_patterns ) {
938 if ( $file =~ /$pattern/ ) {
939 print "filter out: $file\n" if $is_debug;
947 sub fix_file_permissions
952 if ( ($mode >> 6) % 2 == 1 ) {
953 $mode = 0777 & ~$umask;
956 $mode = 0666 & ~$umask;
961 sub get_latest_patchlevel
963 # note: feed only well formed library names to this function
964 # of the form libfoo.so.x.y.z with x,y,z numbers
966 my @sorted_files = sort by_rev
@_;
967 return $sorted_files[-1];
970 # comparison function for sorting
971 my (@field_a, @field_b, $i);
973 $a =~ /^(lib[\w-]+(\.so|\.dylib))\.(\d+)\.(\d+)\.(\d+)$/;
974 @field_a = ($3, $4, $5);
975 $b =~ /^(lib[\w-]+(\.so|\.dylib))\.(\d+)\.(\d+)\.(\d+)$/;
976 @field_b = ($3, $4, $5);
978 for ($i = 0; $i < 3; $i++)
980 if ( ($field_a[$i] < $field_b[$i]) ) {
983 if ( ($field_a[$i] > $field_b[$i]) ) {
994 sub push_default_actions
996 # any default action (that is an action which must be done even without
997 # a corresponding d.lst entry) should be pushed here on the
1011 push(@subdirs, 'zip') if $opt_zip;
1012 push(@subdirs, 'idl') if ! $common_build;
1013 push(@subdirs, 'pus') if ! $common_build;
1014 my @common_subdirs = (
1022 push(@common_subdirs, 'zip') if $opt_zip;
1024 if ( ! $opt_delete ) {
1025 # create all the subdirectories on solver
1026 foreach $subdir (@subdirs) {
1027 push(@action_data, ['mkdir', "%_DEST%/$subdir%_EXT%"]);
1029 if ( $common_build ) {
1030 foreach $subdir (@common_subdirs) {
1031 push(@action_data, ['mkdir', "%COMMON_DEST%/$subdir%_EXT%"]);
1035 push(@action_data, ['mkdir', "%_DEST%/inc%_EXT%/$module"]);
1036 if ( $common_build ) {
1037 push(@action_data, ['mkdir', "%COMMON_DEST%/inc%_EXT%/$module"]);
1038 push(@action_data, ['mkdir', "%COMMON_DEST%/res%_EXT%/img"]);
1040 push(@action_data, ['mkdir', "%_DEST%/res%_EXT%/img"]);
1043 # deliver build.lst to $dest/inc/$module
1044 push(@action_data, ['copy', "build.lst %_DEST%/inc%_EXT%/$module/build.lst"]);
1045 if ( $common_build ) {
1046 # ... and to $common_dest/inc/$module
1047 push(@action_data, ['copy', "build.lst %COMMON_DEST%/inc%_EXT%/$module/build.lst"]);
1050 # need to copy libstaticmxp.dylib for Mac OS X
1051 if ( $^O
eq 'darwin' )
1053 push(@action_data, ['copy', "../%__SRC%/lib/lib*static*.dylib %_DEST%/lib%_EXT%/lib*static*.dylib"]);
1057 sub walk_addincpath_list
1059 my (@addincpath_headers);
1060 return if $#addincpath_list == -1;
1062 # create hash with all addincpath header names
1063 for (my $i = 0; $i <= $#addincpath_list; $i++) {
1064 my @field = split('/', $addincpath_list[$i][0]);
1065 push (@addincpath_headers, $field[-1]);
1068 # now stream all addincpath headers through addincpath filter
1069 for (my $i = 0; $i <= $#addincpath_list; $i++) {
1070 add_incpath_if_newer
($addincpath_list[$i][0], $addincpath_list[$i][1], \
@addincpath_headers)
1071 ?
$files_copied++ : $files_unchanged++;
1075 sub add_incpath_if_newer
1079 my $modify_headers_ref = shift;
1080 my ($from_stat_ref, $header);
1082 push_on_ziplist
($to) if $opt_zip;
1083 push_on_loglist
("ADDINCPATH", "$from", "$to") if $opt_log;
1085 if ( $opt_delete ) {
1086 print "REMOVE: $to\n" if $opt_verbose;
1087 my $rc = unlink($to);
1092 if ( $from_stat_ref = is_newer
($from, $to) ) {
1093 print "ADDINCPATH: $from -> $to\n" if $opt_verbose;
1095 return 1 if $opt_check;
1099 open(FROM
, "<$from");
1100 # slurp whole file in one big string
1101 my $content = <FROM
>;
1105 foreach $header (@
$modify_headers_ref) {
1106 $content =~ s/#include [<"]$header[>"]/#include <$module\/$header>/g
;
1113 utime($$from_stat_ref[9], $$from_stat_ref[9], $to);
1114 fix_file_permissions
($$from_stat_ref[2], $to);
1123 return if ( $opt_check );
1124 # strip $dest from path since we don't want to record it in zip file
1125 if ( $file =~ s
#^\Q$dest\E/##o ) {
1127 # strip minor from path
1129 $ext = expand_macros
($ext);
1130 $file =~ s
#^$ext##o;
1132 push(@zip_list, $file);
1133 } elsif ( $file =~ s
#^\Q$common_dest\E/##o ) {
1135 # strip minor from path
1137 $ext = expand_macros
($ext);
1138 $file =~ s
#^$ext##o;
1140 push(@common_zip_list, $file);
1147 return 0 if ( $opt_check );
1148 return -1 if ( $#entry != 2 );
1149 if (( $entry[0] eq "COPY" ) || ( $entry[0] eq "ADDINCPATH" )) {
1150 return 0 if ( ! -e
$entry[1].$maybedot );
1151 # make 'from' relative to source root
1152 $entry[1] = $module . "/prj/" . $entry[1];
1153 $entry[1] =~ s/^$module\/prj\/\
.\
./$module/;
1155 # platform or common tree?
1157 if ( $entry[2] =~ /^\Q$dest\E/ ) {
1159 } elsif ( $common_build && ( $entry[2] =~ /^\Q$common_dest\E/ )) {
1162 warn "Neither common nor platform tree?";
1165 # make 'to' relative to SOLARVERSION
1166 my $solarversion = $ENV{'SOLARVERSION'};
1167 $solarversion =~ s
#\\#/#g;
1168 $entry[2] =~ s/^\Q$solarversion\E\///;
1169 # strip minor from 'to'
1171 $ext = expand_macros
($ext);
1172 $entry[2] =~ s
#$ext([\\\/])#$1#o;
1175 push @common_log_list, [@entry];
1177 push @log_list, [@entry];
1185 $zipexe .= ' -y' unless $^O
eq 'MSWin32';
1187 my ($platform_zip_file, $common_zip_file);
1188 $platform_zip_file = "%_DEST%/zip%_EXT%/$module.zip";
1189 $platform_zip_file = expand_macros
($platform_zip_file);
1190 my (%dest_dir, %list_ref);
1191 $dest_dir{$platform_zip_file} = $dest;
1192 $list_ref{$platform_zip_file} = \
@zip_list;
1193 if ( $common_build ) {
1194 $common_zip_file = "%COMMON_DEST%/zip%_EXT%/$module.zip";
1195 $common_zip_file = expand_macros
($common_zip_file);
1196 $dest_dir{$common_zip_file} = $common_dest;
1197 $list_ref{$common_zip_file} = \
@common_zip_list;
1201 $ext = expand_macros
($ext);
1204 $zipfiles[0] = $platform_zip_file;
1205 if ( $common_build ) {
1206 push @zipfiles, ($common_zip_file);
1208 foreach my $zip_file ( @zipfiles ) {
1209 print "ZIP: updating $zip_file\n" if $opt_verbose;
1210 next if ( $opt_check );
1212 local $work_file = "";
1214 # We are delivering into a minor. Zip files must not contain the
1215 # minor extension, so we have to pre and post process it.
1217 # Pre process: add minor extension to path, create working copy in
1219 $work_file = get_tempfilename
() . ".zip";
1220 die "Error: temp file $work_file already exists" if ( -e
$work_file);
1221 zipped_path_extension
($zip_file, $work_file, $ext, 1) if ( -e
$zip_file );
1222 } elsif ( $zip_file eq $common_zip_file) {
1223 # Zip file in common tree: work on uniq copy to avoid collisions
1224 $work_file = $zip_file;
1225 $work_file =~ s/\.zip$//;
1226 $work_file .= (sprintf('.%d-%d', $$, time())) . ".zip";
1227 die "Error: temp file $work_file already exists" if ( -e
$work_file);
1228 if ( -e
$zip_file ) {
1229 if ( -z
$zip_file) {
1230 # sometimes there are files of 0 byte size - remove them
1231 unlink $zip_file or print_error
("can't remove empty file '$zip_file': $!",0);
1233 if ( ! copy
($zip_file, $work_file)) {
1234 # give a warning, not an error:
1235 # we can zip from scratch instead of just updating the old zip file
1236 print_warning
("can't copy'$zip_file' into '$work_file': $!", 0);
1242 # No pre processing necessary, working directly on solver.
1243 $work_file = $zip_file;
1246 # zip content has to be relative to $dest_dir
1247 chdir($dest_dir{$zip_file}) or die "Error: cannot chdir into $dest_dir{$zip_file}";
1248 my $this_ref = $list_ref{$zip_file};
1249 if ( $opt_delete ) {
1250 if ( -e
$work_file ) {
1251 open(UNZIP
, "unzip -t $work_file 2>&1 |") or die "error opening zip file";
1252 if ( grep /empty/, (<UNZIP
>)) {
1258 open(ZIP
, "| $zipexe -q -o -d -@ $work_file") or die "error opening zip file";
1259 foreach $file ( @
$this_ref ) {
1260 print "ZIP: removing $file from $platform_zip_file\n" if $is_debug;
1261 print ZIP
"$file\n";
1266 open(ZIP
, "| $zipexe -q -o -u -@ $work_file") or die "error opening zip file";
1267 foreach $file ( @
$this_ref ) {
1268 print "ZIP: adding $file to $zip_file\n" if $is_debug;
1269 print ZIP
"$file\n";
1274 # Post process: strip minor from stored path again
1275 zipped_path_extension
($work_file, $zip_file, $ext, 0);
1276 if (( -e
$work_file ) && ($work_file ne $zip_file)) {
1279 } elsif ( $zip_file eq $common_zip_file) {
1280 # rename work file back
1281 if ( -e
$work_file ) {
1282 if ( -e
$zip_file) {
1283 # do some tricks to be fast. otherwise we may disturb other platforms
1284 # by unlinking a file which just gets copied -> stale file handle.
1285 my $buffer_file=$work_file . '_rm';
1286 rename($zip_file, $buffer_file) or warn "Warning: can't rename old zip file '$zip_file': $!";
1287 if (! rename($work_file, $zip_file)) {
1288 print_error
("can't rename temporary file to $zip_file: $!",0);
1291 unlink $buffer_file;
1293 if (! rename($work_file, $zip_file)) {
1294 print_error
("can't rename temporary file to $zip_file: $!",0);
1303 sub zipped_path_extension
1304 # add given extension to or strip it from stored path
1306 require Archive
::Zip
; import Archive
::Zip
;
1307 my ($from, $to, $extension, $with_ext) = @_;
1309 $zip = Archive
::Zip
->new();
1311 die 'Error: zip read error' unless $zip->read( $from) == 0;
1315 foreach my $member ( $zip->members() ) {
1316 $name = $member->fileName();
1318 if ( $name !~ m
#$extension/# ) {
1319 $name =~ s
#^(.*?)/#$1$extension/#o;
1322 $name =~ s
#^(.*?)$extension/#$1/#o;
1324 $member->fileName( $name );
1325 if ( $member->lastModTime() ) {
1326 if ( $DateTime < $member->lastModTime() ) {
1327 $DateTime = $member->lastModTime();
1332 die 'Error: zip write error' unless $zip->overwrite( ) == 0;
1333 File
::Copy
::move
( $from, $to) or die "Error $!: cannot move $from $to";
1335 die 'Error: zip write error' unless $zip->writeToFileNamed( $to ) == 0;
1337 utime $DateTime, $DateTime, $to;
1339 die "Error: file $from does not exist" if ( ! $opt_delete);
1344 sub get_tempfilename
1346 my $temp_dir = shift;
1347 $temp_dir = ( -d
'/tmp' ?
'/tmp' : $ENV{TMPDIR
} || $ENV{TEMP
} || '.' )
1348 unless defined($temp_dir);
1349 if ( ! -d
$temp_dir ) {
1350 die "no temp directory $temp_dir\n";
1352 my $base_name = sprintf( "%d-%di-%d", $$, time(), $tempcounter++ );
1353 return "$temp_dir/$base_name";
1358 my (%log_file, %file_date);
1359 $log_file{\
@log_list} = "%_DEST%/inc%_EXT%/$module/deliver.log";
1360 $log_file{\
@common_log_list} = "%COMMON_DEST%/inc%_EXT%/$module/deliver.log";
1361 $file_date{\
@log_list} = $logfiledate;
1362 $file_date{\
@common_log_list} = $commonlogfiledate;
1364 my @logs = ( \
@log_list );
1365 push @logs, ( \
@common_log_list ) if ( $common_build );
1366 foreach my $log ( @logs ) {
1367 $log_file{$log} = expand_macros
( $log_file{$log} );
1368 if ( $opt_delete ) {
1369 print "LOG: removing $log_file{$log}\n" if $opt_verbose;
1370 next if ( $opt_check );
1371 unlink $log_file{$log};
1373 print "LOG: writing $log_file{$log}\n" if $opt_verbose;
1374 next if ( $opt_check );
1375 open( LOGFILE
, "> $log_file{$log}" ) or warn "Error: could not open log file.";
1376 foreach my $item ( @
$log ) {
1377 print LOGFILE
"@$item\n";
1380 utime($file_date{$log}, $file_date{$log}, $log_file{$log});
1382 push_on_ziplist
( $log_file{$log} ) if $opt_zip;
1392 # get all checkable actions to perform
1393 foreach my $action ( @action_data ) {
1394 my $path = expand_macros
( $$action[1] );
1395 if ( $$action[0] eq 'mkdir' ) {
1396 $createddir{$path} ++;
1397 } elsif (( $$action[0] eq 'copy' ) || ( $$action[0] eq 'addincpath' )) {
1398 my ($from, $to) = split(' ', $path);
1399 my ($to_fname, $to_dir);
1400 my $withwildcard = 0;
1401 if ( $from =~ /[\*\?\[\]]/ ) {
1404 ($to_fname, $to_dir) = fileparse
($to);
1405 if ( $withwildcard ) {
1406 if ( $to !~ /[\*\?\[\]]/ ) {
1411 $to_dir =~ s/[\\\/\s]$//;
1412 $destdir{$to_dir} ++;
1413 # Check: copy into non existing directory?
1414 if ( ! $createddir{$to_dir} ) {
1415 # unfortunately it is not so easy: it's OK if a subdirectory of $to_dir
1416 # gets created, because mkpath creates the whole tree
1417 foreach my $directory ( keys %createddir ) {
1418 if ( $directory =~ /^\Q$to_dir\E[\\\/]/ ) {
1419 $createddir{$to_dir} ++;
1423 print_warning
("Possibly copying into directory without creating in before: '$to_dir'")
1424 unless $createddir{$to_dir};
1426 # Check: overwrite file?
1428 if ( $destfile{$to} ) {
1429 print_warning
("Multiple entries copying to '$to'");
1439 # remove empty directories
1440 foreach my $path ( @dirlist ) {
1441 $path = expand_macros
($path);
1443 print "RMDIR: $path\n" if $opt_verbose;
1452 my $output_path = expand_macros
("../%__SRC%");
1453 if ( "$output_path" ne "../" ) {
1454 if ( rmtree
([$output_path], 0, 1) ) {
1455 print "Deleted output tree.\n" if $opt_verbose;
1458 print_error
("Error deleting output tree $output_path: $!",0);
1462 print_error
("Output not deleted - INPATH is not set");
1468 my $message = shift;
1471 print STDERR
"$script_name: ";
1473 print STDERR
"$dlst_file: ";
1476 print STDERR
"line $line: ";
1478 print STDERR
"WARNING: $message\n";
1483 my $message = shift;
1486 print STDERR
"$script_name: ";
1488 print STDERR
"$dlst_file: ";
1491 print STDERR
"line $line: ";
1493 print STDERR
"ERROR: $message\n";
1499 print "Module '$module' delivered ";
1501 print "with errors\n";
1503 print "successfully.";
1504 if ( $opt_delete ) {
1505 print " $files_copied files removed,";
1508 print " $files_copied files copied,";
1510 print " $files_unchanged files unchanged\n";
1516 # clean up on unexpected termination
1518 if ( defined($temp_file) && -e
$temp_file ) {
1521 if ( defined($work_file) && -e
$work_file ) {
1523 print STDERR
"$work_file removed\n";
1526 die "caught unexpected signal $sig, terminating ...";
1531 my $exit_code = shift;
1532 print STDERR
"Usage:\ndeliver [OPTION]... [DESTINATION-PATH]\n";
1533 print STDERR
"Options:\n";
1534 print STDERR
" -check just print what would happen, no actual copying of files\n";
1535 print STDERR
" -checkdlst be verbose about (possible) d.lst bugs\n";
1536 print STDERR
" -delete delete files (undeliver), use with care\n";
1537 print STDERR
" -deloutput remove the output tree after copying\n";
1538 print STDERR
" -force copy even if not newer\n";
1539 print STDERR
" -dontdeletecommon do not delete common files (for -delete option)\n";
1540 print STDERR
" -help print this message\n";
1541 if ( !defined($ENV{GUI
}) || $ENV{GUI
} ne 'WNT' ) {
1542 print STDERR
" -link hard link files into the solver to save disk space\n";
1544 print STDERR
" -minor deliver into minor (milestone)\n";
1545 print STDERR
" -quiet be quiet, only report errors\n";
1546 print STDERR
" -verbose be verbose\n";
1547 print STDERR
" -zip additionally create zip files of delivered content\n";
1548 print STDERR
"Option '-zip' and a destination-path are mutually exclusive.\n";
1549 print STDERR
"Options '-check' and '-quiet' are mutually exclusive.\n";
1553 # vim: set ts=4 shiftwidth=4 expandtab syntax=perl: