diff options
-rwxr-xr-x | Master/texmf/scripts/texlive/tlmgr.pl | 348 |
1 files changed, 323 insertions, 25 deletions
diff --git a/Master/texmf/scripts/texlive/tlmgr.pl b/Master/texmf/scripts/texlive/tlmgr.pl index 3707b6b2acd..f5c0e190c49 100755 --- a/Master/texmf/scripts/texlive/tlmgr.pl +++ b/Master/texmf/scripts/texlive/tlmgr.pl @@ -2458,6 +2458,20 @@ sub action_recreate_tlpdb { # CHECK # +sub init_tltree { + my $svn = shift; + my $Master = $localtlpdb->root; + my $tltree = TeXLive::TLTREE->new ("svnroot" => $Master); + if ($svn) { + debug("Initializine TLTREE from svn\n"); + $tltree->init_from_svn; + } else { + debug("Initializine TLTREE from find\n"); + $tltree->init_from_files; + } + return($tltree); +} + sub action_check { my $svn = defined($opts{"use-svn"}) ? $opts{"use-svn"} : 0; my $what = shift @ARGV; @@ -2465,12 +2479,23 @@ sub action_check { init_local_db(); my $ret = 0; if ($what =~ m/^all/i) { - $ret ||= check_files($svn); - $ret ||= check_collections($svn); + my $tltree = init_tltree($svn); + $ret ||= check_files($tltree); + $ret ||= check_depends(); + $ret ||= check_executes(); + # we do NOT automatically run the runfiles check when called with "all" } elsif ($what =~ m/^files/i) { - $ret ||= check_files($svn); + my $tltree = init_tltree($svn); + $ret ||= check_files($tltree); } elsif ($what =~ m/^collections/i) { - $ret ||= check_collections($svn); + tlwarn("the \"collections\" check is replaced by the \"depends\" check.\n"); + $ret ||= check_depends(); + } elsif ($what =~ m/^depends/i) { + $ret ||= check_depends(); + } elsif ($what =~ m/^runfiles/i) { + $ret ||= check_runfiles(); + } elsif ($what =~ m/^executes/i) { + $ret ||= check_executes(); } else { print "No idea how to check that: $what\n"; } @@ -2480,7 +2505,7 @@ sub action_check { # check file coverage, roughly equivalent to tlpkg/bin/check-file-coverage # sub check_files { - my $svn = shift; + my $tltree = shift; my $ret = 0; my %filetopacks; my $Master = $localtlpdb->root; @@ -2554,14 +2579,6 @@ sub check_files { install-tl.bat$ install-tl.bat.manifest$ !; - my $tltree = TeXLive::TLTREE->new ("svnroot" => $Master); - if ($svn) { - debug("Initializine TLTREE from svn\n"); - $tltree->init_from_svn; - } else { - debug("Initializine TLTREE from find\n"); - $tltree->init_from_files; - } my %tltreefiles = %{$tltree->{'_allfiles'}}; my @tlpdbfiles = keys %filetopacks; my @nohit; @@ -2592,25 +2609,295 @@ sub check_files { return($ret); } -# check collections +# check runfiles # -sub check_collections { - my @missing = (); - for my $p ($localtlpdb->collections()) { - my $col = $localtlpdb->get_package($p); - for my $d ($col->depends) { - push @missing, "$d ($p)" if (!defined($localtlpdb->get_package($d))); +# Check there are no duplicated runtimes files +sub check_runfiles { + my $Master = $localtlpdb->root; + # build a list of all runtime files associated to 'normal' packages + # + (my $non_normal = `ls $Master/bin`) =~ s/\n/\$|/g; # binaries + $non_normal .= '^0+texlive|^bin-|^collection-|^scheme-|^texlive-'; + my @runtime_files = (); + foreach my $tlpn ($localtlpdb->list_packages) { + next if ($tlpn =~ /$non_normal/); + my $tlp = $localtlpdb->get_package($tlpn); + my @files = $tlp->runfiles; + if ($tlp->relocated) { for (@files) { s!^$TeXLive::TLConfig::RelocPrefix/!$TeXLive::TLConfig::RelocTree/!; } } + push @runtime_files, @files; + } + + # build the duplicates list + # + my @duplicates = (""); # just to use $duplicates[-1] freely + my $prev = ""; + foreach my $f (sort map { TeXLive::TLUtils::basename($_) } @runtime_files) { + push (@duplicates, $f) if (($f eq $prev) and not ($f eq $duplicates[-1])); + $prev = $f; + } + shift @duplicates; # get rid of the fake 1st value + + # @duplicates = ('8r-base.map', 'aer.sty', 'lm-ec.map'); #Â for debugging + + # check if duplicates are different files + # + foreach my $f (@duplicates) { + # assume tex4ht stuff is ok, and don't worry about Changes/README for now + # same for .xdy files + next if ($f =~ /(^Changes$|^README$|\.htf$|\.4hf$|\.xdy$)/); + # + my @copies = grep (/\/$f$/, @runtime_files); + # map files can be duplicated as long as copies don't concern the same engine + if ($f =~ /\.map$/) { + my $need_check = 0; + my $prev_dir = ""; + my @cop = @copies; # don't break the outside list + map { s#^texmf-dist/fonts/map/(.*?)/.*#$1# } @cop; + foreach my $dir (sort @cop ) { + last if ($need_check = ($dir eq $prev_dir)); + $prev_dir = $dir; + } + next unless $need_check; + } + # if all copies are identical, ok, else, complain + my $diff = 0; + for (my $i = 1; $i < scalar(@copies); $i++) { + if ($diff = system ("diff -q --strip-trailing-cr " + . "$Master/$copies[$i-1] $Master/$copies[$i] >/dev/null")) { + print "# $f\ndiff $Master/$copies[$i-1] $Master/$copies[$i]\n"; + last; + } + } + print join ("\n", @copies), "\n" if ($diff and (scalar(@copies) > 2)); + } +} + +# check executes +# +sub check_executes { + my $Master = $localtlpdb->root; + my (%maps,%langcodes,%fmtlines); + for my $pkg ($localtlpdb->list_packages) { + for my $e ($localtlpdb->get_package($pkg)->executes) { + if ($e =~ m/add(Mixed)?Map\s+(.*)$/) { + my $foo = $2; + chomp($foo); + push @{$maps{$foo}}, $pkg; + } elsif ($e =~ m/AddFormat\s+(.*)$/) { + my $foo = $1; + chomp($foo); + push @{$fmtlines{$foo}}, $pkg; + } elsif ($e =~ m/AddHyphen\s+.*\s+file=(\S+)\s*$/) { + my $foo = $1; + chomp($foo); + push @{$langcodes{$foo}}, $pkg; + } else { + warn "$pkg: unmatched execute: $e\n"; + } + } + } + my %badmaps; + foreach my $mf (keys %maps) { + my @found = $localtlpdb->find_file($mf); + if ($#found < 0) { + $badmaps{$mf} = $maps{$mf}; + } + if ($#found > 0) { + # we want to check for multiple inclusions + my %mapfn; + foreach my $foo (@found) { + $foo =~ m/^(.*):(.*)$/; + push @{$mapfn{$2}}, $1; + } + foreach my $k (keys %mapfn) { + my @bla = @{$mapfn{$k}}; + if ($#bla > 0) { + warn "map file $mf occurs multiple times (in pkg @bla)!\n"; + } + } + } + } + if (keys %badmaps) { + print "mentioned map file not occuring in any package:\n"; + foreach my $mf (keys %badmaps) { + print "\t$mf (execute in @{$badmaps{$mf}})\n"; } } - return(0) if (!@missing); - print "Packages listed in collections but not present:\n"; - for my $m (@missing) { - print "\t$m\n"; + my %badhyphcodes; + my %problemhyphen; + foreach my $lc (keys %langcodes) { + next if ($lc eq "zerohyph.tex"); + my @found = $localtlpdb->find_file("texmf-dist/tex/generic/hyph-utf8/loadhyph/$lc"); + if ($#found < 0) { + # try again this time search all packages + my @found = $localtlpdb->find_file("$lc"); + if ($#found < 0) { + $badhyphcodes{$lc} = $langcodes{$lc}; + } else { + $problemhyphen{$lc} = [ @found ]; + } + } + } + if (keys %badhyphcodes) { + print "mentioned hyphen loaders without file:\n"; + foreach my $mf (keys %badhyphcodes) { + print "\t$mf (execute in @{$badhyphcodes{$mf}})\n"; + } + } + # disable the echoing of problematic hyphens + #if (keys %problemhyphen) { + # print "hyphen files with possible problematic location:\n"; + # foreach my $mf (keys %problemhyphen) { + # print "\t$mf (@{$problemhyphen{$mf}})\n"; + # } + #} + # + # what should be checked for the executes? we could check + # - the existence of the engine in bin/i386-linux or all $arch + # - the existence of the format name link/bat + # - parse the options parameter and check for the inifile + # - rework the format definition that we have inifile=pdflatex.ini + # isn't the * unnecessary? + my %missingbins; + my %missingengines; + my %missinginis; + for (keys %fmtlines) { + my %r = TeXLive::TLUtils::parse_AddFormat_line("$_"); + if (defined($r{"error"})) { + die "$r{'error'}, parsing $_, package(s) @{$fmtlines{$_}}"; + } + my $opt = $r{"options"}; + my $engine = $r{"engine"}; + my $name = $r{"name"}; + my $mode = $r{"mode"}; + # special case for cont-en ... + next if ($name eq "cont-en"); + # we check that the name exist in bin/$arch + for my $a ($localtlpdb->available_architectures) { + my $f = "$Master/bin/$a/$name"; + if (!check_file($a, $f)) { + if ($a eq "i386-cygwin") { + # on cygwin the following can happen: xmltex -> pdftex, but + # there is only pdftex.exe, so -r does not succeed + if (-l $f) { + my $ld = readlink ($f); + if (!check_file($a, "$Master/bin/$a/$ld")) { + push @{$missingbins{$_}}, "bin/$a/$name" if $mode; + } + } else { + push @{$missingbins{$_}}, "bin/$a/$name" if $mode; + } + } else { + push @{$missingbins{$_}}, "bin/$a/$name" if $mode; + } + } + if (!check_file($a, "$Master/bin/$a/$engine")) { + push @{$missingengines{$_}}, "bin/$a/$engine" if $mode; + } + } + # check for the existence of the .ini file + # by using the last word in the options value + my $inifile = $opt; + # $inifile now contains "bla bla bla *file.ini" + # strip initial and trailing " + $inifile =~ s/^"(.*)"$/$1/; + # remove everything before the last space + $inifile =~ s/^.* ([^ ]*)$/$1/; + # remove the optional leading * + $inifile =~ s/^\*//; + my @found = $localtlpdb->find_file("$inifile"); + if ($#found < 0) { + $missinginis{$_} = "$inifile"; + } + } + if (keys %missinginis) { + print "mentioned ini files that cannot be found:\n"; + for my $i (keys %missinginis) { + print "\t $missinginis{$i} (execute: $i)\n"; + } + } + if (keys %missingengines) { + print "mentioned engine files that cannot be found:\n"; + for my $i (keys %missingengines) { + print "\t @{$missingengines{$i}}\n"; + } + } + if (keys %missingbins) { + print "mentioned bin files that cannot be found:\n"; + for my $i (keys %missingbins) { + print "\t @{$missingbins{$i}}\n"; + } + } +} + +sub check_file { + my ($a, $f) = @_; + if (-r $f) { + return 1; + } else { + # not -r, so check for the extensions .bat and .exe on w32 and cygwin + if (($a eq "win32") || ($a eq "i386-cygwin")) { + if (-r "$f.exe" || -r "$f.bat") { + return 1; + } + } + return 0; } - print "\n"; } +# check depends +# +sub check_depends { + my $ret = 0; + my $Master = $localtlpdb->root; + my %presentpkg; + for my $pkg ($localtlpdb->list_packages) { + $presentpkg{$pkg} = 1; + } + # list of collections. + my @colls = $localtlpdb->collections; + my @coll_deps = $localtlpdb->expand_dependencies("-no-collections",$localtlpdb,@colls); + my %coll_deps; + @coll_deps{@coll_deps} = (); # initialize hash with keys from list + + my (%wrong_dep, @no_dep); + for my $pkg ($localtlpdb->list_packages) { + # do not check any package starting with 00texlive. + next if $pkg =~ m/^00texlive/; + + # For each package, check that it is a dependency of some collection. + # Whatever is left in %coll_deps after this loop will be the problem + if (! exists $coll_deps{$pkg}) { + # Except that schemes and our special .win32 packages are ok. + push (@no_dep, $pkg) unless $pkg =~/^scheme-|\.win32$/; + } + + # For each dependency, check that we have a package. + for my $d ($localtlpdb->get_package($pkg)->depends) { + next if ($d =~ m/\.ARCH$/); + if (!defined($presentpkg{$d})) { + push (@{$wrong_dep{$d}}, $pkg); + } + } + } + + if (keys %wrong_dep) { + $ret++; + print "DEPENDS WITHOUT PACKAGES:\n"; + for my $d (keys %wrong_dep) { + print "$d occurring in ", @{$wrong_dep{$d}}, "\n"; + } + } + + if (@no_dep) { + $ret++; + print "PACKAGES NOT IN ANY COLLECTION: @no_dep\n"; + } + + return($ret); +} + # Subroutines galore. # @@ -3367,6 +3654,17 @@ are actually present, and lists those missing. Lists those packages which occur as dependencies in an installed collections, but are themselves not installed. Options: +=item B<executes> + +Check that the files referred to by C<execute> directives in the TeX +Live Database are present. + +=item B<runfiles> + +List those filenames that are occurring more than one time in the runfiles. +This test needs the C<diff> utility and so is not executed when C<check> +is called without or with the C<all> argument. + =back =over 4 |