summaryrefslogtreecommitdiff
path: root/Build
diff options
context:
space:
mode:
Diffstat (limited to 'Build')
-rw-r--r--Build/source/texk/tests/TeXLive/TLUtils.pm386
1 files changed, 242 insertions, 144 deletions
diff --git a/Build/source/texk/tests/TeXLive/TLUtils.pm b/Build/source/texk/tests/TeXLive/TLUtils.pm
index 121b6db5f6e..0aab6a6248c 100644
--- a/Build/source/texk/tests/TeXLive/TLUtils.pm
+++ b/Build/source/texk/tests/TeXLive/TLUtils.pm
@@ -5,7 +5,7 @@
package TeXLive::TLUtils;
-my $svnrev = '$Revision: 50493 $';
+my $svnrev = '$Revision: 52568 $';
my $_modulerevision = ($svnrev =~ m/: ([0-9]+) /) ? $1 : "unknown";
sub module_revision { return $_modulerevision; }
@@ -38,7 +38,7 @@ C<TeXLive::TLUtils> -- utilities used in TeX Live infrastructure
TeXLive::TLUtils::wsystem($msg,@args);
TeXLive::TLUtils::xsystem(@args);
TeXLive::TLUtils::run_cmd($cmd);
- TeXLive::TLUtils::system_pipe($prog, $infile, $outfile, $removeIn, @extraargs);
+ TeXLive::TLUtils::system_pipe($prog, $infile, $outfile, $removeIn, @args);
=head2 File utilities
@@ -79,6 +79,19 @@ C<TeXLive::TLUtils> -- utilities used in TeX Live infrastructure
TeXLive::TLUtils::w32_remove_from_path($bindir, $multiuser);
TeXLive::TLUtils::setup_persistent_downloads();
+=head2 Logging and debugging
+
+ TeXLive::TLUtils::info($str1, ...); # output unless -q
+ TeXLive::TLUtils::debug($str1, ...); # output if -v
+ TeXLive::TLUtils::ddebug($str1, ...); # output if -vv
+ TeXLive::TLUtils::dddebug($str1, ...); # output if -vvv
+ TeXLive::TLUtils::log($str1, ...); # only to log file
+ TeXLive::TLUtils::tlwarn($str1, ...); # warn on stderr and log
+ TeXLive::TLUtils::tldie($str1, ...); # tlwarn and die
+ TeXLive::TLUtils::debug_hash($label, HASH); # warn stringified HASH
+ TeXLive::TLUtils::backtrace(); # return call stack as string
+ TeXLive::TLUtils::process_logging_options($texdir); # handle -q -v* -logfile
+
=head2 Miscellaneous
TeXLive::TLUtils::sort_uniq(@list);
@@ -114,7 +127,7 @@ C<TeXLive::TLUtils> -- utilities used in TeX Live infrastructure
# avoid -warnings.
our $PERL_SINGLE_QUOTE; # we steal code from Text::ParseWords
use vars qw(
- $::LOGFILENAME @::LOGLINES
+ $::LOGFILE $::LOGFILENAME @::LOGLINES
@::debug_hook @::ddebug_hook @::dddebug_hook @::info_hook
@::install_packages_hook @::warn_hook
$TeXLive::TLDownload::net_lib_avail
@@ -1088,18 +1101,25 @@ sub rmtree {
=item C<copy("-f", $file, $destfile)>
+=item C<copy("-L", $file, $destfile)>
+
Copy file C<$file> to directory C<$target_dir>, or to the C<$destfile>
-in the second case. No external programs are involved. Since we need
-C<sysopen()>, the Perl module C<Fcntl.pm> is required. The time stamps
-are preserved and symlinks are created on Unix systems. On Windows,
-C<(-l $file)> will never return 'C<true>' and so symlinks will be
-(uselessly) copied as regular files.
+if the first argument is C<"-f">. No external programs are involved.
+Since we need C<sysopen()>, the Perl module C<Fcntl.pm> is required. The
+time stamps are preserved and symlinks are created on Unix systems. On
+Windows, C<(-l $file)> will never return 'C<true>' and so symlinks will
+be (uselessly) copied as regular files.
+
+If the argument is C<"-L"> and C<$file> is a symlink, the link is
+dereferenced before the copying is done. (If both C<"-f"> and C<"-L">
+are desired, they must be given in that order, although the current code
+has no need to do this.)
C<copy> invokes C<mkdirhier> if target directories do not exist. Files
have mode C<0777> if they are executable and C<0666> otherwise, with
the set bits in I<umask> cleared in each case.
-C<$file> can begin with a file:/ prefix.
+C<$file> can begin with a C<file:/> prefix.
If C<$file> is not readable, we return without copying anything. (This
can happen when the database and files are not in perfect sync.) On the
@@ -1111,12 +1131,21 @@ that is a fatal error.
sub copy {
my $infile = shift;
my $filemode = 0;
+ my $dereference = 0;
if ($infile eq "-f") { # second argument is a file
$filemode = 1;
$infile = shift;
}
+ if ($infile eq "-L") {
+ $dereference = 1;
+ $infile = shift;
+ }
my $destdir=shift;
+ # while we're trying to figure out the versioned containers.
+ #debug("copy($infile, $destdir, filemode=$filemode)\n");
+ #debug("copy: backtrace:\n", backtrace(), "copy: end backtrace\n");
+
my $outfile;
my @stat;
my $mode;
@@ -1141,9 +1170,24 @@ sub copy {
die "mkdirhier($destdir) failed: $err\n" if ! $ret;
}
- if (-l "$infile") {
- symlink (readlink $infile, "$destdir/$filename")
- || die "symlink(readlink $infile, $destdir/$filename) failed: $!";
+ # if we should dereference, change $infile to refer to the link target.
+ if (-l $infile && $dereference) {
+ my $linktarget = readlink($infile);
+ # The symlink target should always be relative, and we need to
+ # prepend the directory containing the link in that case.
+ # (Although it should never happen, if the symlink target happens
+ # to already be absolute, do not prepend.)
+ if ($linktarget !~ m,^/,) {
+ $infile = Cwd::abs_path(dirname($infile)) . "/$linktarget";
+ }
+ }
+
+ if (-l $infile) {
+ my $linktarget = readlink($infile);
+ my $dest = "$destdir/$filename";
+ debug("TLUtils::copy: doing symlink($linktarget,$dest)"
+ . " [from readlink($infile)]\n");
+ symlink($linktarget, $dest) || die "symlink($linktarget,$dest) failed: $!";
} else {
if (! open (IN, $infile)) {
warn "open($infile) failed, not copying: $!";
@@ -2101,14 +2145,21 @@ sub w32_remove_from_path {
=pod
-=item C<check_file($what, $checksum, $checksize>
+=item C<check_file_and_remove($what, $checksum, $checksize>
-Remove C<$what> if either the given C<$checksum> or C<$checksize> does
-not agree. If a check argument is not given, that check is not performed.
+Remove the file C<$what> if either the given C<$checksum> or
+C<$checksize> for C<$what> does not agree with our recomputation using
+C<TLCrypto::tlchecksum> and C<stat>, respectively. If a check argument
+is not given, that check is not performed. If the checksums agree, the
+size is not checked. The return status is random.
+
+This unusual behavior (removing the given file) is because this is used
+for newly-downloaded files; see the calls in the C<unpack> routine
+(which is the only caller).
=cut
-sub check_file {
+sub check_file_and_remove {
my ($xzfile, $checksum, $checksize) = @_;
debug("check_file $xzfile, $checksum, $checksize\n");
if (!$checksum && !$checksize) {
@@ -2116,12 +2167,23 @@ sub check_file {
"available for $xzfile, cannot check integrity");
return;
}
+
+ # The idea is that if one of the tests fail, we want to save a copy of
+ # the input file for debugging. But we can't just omit removing the
+ # file, since the caller depends on the removal. So we copy it to a
+ # new temporary directory, which we want to persist, so can't use tl_tmpdir.
+ my $check_file_tmpdir = undef;
+
# only run checksum tests if we can actually compute the checksum
if ($checksum && ($checksum ne "-1") && $::checksum_method) {
my $tlchecksum = TeXLive::TLCrypto::tlchecksum($xzfile);
if ($tlchecksum ne $checksum) {
- tlwarn("TLUtils::check_file: removing $xzfile, checksums differ:\n");
- tlwarn("TLUtils::check_file: TL=$tlchecksum, arg=$checksum\n");
+ tlwarn("TLUtils::check_file: checksums differ for $xzfile:\n");
+ tlwarn("TLUtils::check_file: tlchecksum=$tlchecksum, arg=$checksum\n");
+ (undef,$check_file_tmpdir) = File::temp::tempdir("tlcheckfileXXXXXXXX");
+ tlwarn("TLUtils::check_file: removing $xzfile, "
+ . "but saving copy in $check_file_tmpdir\n");
+ copy($xzfile, $check_file_tmpdir);
unlink($xzfile);
return;
} else {
@@ -2134,7 +2196,14 @@ sub check_file {
my $filesize = (stat $xzfile)[7];
if ($filesize != $checksize) {
tlwarn("TLUtils::check_file: removing $xzfile, sizes differ:\n");
- tlwarn("TLUtils::check_file: TL=$filesize, arg=$checksize\n");
+ tlwarn("TLUtils::check_file: tlfilesize=$filesize, arg=$checksize\n");
+ if (!defined($check_file_tmpdir)) {
+ # the tmpdir should always be undefined, since we shouldn't get
+ # here if the checksums failed, but test anyway.
+ $check_file_tmpdir = File::temp::tempdir("tlcheckfileXXXXXXXX");
+ tlwarn("TLUtils::check_file: saving copy in $check_file_tmpdir\n");
+ copy($xzfile, $check_file_tmpdir);
+ }
unlink($xzfile);
return;
}
@@ -2149,10 +2218,11 @@ sub check_file {
If necessary, downloads C$what>, and then unpacks it into C<$targetdir>.
C<@opts> is assigned to a hash and can contain the following
-options: C<tmpdir> (use this directory for downloaded files),
+keys: C<tmpdir> (use this directory for downloaded files),
C<checksum> (check downloaded file against this checksum),
C<size> (check downloaded file against this size),
C<remove> (remove temporary files after operation).
+
Returns a pair of values: in case of error return 0 and an additional
explanation, in case of success return 1 and the name of the package.
@@ -2202,26 +2272,26 @@ sub unpack {
# we are installing from the NET
# check for the presence of $what in $tempdir
if (-r $containerfile) {
- check_file($containerfile, $checksum, $size);
+ check_file_and_remove($containerfile, $checksum, $size);
}
# if the file is now not present, we can use it
if (! -r $containerfile) {
# try download the file and put it into temp
if (!download_file($what, $containerfile)) {
- return(0, "downloading did not succeed");
+ return(0, "downloading did not succeed (download_file failed)");
}
# remove false downloads
- check_file($containerfile, $checksum, $size);
+ check_file_and_remove($containerfile, $checksum, $size);
if ( ! -r $containerfile ) {
- return(0, "downloading did not succeed");
+ return(0, "downloading did not succeed (check_file_and_remove failed)");
}
}
} else {
# we are installing from local compressed files
- # copy it to temp
- TeXLive::TLUtils::copy($what, $tempdir);
+ # copy it to temp with dereferencing of link target
+ TeXLive::TLUtils::copy("-L", $what, $tempdir);
- check_file($containerfile, $checksum, $size);
+ check_file_and_remove($containerfile, $checksum, $size);
if (! -r $containerfile) {
return (0, "consistency checks failed");
}
@@ -3210,124 +3280,21 @@ sub parse_AddFormat_line {
return %ret;
}
-
=back
-=head2 Miscellaneous
-
-Ideas from Fabrice Popineau's C<FileUtils.pm>.
-
-=over 4
-
-=item C<sort_uniq(@list)>
-
-The C<sort_uniq> function sorts the given array and throws away multiple
-occurrences of elements. It returns a sorted and unified array.
-
-=cut
-
-sub sort_uniq {
- my (@l) = @_;
- my ($e, $f, @r);
- $f = "";
- @l = sort(@l);
- foreach $e (@l) {
- if ($e ne $f) {
- $f = $e;
- push @r, $e;
- }
- }
- return @r;
-}
-
-
-=item C<push_uniq(\@list, @new_items)>
-
-The C<push_uniq> function pushes the last argument @ITEMS to the $LIST
-referenced by the first argument, if they are not already in the list.
-
-=cut
-
-sub push_uniq {
- my ($l, @new_items) = @_;
- for my $e (@new_items) {
- if (! &member($e, @$l)) {
- push (@$l, $e);
- }
- }
-}
-
-=item C<member($item, @list)>
-
-The C<member> function returns true if the first argument
-is also inclued in the list of the remaining arguments.
-
-=cut
-
-sub member {
- my $what = shift;
- return scalar grep($_ eq $what, @_);
-}
-
-
-=item C<merge_into(\%to, \%from)>
-
-Merges the keys of %from into %to.
-
-=cut
-
-sub merge_into {
- my ($to, $from) = @_;
- foreach my $k (keys %$from) {
- if (defined($to->{$k})) {
- push @{$to->{$k}}, @{$from->{$k}};
- } else {
- $to->{$k} = [ @{$from->{$k}} ];
- }
- }
-}
+=head2 Logging
+Logging and debugging messages.
-=item C<texdir_check($texdir)>
-
-Test whether installation with TEXDIR set to $texdir would succeed due to
-writing permissions.
+=item C<logit($out,$level,@rest)>
-Writable or not, we will not allow installation to the root
-directory (Unix) or the root of a drive (Windows).
+Internal routine to write message to both C<$out> (references to
+filehandle) and C<$::LOGFILE>, at level C<$level>, of concatenated items
+in C<@rest>. If the log file is not initialized yet, the message is
+saved to be logged later (unless the log file never comes into existence).
=cut
-sub texdir_check {
- my $texdir = shift;
- return 0 unless defined $texdir;
- # convert to absolute, for safer parsing.
- # The return value may still contain symlinks,
- # but no unnecessary terminating '/'.
- $texdir = tl_abs_path($texdir);
- return 0 unless defined $texdir;
- # also reject the root of a drive,
- # assuming that only the canonical form of the root ends with /
- return 0 if $texdir =~ m!/$!;
- # win32: for now, reject the root of a samba share
- return 0 if win32() && $texdir =~ m!^//[^/]+/[^/]+$!;
- my $texdirparent;
- my $texdirpparent;
-
- return dir_writable($texdir) if (-d $texdir);
- ($texdirparent = $texdir) =~ s!/[^/]*$!!;
- #print STDERR "Checking $texdirparent".'[/]'."\n";
- return dir_creatable($texdirparent) if -d dir_slash($texdirparent);
- # try another level up the tree
- ($texdirpparent = $texdirparent) =~ s!/[^/]*$!!;
- #print STDERR "Checking $texdirpparent".'[/]'."\n";
- return dir_creatable($texdirpparent) if -d dir_slash($texdirpparent);
- return 0;
-}
-
-
-# no newlines or spaces are added, multiple args are just concatenated.
-#
sub logit {
my ($out, $level, @rest) = @_;
_logit($out, $level, @rest) unless $::opt_quiet;
@@ -3353,7 +3320,6 @@ sub _logit {
}
}
-
=item C<info ($str1, $str2, ...)>
Write a normal informational message, the concatenation of the argument
@@ -3377,7 +3343,6 @@ sub info {
}
}
-
=item C<debug ($str1, $str2, ...)>
Write a debugging message, the concatenation of the argument strings.
@@ -3399,7 +3364,6 @@ sub debug {
}
}
-
=item C<ddebug ($str1, $str2, ...)>
Write a deep debugging message, the concatenation of the argument
@@ -3495,7 +3459,7 @@ sub tldie {
}
}
-=item C<debug_hash ($label, hash))>
+=item C<debug_hash ($label, HASH)>
Write LABEL followed by HASH elements, all on one line, to stderr.
If HASH is a reference, it is followed.
@@ -3520,7 +3484,26 @@ sub debug_hash {
warn "$str\n";
}
-
+=item C<backtrace()>
+
+Return call(er) stack, as a string.
+
+=cut
+
+sub backtrace {
+ my $ret = "";
+
+ my ($line, $subr);
+ my $stackframe = 1; # skip ourselves
+ while ((undef,$filename,$line,$subr) = caller ($stackframe)) {
+ # the undef is for the package, which is already included in $subr.
+ $ret .= " -> ${filename}:${line}: ${subr}\n";
+ $stackframe++;
+ }
+
+ return $ret;
+}
+
=item C<process_logging_options ($texdir)>
This function handles the common logging options for TeX Live scripts.
@@ -3593,6 +3576,118 @@ sub process_logging_options {
}
}
+=back
+
+=head2 Miscellaneous
+
+Some ideas from Fabrice Popineau's C<FileUtils.pm>.
+
+=over 4
+
+=item C<sort_uniq(@list)>
+
+The C<sort_uniq> function sorts the given array and throws away multiple
+occurrences of elements. It returns a sorted and unified array.
+
+=cut
+
+sub sort_uniq {
+ my (@l) = @_;
+ my ($e, $f, @r);
+ $f = "";
+ @l = sort(@l);
+ foreach $e (@l) {
+ if ($e ne $f) {
+ $f = $e;
+ push @r, $e;
+ }
+ }
+ return @r;
+}
+
+
+=item C<push_uniq(\@list, @new_items)>
+
+The C<push_uniq> function pushes the last argument @ITEMS to the $LIST
+referenced by the first argument, if they are not already in the list.
+
+=cut
+
+sub push_uniq {
+ my ($l, @new_items) = @_;
+ for my $e (@new_items) {
+ if (! &member($e, @$l)) {
+ push (@$l, $e);
+ }
+ }
+}
+
+=item C<member($item, @list)>
+
+The C<member> function returns true if the first argument
+is also inclued in the list of the remaining arguments.
+
+=cut
+
+sub member {
+ my $what = shift;
+ return scalar grep($_ eq $what, @_);
+}
+
+=item C<merge_into(\%to, \%from)>
+
+Merges the keys of %from into %to.
+
+=cut
+
+sub merge_into {
+ my ($to, $from) = @_;
+ foreach my $k (keys %$from) {
+ if (defined($to->{$k})) {
+ push @{$to->{$k}}, @{$from->{$k}};
+ } else {
+ $to->{$k} = [ @{$from->{$k}} ];
+ }
+ }
+}
+
+=item C<texdir_check($texdir)>
+
+Test whether installation with TEXDIR set to $texdir would succeed due to
+writing permissions.
+
+Writable or not, we will not allow installation to the root
+directory (Unix) or the root of a drive (Windows).
+
+=cut
+
+sub texdir_check {
+ my $texdir = shift;
+ return 0 unless defined $texdir;
+ # convert to absolute, for safer parsing.
+ # The return value may still contain symlinks,
+ # but no unnecessary terminating '/'.
+ $texdir = tl_abs_path($texdir);
+ return 0 unless defined $texdir;
+ # also reject the root of a drive,
+ # assuming that only the canonical form of the root ends with /
+ return 0 if $texdir =~ m!/$!;
+ # win32: for now, reject the root of a samba share
+ return 0 if win32() && $texdir =~ m!^//[^/]+/[^/]+$!;
+ my $texdirparent;
+ my $texdirpparent;
+
+ return dir_writable($texdir) if (-d $texdir);
+ ($texdirparent = $texdir) =~ s!/[^/]*$!!;
+ #print STDERR "Checking $texdirparent".'[/]'."\n";
+ return dir_creatable($texdirparent) if -d dir_slash($texdirparent);
+ # try another level up the tree
+ ($texdirpparent = $texdirparent) =~ s!/[^/]*$!!;
+ #print STDERR "Checking $texdirpparent".'[/]'."\n";
+ return dir_creatable($texdirpparent) if -d dir_slash($texdirpparent);
+ return 0;
+}
+
=pod
This function takes a single argument I<path> and returns it with
@@ -4165,6 +4260,7 @@ sub parse_line {
return(@pieces);
}
+
=item C<mktexupd ()>
Append entries to C<ls-R> files. Usage example:
@@ -4262,7 +4358,7 @@ sub mktexupd {
return $hash;
}
-
+
=item C<check_sys_user_mode($user,$sys,$tmfc, $tmfsc, $tmfv, $tmfsv)>
=cut
@@ -4314,6 +4410,7 @@ sub setup_sys_user_mode {
return ($texmfconfig, $texmfvar);
}
+
=item C<prepend_own_path()>
Prepend the location of the TeX Live binaries to the PATH environment
@@ -4335,7 +4432,7 @@ sub prepend_own_path {
}
}
-
+
=item C<repository_to_array($r)>
Return hash of tags to urls for space-separated list of repositories
@@ -4376,6 +4473,7 @@ sub repository_to_array {
return %r;
}
+
=item C<encode_json($ref)>
Returns the JSON representation of the object C<$ref> is pointing at.