From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- systems/texlive/tlnet/install-tl | 3176 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 3176 insertions(+) create mode 100644 systems/texlive/tlnet/install-tl (limited to 'systems/texlive/tlnet/install-tl') diff --git a/systems/texlive/tlnet/install-tl b/systems/texlive/tlnet/install-tl new file mode 100644 index 0000000000..68c6f20f18 --- /dev/null +++ b/systems/texlive/tlnet/install-tl @@ -0,0 +1,3176 @@ +#!/usr/bin/env perl +# $Id: install-tl 51840 2019-08-07 22:27:00Z karl $ +# Copyright 2007-2019 +# Reinhard Kotucha, Norbert Preining, Karl Berry, Siep Kroonenberg. +# This file is licensed under the GNU General Public License version 2 +# or any later version. +# +# Be careful when changing wording: *every* normal informational message +# output here must be recognized by the long grep in tl-update-tlnet. + +my $svnrev = '$Revision: 51840 $'; +$svnrev =~ m/: ([0-9]+) /; +$::installerrevision = ($1 ? $1 : 'unknown'); + +# taken from 00texlive.config: release, $tlpdb->config_release; +our $texlive_release; + +BEGIN { + $^W = 1; + my $Master; + my $me = $0; + $me =~ s!\\!/!g if $^O =~ /^MSWin/i; + if ($me =~ m!/!) { + ($Master = $me) =~ s!(.*)/[^/]*$!$1!; + } else { + $Master = "."; + } + $::installerdir = $Master; + + # All platforms: add the installer modules + unshift (@INC, "$::installerdir/tlpkg"); +} + +# debugging communication with external gui: use shared logfile + +our $dblfile = "/tmp/dblog"; +$dblfile = $ENV{'TEMP'} . "\\dblog.txt" if ($^O =~ /^MSWin/i); +$dblfile = $ENV{'TMPDIR'} . "/dblog" if ($^O eq 'darwin' + && exists $ENV{'TMPDIR'}); +sub dblog { + my $s = shift; + open(my $dbf, ">>", $dblfile); + print $dbf "PERL: $s\n"; + close $dbf; +} + + +# On unix, this run of install-tl may do duty as a wrapper for +# install-tl-gui.tcl, which in its turn will start an actual run of install-tl. +# Skip this wrapper block if we can easily rule out a tcl gui: + +if (($^O !~ /^MSWin/i) && + # this wrapper is only for unix, since windows has its own wrapper + ($#ARGV >= 0 || ($^O eq 'darwin')) && + # tcl parameter may still come, or tcl is default + ($#ARGV < 0 || $ARGV[0] ne '-from_ext_gui') + # this run is not invoked by tcl + ) { + + # make syntax uniform: --a => -a, a=b => 'a b' + my @tmp_args = (); + my $p; + my $i=-1; + while ($i<$#ARGV) { + $p = $ARGV[++$i]; + $p =~ s/^--/-/; + if ($p =~ /^(.*)=(.*)$/) { + push (@tmp_args, $1, $2); + } else { + push (@tmp_args, $p); + } + } + + # build argument array @new_args for install-tl-gui.tcl. + # '-gui' or '-gui tcl' will not be copied to @new_args. + # quit scanning and building @new_args once tcl is ruled out. + my $want_tcl = ($^O eq 'darwin'); + my $asked4tcl = 0; + my @new_args = (); + $i = -1; + while ($i < $#tmp_args) { + $p = $tmp_args[++$i]; + if ($p eq '-gui') { + # look ahead at next parameter + if ($i == $#tmp_args || $tmp_args[$i+1] =~ /^-/) { + $want_tcl = 1; # default value for -gui + $asked4tcl = 1; + } elsif ($tmp_args[$i+1] eq 'tcl') { + $want_tcl = 1; + $asked4tcl = 1; + $i++; + } else { # other value for -gui + $want_tcl = 0; + last; + } + } else { + my $forbid = 0; + for my $q (qw/in-place profile help/) { + if ($p eq "-$q") { + # default text mode + $want_tcl = 0 unless $asked4tcl; + last; + } + } + for my $q (qw/print-platform version no-gui/) { + if ($p eq "-$q") { + # enforce text mode + $want_tcl = 0; + $forbid = 1; + last; + } + } + last if $forbid; + # not gui-related, continue collecting @new_args + push (@new_args, $p); + } + } + # done scanning arguments + if ($want_tcl) { + unshift (@new_args, "--"); + unshift (@new_args, "$::installerdir/tlpkg/installer/install-tl-gui.tcl"); + my @wishes = qw /wish wish8.6 wish8.5 tclkit/; + unshift @wishes, $ENV{'WISH'} if (defined $ENV{'WISH'}); + foreach my $w (@wishes) { + if (!exec($w, @new_args)) { + next; # no return on successful exec + } + } + # no succesful exec of wish + } # else continue with main installer below +} +# end of wrapper block, start of the real installer + +use Cwd 'abs_path'; +use Getopt::Long qw(:config no_autoabbrev); +use Pod::Usage; +use POSIX (); + +use TeXLive::TLUtils qw(platform platform_desc sort_archs + which getenv win32 unix info log debug tlwarn ddebug tldie + member process_logging_options rmtree wsystem + mkdirhier make_var_skeleton make_local_skeleton install_package copy + install_packages dirname setup_programs native_slashify forward_slashify); +use TeXLive::TLPOBJ; +use TeXLive::TLPDB; +use TeXLive::TLConfig; +use TeXLive::TLCrypto; +use TeXLive::TLDownload; +use TeXLive::TLPaper; + +if (win32) { + require TeXLive::TLWinGoo; + TeXLive::TLWinGoo->import( qw( + &is_vista + &is_seven + &admin + &non_admin + ®_country + &expand_string + &get_system_path + &get_user_path + &setenv_reg + &unsetenv_reg + &adjust_reg_path_for_texlive + ®ister_extension + &unregister_extension + ®ister_file_type + &unregister_file_type + &broadcast_env + &update_assocs + &add_menu_shortcut + &remove_desktop_shortcut + &remove_menu_shortcut + &create_uninstaller + &maybe_make_ro + )); +} + +use strict; + +# global list of lines that get logged (see TLUtils.pm::_logit). +@::LOGLINES = (); + +# global list of warnings +@::WARNLINES = (); + +# we play around with the environment, place to keep original +my %origenv = (); + +# $install{$packagename} == 1 if it should be installed +my %install; + +# the different modules have to assign a code blob to this global variable +# which starts the installation. +# Example: In install-menu-text.pl there is +# $::run_menu = \&run_menu_text; +# +$::run_menu = sub { die "no UI defined." ; }; + +# the default scheme to be installed +my $default_scheme='scheme-full'; + +# common fmtutil args, keep in sync with tlmgr.pl. +our $common_fmtutil_args = + "--no-error-if-no-engine=$TeXLive::TLConfig::PartialEngineSupport"; + +# some arrays where the lists of collections to be installed are saved +# our for menus +our @collections_std; + +# The global variable %vars is an associative list which contains all +# variables and their values which can be changed by the user. +# needs to be our since TeXLive::TLUtils uses it +# +# The following values are taken from the remote tlpdb using the +# $tlpdb->tlpdbopt_XXXXX +# settings (i.e., taken from tlpkg/tlpsrc/00texlive.installation.tlpsrc +# +# 'tlpdbopt_sys_bin' => '/usr/local/bin', +# 'tlpdbopt_sys_man' => '/usr/local/man', +# 'tlpdbopt_sys_info' => '/usr/local/info', +# 'tlpdbopt_install_docfiles' => 1, +# 'tlpdbopt_install_srcfiles' => 1, +# 'tlpdbopt_create_formats' => 0, +our %vars=( # 'n_' means 'number of'. + 'this_platform' => '', + 'n_systems_available' => 0, + 'n_systems_selected' => 0, + 'n_collections_selected' => 0, + 'n_collections_available' => 0, + 'total_size' => 0, + 'src_splitting_supported' => 1, + 'doc_splitting_supported' => 1, + 'selected_scheme' => $default_scheme, + 'instopt_portable' => 0, + 'instopt_letter' => 0, + 'instopt_adjustrepo' => 1, + 'instopt_write18_restricted' => 1, + 'instopt_adjustpath' => 0, + ); + +my %path_keys = ( + 'TEXMFLOCAL' => 1, + 'TEXMFCONFIG' => 1, + 'TEXMFSYSCONFIG' => 1, + 'TEXMFVAR' => 1, + 'TEXMFSYSVAR' => 1, + 'TEXDIR' => 1, + 'TEXMFHOME' => 1, +); + +# option handling +my $opt_allow_ftp = 0; +my $opt_custom_bin; +my $opt_force_arch; +# tcl gui weeded out at start +my $opt_gui = "text"; +my $opt_help = 0; +my $opt_init_from_profile = ""; +my $opt_location = ""; +my $opt_no_gui = 0; +my $opt_nonadmin = 0; +my $opt_persistent_downloads = 1; +my $opt_portable = 0; +my $opt_print_arch = 0; +my $opt_profile = ""; +my $opt_scheme = ""; +my $opt_version = 0; +my $opt_warn_checksums = 1; +my $opt_font; +# unusual cases: +$::opt_select_repository = 0; +our $opt_in_place = 0; +# don't set this to a value, see below +my $opt_verify_downloads; + +# show all options even those not relevant for that arch +$::opt_all_options = 0; + +# default language for GUI installer +$::lang = "en"; + +# use the fancy directory selector for TEXDIR +# no longer used, although we still accept the parameter +$::alternative_selector = 0; + +# do not debug translations by default +$::debug_translation = 0; + +# some strings to describe the different meanings of tlpdbopt_file_assoc +$::fileassocdesc[0] = "None"; +$::fileassocdesc[1] = "Only new"; +$::fileassocdesc[2] = "All"; + +# before we try to interact with the user, we need to know whether or not +# install-tl was called from an external gui. This gui will start install-tl +# with "-from_ext_gui" as its first command-line option. + +my $from_ext_gui = 0; +if ((defined $ARGV[0]) && $ARGV[0] eq "-from_ext_gui") { + shift @ARGV; + $from_ext_gui = 1; + + # do not buffer output to the frontend + select(STDERR); + $| = 1; + select(STDOUT); + $| = 1; + + # windows: suppress console windows when invoking other programs + Win32::SetChildShowWindow(0) if win32(); + # ___, defined in this file, replaces the GUI translating function + *__ = \&::___; +} + +# if we find a file installation.profile we ask the user whether we should +# continue with the installation +# note, we are in the directory from which the aborted installation was run. +my %profiledata; +if (-r "installation.profile") { + if ($from_ext_gui) { # prepare for dialog interaction + print "mess_yesno\n"; + } + my $pwd = Cwd::getcwd(); + print "ABORTED TL INSTALLATION FOUND: installation.profile (in $pwd)\n"; + print + "Do you want to continue with the exact same settings as before (y/N): "; + print "\nendmess\n" if $from_ext_gui; + my $answer = ; + if ($answer =~ m/^y(es)?$/i) { + $opt_profile = "installation.profile"; + } +} + + +# first process verbosity/quiet options +process_logging_options(); +# now the others +GetOptions( + "all-options" => \$::opt_all_options, + "custom-bin=s" => \$opt_custom_bin, + "debug-translation" => \$::debug_translation, + "fancyselector" => \$::alternative_selector, + "force-platform|force-arch=s" => \$opt_force_arch, + "gui:s" => \$opt_gui, + "in-place" => \$opt_in_place, + "init-from-profile=s" => \$opt_init_from_profile, + "lang=s" => \$::opt_lang, + "gui-lang=s" => \$::opt_lang, + "location|url|repository|repos|repo=s" => \$opt_location, + "no-cls", # $::opt_no_cls in install-menu-text-pl + "no-gui" => \$opt_no_gui, + "non-admin" => \$opt_nonadmin, + "persistent-downloads!" => \$opt_persistent_downloads, + "portable" => \$opt_portable, + "print-platform|print-arch" => \$opt_print_arch, + "profile=s" => \$opt_profile, + "scheme=s" => \$opt_scheme, + "select-repository" => \$::opt_select_repository, + "font=s" => \$opt_font, + "tcl", # handled by wrapper + "verify-downloads!" => \$opt_verify_downloads, + "version" => \$opt_version, + "warn-checksums!" => \$opt_warn_checksums, + "help|?" => \$opt_help) or pod2usage(2); + +if ($opt_gui eq "expert") { + $opt_gui = "perltk"; +} +if ($opt_gui eq "" || $opt_gui eq "tcl") { + # tried and failed tcl, try perltk instead + $opt_gui = "perltk"; + warn "Setting opt_gui to perltk instead"; +} +if ($from_ext_gui) { + $opt_gui = "extl"; +} + +if ($opt_help) { + # theoretically we could make a subroutine with all the same + # painful checks as we do in tlmgr, but let's not bother until people ask. + my @noperldoc = (); + if (win32() || $ENV{"NOPERLDOC"}) { + @noperldoc = ("-noperldoc", "1"); + } + + # Tweak less stuff same as tlmgr, though. + # less can break control characters and thus the output of pod2usage + # is broken. We add/set LESS=-R in the environment and unset + # LESSPIPE and LESSOPEN to try to help. + # + if (defined($ENV{'LESS'})) { + $ENV{'LESS'} .= " -R"; + } else { + $ENV{'LESS'} = "-R"; + } + delete $ENV{'LESSPIPE'}; + delete $ENV{'LESSOPEN'}; + + pod2usage(-exitstatus => 0, -verbose => 2, @noperldoc); + die "sorry, pod2usage did not work; maybe a download failure?"; +} + +if ($opt_version) { + print "install-tl (TeX Live Cross Platform Installer)", + " revision $::installerrevision\n"; + if (open (REL_TL, "$::installerdir/release-texlive.txt")) { + # print first and last lines, which have the TL version info. + my @rel_tl = ; + print $rel_tl[0]; + print $rel_tl[$#rel_tl]; + close (REL_TL); + } + if ($::opt_verbosity > 0) { + print "Module revisions:"; + print "\nTLConfig: " . TeXLive::TLConfig->module_revision(); + print "\nTLCrypto: " . TeXLive::TLCrypto->module_revision(); + print "\nTLDownload: ".TeXLive::TLDownload->module_revision(); + print "\nTLPDB: " . TeXLive::TLPDB->module_revision(); + print "\nTLPOBJ: " . TeXLive::TLPOBJ->module_revision(); + print "\nTLTREE: " . TeXLive::TLTREE->module_revision(); + print "\nTLUtils: " . TeXLive::TLUtils->module_revision(); + print "\nTLWinGoo: " . TeXLive::TLWinGoo->module_revision() if win32(); + print "\n"; + } + exit 0; +} + +die "$0: Options custom-bin and in-place are incompatible.\n" + if ($opt_in_place && $opt_custom_bin); + +die "$0: Options profile and in-place are incompatible.\n" + if ($opt_in_place && $opt_profile); + +die "$0: Options init-from-profile and in-place are incompatible.\n" + if ($opt_in_place && $opt_init_from_profile); + +if ($#ARGV >= 0) { + # we still have arguments left, should only be gui, otherwise die + if ($ARGV[0] =~ m/^gui$/i) { + $opt_gui = "perltk"; + } else { + die "$0: Extra arguments `@ARGV'; try --help if you need it.\n"; + } +} + + +if (defined($::opt_lang)) { + $::lang = $::opt_lang; +} + +if ($opt_profile) { # for now, not allowed if in_place + if (-r $opt_profile) { + info("Automated TeX Live installation using profile: $opt_profile\n"); + } else { + $opt_profile = ""; + info( + "Profile $opt_profile not readable, continuing in interactive mode.\n"); + } +} + +if ($opt_nonadmin and win32()) { + non_admin(); +} + + +# the TLPDB instances we will use. $tlpdb is for the one from the installation +# media, while $localtlpdb is for the new installation +# $tlpdb must be our because it is used in install-menu-text.pl +our $tlpdb; +my $localtlpdb; +my $location; + +@::info_hook = (); + +our $media; +our @media_available; + +TeXLive::TLUtils::initialize_global_tmpdir(); + +# special uses of install-tl: +if ($opt_print_arch) { + print platform()."\n"; + exit 0; +} + +if (TeXLive::TLCrypto::setup_checksum_method()) { + # try to setup gpg: + # either explicitly requested or nothing requested + if ((defined($opt_verify_downloads) && $opt_verify_downloads) + || + (!defined($opt_verify_downloads))) { + if (TeXLive::TLCrypto::setup_gpg($::installerdir)) { + # make sure we actually do verify ... + $opt_verify_downloads = 1; + log("Trying to verify cryptographic signatures!\n") + } else { + if ($opt_verify_downloads) { + tldie("$0: No gpg found, but verification explicitly requested " + . "on command line, so quitting.\n"); + } else { + # implicitly requested, just + debug("Couldn't detect gpg so will proceed without verification!\n"); + } + } + } +} else { + if ($opt_warn_checksums) { + tldie(<new; $foo->destroy; }; + if ($@) { + tlwarn("perl/Tk unusable, cannot create main window.\n"); + if (platform() eq "universal-darwin") { + tlwarn("That could be because X11 is not installed or started.\n"); + } + tlwarn("Error message from creating MainWindow:\n"); + tlwarn(" $@\n"); + tlwarn("Continuing in text mode...\n"); + $opt_gui = "text"; + } else { + # try to set up fonts if $opt_gui is given + if ($opt_font) { + my @a; + push @a, "--font", $opt_font; + Tk::CmdLine::SetArguments(@a); + } + } + if ($opt_gui eq "text") { + # we switched from GUI to non-GUI mode, tell the user and wait a bit + tlwarn("\nSwitching to text mode installer, if you want to cancel, do it now.\n"); + tlwarn("Waiting for 3 seconds\n"); + sleep(3); + } +} + +if (defined($opt_force_arch)) { + tlwarn("Overriding platform to $opt_force_arch\n"); + $::_platform_ = $opt_force_arch; +} + +# initialize the correct platform +platform(); +$vars{'this_platform'} = $::_platform_; + +# we do not support cygwin < 1.7, so check for that +if (!$opt_custom_bin && (platform() eq "i386-cygwin")) { + chomp( my $un = `uname -r`); + if ($un =~ m/^(\d+)\.(\d+)\./) { + if ($1 < 2 && $2 < 7) { + tldie("$0: Sorry, the TL binaries require at least cygwin 1.7, " + . "not $1.$2\n"); + } + } +} + +# determine which media are available, don't put NET here, it is +# assumed to be available at any time +{ + # check the installer dir for what is present + my $tmp = $::installerdir; + $tmp = abs_path($tmp); + # remove trailing \ or / (e.g. root of dvd drive on w32) + $tmp =~ s,[\\\/]$,,; + if (-d "$tmp/$Archive") { + push @media_available, "local_compressed#$tmp"; + } + if (-r "$tmp/texmf-dist/web2c/texmf.cnf") { + push @media_available, "local_uncompressed#$tmp"; + } +} + +# check command line arguments if given +if ($opt_location) { + my $tmp = $opt_location; + if ($tmp =~ m!^(https?|ftp)://!i) { + push @media_available, "NET#$tmp"; + + } elsif ($tmp =~ m!^(rsync|)://!i) { + tldie ("$0: sorry, rsync unsupported; use an http or ftp url here.\n"); + + } else { + # remove leading file:/+ part + $tmp =~ s!^file://*!/!i; + $tmp = abs_path($tmp); + # remove trailing \ or / (e.g. root of dvd drive on w32) + $tmp =~ s,[\\\/]$,,; + if (-d "$tmp/$Archive") { + push @media_available, "local_compressed#$tmp"; + } + if (-d "$tmp/texmf-dist/web2c") { + push @media_available, "local_uncompressed#$tmp"; + } + } +} + +# find wget, tar, xz +if (!setup_programs ("$::installerdir/tlpkg/installer", "$::_platform_")) { + tldie("$0: Goodbye.\n"); +} + + +if ($opt_profile eq "") { + if ($opt_init_from_profile) { + read_profile("$opt_init_from_profile", seed => 1); + } + # do the normal interactive installation. + # + # here we could load different menu systems. Currently several things + # are "our" so that the menu implementation can use it. The $tlpdb, the + # %var, and all the @collection* + # install-menu-*.pl have to assign a code ref to $::run_menu which is + # run, and should change ONLY stuff in %vars + # The allowed keys in %vars should be specified somewhere ... + # the menu implementation should return + # MENU_INSTALL do the installation + # MENU_ABORT abort every action immediately, no cleanup + # MENU_QUIT try to quit and clean up mess + our $MENU_INSTALL = 0; + our $MENU_ABORT = 1; + our $MENU_QUIT = 2; + $opt_gui = "text" if ($opt_no_gui); + # finally do check for additional screens in the $opt_gui setting: + # format: + # --gui :,,... + # which will passed to run_menu (, , ...) + # + my @runargs; + if ($opt_gui =~ m/^([^:]*):(.*)$/) { + $opt_gui = $1; + @runargs = split ",", $2; + } + if (-r "$::installerdir/tlpkg/installer/install-menu-${opt_gui}.pl") { + require("installer/install-menu-${opt_gui}.pl"); + } else { + tlwarn("UI plugin $opt_gui not found,\n"); + tlwarn("Using text mode installer.\n"); + require("installer/install-menu-text.pl"); + } + + # before we start the installation we check for the existence of + # a previous installation, and in case we ship inform the UI + { + my $tlmgrwhich = which("tlmgr"); + if ($tlmgrwhich) { + my $dn = dirname($tlmgrwhich); + $dn = abs_path("$dn/../.."); + # The "make Karl happy" case, check that we are not running install-tl + # from the same tree where tlmgr is hanging around + my $install_tl_root = abs_path($::installerdir); + my $tlpdboldpath + = $dn . + "/$TeXLive::TLConfig::InfraLocation/$TeXLive::TLConfig::DatabaseName"; + if (-r $tlpdboldpath && $dn ne $install_tl_root) { + debug ("found old installation in $dn\n"); + push @runargs, "-old-installation-found=$dn"; + # only the text-mode menu will pay attention! + } + } + } + + my $ret = &{$::run_menu}(@runargs); + if ($ret == $MENU_QUIT) { + do_cleanup(); # log, profile, temp files + flushlog(); + exit(1); + } elsif ($ret == $MENU_ABORT) { + # here, omit do_cleanup() + flushlog(); + exit(2); + } + if ($ret != $MENU_INSTALL) { + tlwarn("Unknown return value of run_menu: $ret\n"); + exit(3); + } +} else { # no interactive setting of options + *__ = \&::___; + if (!do_remote_init()) { + die ("Exiting installation.\n"); + } + read_profile($opt_profile); + if ($from_ext_gui) { + print STDOUT "startinst\n"; + } +} + +my $varsdump = ""; +foreach my $key (sort keys %vars) { + my $val = $vars{$key} || ""; + $varsdump .= " $key: \"$val\"\n"; +} +log("Settings:\n" . $varsdump); + +# portable option overrides any system integration options +$vars{'instopt_adjustpath'} = 0 if $vars{'instopt_portable'}; +$vars{'tlpdbopt_file_assocs'} = 0 if $vars{'instopt_portable'}; +$vars{'tlpdbopt_desktop_integration'} = 0 if $vars{'instopt_portable'}; +install_warnlines_hook(); # collect warnings in @::WARNLINES +info("Installing to: $vars{TEXDIR}\n"); + +$::env_warns = ""; +create_welcome(); +my $status = 1; +if ($opt_gui eq 'text' or $opt_gui eq 'extl' or $opt_profile ne "" or + !(-r "$::installerdir/tlpkg/installer/tracked-install.pl")) { + $status = do_installation(); + if (@::WARNLINES) { + foreach my $t (@::WARNLINES) { print STDERR $t; } + } + if ($::env_warns) { tlwarn($::env_warns); } + unless ($ENV{"TEXLIVE_INSTALL_NO_WELCOME"} or $opt_gui eq 'extl') { + info(join("\n", @::welcome_arr)); + } + do_cleanup(); # sets $::LOGFILENAME if not already defined + if ($LOGFILENAME) { + print STDOUT "\nLogfile: $::LOGFILENAME\n"; + } else { + # do_cleanup sets $::LOGFILENAME to "" + #if no logfile could be written + print STDERR + "Cannot create logfile $vars{'TEXDIR'}/install-tl.log: $!\n"; + } + printf STDOUT "Installed on platform %s at %s\n", + $vars{'this_platform'}, $vars{'TEXDIR'} if ($opt_gui eq 'extl'); +} else { + require("installer/tracked-install.pl"); + $status = installer_tracker(); +} +exit $status; + + +################################################################### +# +# FROM HERE ON ONLY SUBROUTINES +# NO VARIABLE DECLARATIONS OR CODE +# +################################################################### + +# +# SETUP OF REMOTE STUFF +# +# this is now a sub since it is called from the ui plugins on demand +# this allows selecting a mirror first and then continuing + +sub only_load_remote { + my $selected_location = shift; + + # determine where we will find the distribution to install from. + # + $location = $opt_location; + $location = $selected_location if defined($selected_location); + $location || ($location = "$::installerdir"); + if ($location =~ m!^(ctan$|(https?|ftp)://)!i) { + # remove any trailing tlpkg[/texlive.tlpdb] or / + $location =~ s,/(tlpkg(/texlive\.tlpdb)?|archive)?/*$,,; + if ($location =~ m/^ctan$/i) { + $location = TeXLive::TLUtils::give_ctan_mirror(); + } elsif ($location =~ m/^$TeXLiveServerURL/) { + my $mirrorbase = TeXLive::TLUtils::give_ctan_mirror_base(); + $location =~ s,^($TeXLiveServerURL|ctan$),$mirrorbase,; + } + $TeXLiveURL = $location; + $media = 'NET'; + } else { + if (scalar grep($_ =~ m/^local_compressed/, @media_available)) { + $media = 'local_compressed'; + # for in_place option we want local_uncompressed media + $media = 'local_uncompressed' if $opt_in_place && + member('local_uncompressed', @media_available); + } elsif (scalar grep($_ =~ m/^local_uncompressed/, @media_available)) { + $media = 'local_uncompressed'; + } else { + if ($opt_location) { + # user gave a --location but nothing can be found there, so die + die "$0: cannot find installation source at $opt_location.\n"; + } + # no --location given, but NET installation + $TeXLiveURL = $location = TeXLive::TLUtils::give_ctan_mirror(); + $media = 'NET'; + } + } + if ($from_ext_gui) {print "location: $location\n";} + return load_tlpdb(); +} # only_load_remote + + +sub do_remote_init { + if (!only_load_remote(@_)) { + tlwarn("$0: Could not load TeX Live Database from $location, goodbye.\n"); + return 0; + } + if (!do_version_agree()) { + TeXLive::TLUtils::tldie <config_release; + if ($media eq "local_uncompressed") { + # existing installation may not have 00texlive.config metapackage + # so use TLConfig to establish what release we have + $texlive_release ||= $TeXLive::TLConfig::ReleaseYear; + } + + # if the release from the remote TLPDB does not agree with the + # TLConfig::ReleaseYear in the first 4 places break out here. + # Why only the first four places: some optional network distributions + # might use + # release/2009-foobar + if ($media eq "NET" + && $texlive_release !~ m/^$TeXLive::TLConfig::ReleaseYear/) { + return 0; + } else { + return 1; + } +} # do_version_agree + +sub final_remote_init { + info("Installing TeX Live $TeXLive::TLConfig::ReleaseYear from: $location" . + ($tlpdb->is_verified ? " (verified)" : " (not verified)") . "\n"); + + info("Platform: ", platform(), " => \'", platform_desc(platform), "\'\n"); + if ($opt_custom_bin) { + if (-d $opt_custom_bin && (-r "$opt_custom_bin/kpsewhich" + || -r "$opt_custom_bin/kpsewhich.exe")) { + info("Platform overridden, binaries taken from $opt_custom_bin\n" + . "and will be installed into .../bin/custom.\n"); + } else { + tldie("$0: -custom-bin argument must be a directory " + . "with TeX Live binaries, not like: $opt_custom_bin\n"); + } + } + if ($media eq "local_uncompressed") { + info("Distribution: live (uncompressed)\n"); + } elsif ($media eq "local_compressed") { + info("Distribution: inst (compressed)\n"); + } elsif ($media eq "NET") { + info("Distribution: net (downloading)\n"); + info("Using URL: $TeXLiveURL\n"); + TeXLive::TLUtils::setup_persistent_downloads() if $opt_persistent_downloads; + } else { + info("Distribution: $media\n"); + } + info("Directory for temporary files: $::tl_tmpdir\n"); + + if ($opt_in_place and ($media ne "local_uncompressed")) { + print "TeX Live not local or not decompressed; 'in_place' option not applicable\n"; + $opt_in_place = 0; + } elsif ( + $opt_in_place and (!TeXLive::TLUtils::texdir_check($::installerdir))) { + print "Installer dir not writable; 'in_place' option not applicable\n"; + $opt_in_place = 0; + } + $opt_scheme = "" if $opt_in_place; + $vars{'instopt_portable'} = $opt_portable; + $vars{'instopt_adjustpath'} = 1 if win32(); + + log("Installer revision: $::installerrevision\n"); + log("Database revision: " . $tlpdb->config_revision . "\n"); + + # correctly set the splitting support + # for local_uncompressed we always support splitting + if (($media eq "NET") || ($media eq "local_compressed")) { + $vars{'src_splitting_supported'} = $tlpdb->config_src_container; + $vars{'doc_splitting_supported'} = $tlpdb->config_doc_container; + } + set_platforms_supported(); + set_texlive_default_dirs(); + set_install_platform(); + initialize_collections(); + + # initialize the scheme from the command line value, if given. + if ($opt_scheme) { + # add the scheme- prefix if they didn't give it. + $opt_scheme = "scheme-$opt_scheme" if $opt_scheme !~ /^scheme-/; + my $scheme = $tlpdb->get_package($opt_scheme); + if (defined($scheme)) { + select_scheme($opt_scheme); # select it + } else { + tlwarn("Scheme $opt_scheme not defined, ignoring it.\n"); + } + } +} # final_remote_init + + +sub do_installation { + if (win32()) { + non_admin() if !$vars{'tlpdbopt_w32_multi_user'}; + } + if ($vars{'instopt_portable'}) { + $vars{'tlpdbopt_desktop_integration'} = 0; + $vars{'tlpdbopt_file_assocs'} = 0; + $vars{'instopt_adjustpath'} = 0; + $vars{'tlpdbopt_w32_multi_user'} = 0; + } + if ($vars{'selected_scheme'} ne "scheme-infraonly" + && $vars{'n_collections_selected'} <= 0) { + tldie("$0: Nothing selected, nothing to install, exiting!\n"); + } + # maybe_make_ro tests for admin, local drive and NTFS before proceeding. + # making the root read-only automatically locks everything below it. + # do TEXDIR now, before it loses its final slash + mkdirhier "$vars{'TEXDIR'}"; + if (win32()) { + TeXLive::TLWinGoo::maybe_make_ro ($vars{'TEXDIR'}); + } + # now remove final slash from TEXDIR even if it is the root of a drive + $vars{'TEXDIR'} =~ s!/$!!; + # do the actual installation + make_var_skeleton "$vars{'TEXMFSYSVAR'}"; + make_local_skeleton "$vars{'TEXMFLOCAL'}"; + mkdirhier "$vars{'TEXMFSYSCONFIG'}"; + if (win32()) { + TeXLive::TLWinGoo::maybe_make_ro ($vars{'TEXMFSYSVAR'}); + TeXLive::TLWinGoo::maybe_make_ro ($vars{'TEXMFLOCAL'}); + TeXLive::TLWinGoo::maybe_make_ro ($vars{'TEXMFSYSCONFIG'}); + } + + if ($opt_in_place) { + $localtlpdb = $tlpdb; + } else { + $localtlpdb=new TeXLive::TLPDB; + $localtlpdb->root("$vars{'TEXDIR'}"); + } + if (!$opt_in_place) { + # have to do top-level release-texlive.txt as a special file, so + # tl-update-images can insert the final version number without + # having to remake any packages. But if the source does not exist, + # or the destination already exists, don't worry about it (even + # though these cases should never arise); it's not that important. + # + if (-e "$::installerdir/release-texlive.txt" + && ! -e "$vars{TEXDIR}/release-texlive.txt") { + copy("$::installerdir/release-texlive.txt", "$vars{TEXDIR}/"); + } + # + calc_depends(); + save_options_into_tlpdb(); + # we need to do that dir, since we use the TLPDB->install_package which + # might change into texmf-dist for relocated packages + mkdirhier "$vars{'TEXDIR'}/texmf-dist"; + do_install_packages(); + if ($opt_custom_bin) { + $vars{'this_platform'} = "custom"; + my $TEXDIR="$vars{'TEXDIR'}"; + mkdirhier("$TEXDIR/bin/custom"); + for my $f (<$opt_custom_bin/*>) { + copy($f, "$TEXDIR/bin/custom"); + } + } + } + # now we save every scheme that is fully covered by the stuff we have + # installed to the $localtlpdb + foreach my $s ($tlpdb->schemes) { + my $stlp = $tlpdb->get_package($s); + die ("This cannot happen, $s not defined in tlpdb") if ! defined($stlp); + my $incit = 1; + foreach my $d ($stlp->depends) { + if (!defined($localtlpdb->get_package($d))) { + $incit = 0; + last; + } + } + if ($incit) { + $localtlpdb->add_tlpobj($stlp); + } + } + + # include a 00texlive.config package in the new tlpdb, + # so that further installations and updates using the new installation + # as the source can work. Only include the release info, the other + # 00texlive.config entries are not relevant for this case. + my $tlpobj = new TeXLive::TLPOBJ; + $tlpobj->name("00texlive.config"); + my $t = $tlpdb->get_package("00texlive.config"); + $tlpobj->depends("minrelease/" . $tlpdb->config_minrelease, + "release/" . $tlpdb->config_release); + $localtlpdb->add_tlpobj($tlpobj); + + $localtlpdb->save unless $opt_in_place; + + my $errcount = do_postinst_stuff(); + + #tlwarn("!!! Dummy test warning\n"); + #tlwarn("!!! Another test warning\n"); + #$errcount += 2; + + # check environment for possibly tex-related strings: + check_env() unless $ENV{"TEXLIVE_INSTALL_ENV_NOCHECK"}; + + # We do clean up in the main installation part + # don't do this here because it closes the log file and + # further messages (warnings, welcome) are not logged. + # log, profile, temp files: + # do_cleanup(); + + # create_welcome(); already invoked in main program + if (@::WARNLINES) { + unshift @::WARNLINES, ("\nSummary of warnings:\n"); + } + my $status = 0; + if ($errcount > 0) { + $status = 1; + warn "\n$0: errors in installation reported above\n"; + } + + return $status; +} # do_installation + +sub run_postinst_cmd { + my ($cmd) = @_; + + info ("running $cmd ..."); + my ($out,$ret) = TeXLive::TLUtils::run_cmd ("$cmd 2>&1"); + if ($ret == 0) { + info ("done\n"); + } else { + info ("failed\n"); + tlwarn ("$0: $cmd failed (status $ret): $!\n"); + $ret = 1; # be sure we don't overflow the sum on anything crazy + } + log ($out); + + return $ret; +} # run_postinst_cmd + + +# +# Make texmf.cnf, backup directory, cleanups, path setting, and +# (most importantly) post-install subprograms: mktexlsr, fmtutil, +# and more. Return count of errors detected, hopefully zero. +# +sub do_postinst_stuff { + my $TEXDIR = "$vars{'TEXDIR'}"; + my $TEXMFSYSVAR = "$vars{'TEXMFSYSVAR'}"; + my $TEXMFSYSCONFIG = "$vars{'TEXMFSYSCONFIG'}"; + my $TEXMFVAR = "$vars{'TEXMFVAR'}"; + my $TEXMFCONFIG = "$vars{'TEXMFCONFIG'}"; + my $TEXMFLOCAL = "$vars{'TEXMFLOCAL'}"; + my $tmv; + + do_texmf_cnf(); + + # clean up useless files in texmf-dist/tlpkg as this is only + # created by the relocatable packages + if (-d "$TEXDIR/$TeXLive::TLConfig::RelocTree/tlpkg") { + rmtree("$TEXDIR/TeXLive::TLConfig::RelocTree/tlpkg"); + } + + # create package backup directory for tlmgr autobackup to work + mkdirhier("$TEXDIR/$TeXLive::TLConfig::PackageBackupDir"); + + # final program execution + # we have to do several things: + # - clean the environment from spurious TEXMF related variables + # - add the bin dir to the PATH + # - select perl interpreter and set the correct perllib + # - run the programs + + # Step 1: Clean the environment. + %origenv = %ENV; + my @TMFVARS=qw(VARTEXFONTS + TEXMF SYSTEXMF VARTEXFONTS + TEXMFDBS WEB2C TEXINPUTS TEXFORMATS MFBASES MPMEMS TEXPOOL MFPOOL MPPOOL + PSHEADERS TEXFONTMAPS TEXPSHEADERS TEXCONFIG TEXMFCNF + TEXMFMAIN TEXMFDIST TEXMFLOCAL TEXMFSYSVAR TEXMFSYSCONFIG + TEXMFVAR TEXMFCONFIG TEXMFHOME TEXMFCACHE); + + if (defined($ENV{'TEXMFCNF'})) { + tlwarn "WARNING: environment variable TEXMFCNF is set. +You should know what you are doing. +We will unset it for the post-install actions, but all further +operations might be disturbed.\n\n"; + } + foreach $tmv (@TMFVARS) { + delete $ENV{$tmv} if (defined($ENV{$tmv})); + } + + # Step 2: Setup the PATH, switch to the new Perl + + my $pathsep = (win32)? ';' : ':'; + my $plat_bindir = "$TEXDIR/bin/$vars{'this_platform'}"; + my $perl_bindir = "$TEXDIR/tlpkg/tlperl/bin"; + my $perl_libdir = "$TEXDIR/tlpkg/tlperl/lib"; + my $progext = (win32)? '.exe' : ''; + + debug("Prepending $plat_bindir to PATH\n"); + $ENV{'PATH'} = $plat_bindir . $pathsep . $ENV{'PATH'}; + + if (win32) { + debug("Prepending $perl_bindir to PATH\n"); + $ENV{'PATH'} = "$perl_bindir" . "$pathsep" . "$ENV{'PATH'}"; + $ENV{'PATH'} =~ s!/!\\!g; + } + + debug("\nNew PATH is:\n"); + foreach my $dir (split $pathsep, $ENV{'PATH'}) { + debug(" $dir\n"); + } + debug("\n"); + if (win32) { + $ENV{'PERL5LIB'} = $perl_libdir; + } + + # + # post install actions + # + + my $usedtlpdb = $opt_in_place ? $tlpdb : $localtlpdb; + + if (win32()) { + debug("Actual environment:\n" . `set` ."\n\n"); + debug("Effective TEXMFCNF: " . `kpsewhich -expand-path=\$TEXMFCNF` ."\n"); + } + + # Step 4: run the programs + my $errcount = 0; + + if (!$opt_in_place) { + wsystem("running", 'mktexlsr', "$TEXDIR/texmf-dist") && exit(1); + } + + # we have to generate the various config file. That could be done with + # texconfig generate * but Win32 does not have texconfig. But we have + # $localtlpdb and this is simple code, so do it directly, i.e., duplicate + # the code from the various generate-*.pl scripts + + mkdirhier "$TEXDIR/texmf-dist/web2c"; + info("writing fmtutil.cnf to $TEXDIR/texmf-dist/web2c/fmtutil.cnf\n"); + TeXLive::TLUtils::create_fmtutil($usedtlpdb, + "$TEXDIR/texmf-dist/web2c/fmtutil.cnf"); + + # warn if fmtutil-local.cnf is present + if (-r "$TEXMFLOCAL/web2c/fmtutil-local.cnf") { + tlwarn("Old configuration file $TEXMFLOCAL/web2c/fmtutil-local.cnf found.\n"); + tlwarn("fmtutil now reads *all* fmtutil.cnf files, so probably the easiest way\nis to rename the above file to $TEXMFLOCAL/web2c/fmtutil.cnf\n"); + } + + + info("writing updmap.cfg to $TEXDIR/texmf-dist/web2c/updmap.cfg\n"); + TeXLive::TLUtils::create_updmap ($usedtlpdb, + "$TEXDIR/texmf-dist/web2c/updmap.cfg"); + + info("writing language.dat to $TEXMFSYSVAR/tex/generic/config/language.dat\n"); + TeXLive::TLUtils::create_language_dat($usedtlpdb, + "$TEXMFSYSVAR/tex/generic/config/language.dat", + "$TEXMFLOCAL/tex/generic/config/language-local.dat"); + + info("writing language.def to $TEXMFSYSVAR/tex/generic/config/language.def\n"); + TeXLive::TLUtils::create_language_def($usedtlpdb, + "$TEXMFSYSVAR/tex/generic/config/language.def", + "$TEXMFLOCAL/tex/generic/config/language-local.def"); + + info("writing language.dat.lua to $TEXMFSYSVAR/tex/generic/config/language.dat.lua\n"); + TeXLive::TLUtils::create_language_lua($usedtlpdb, + "$TEXMFSYSVAR/tex/generic/config/language.dat.lua", + "$TEXMFLOCAL/tex/generic/config/language-local.dat.lua"); + + wsystem("running", "mktexlsr", + $TEXMFSYSVAR, $TEXMFSYSCONFIG, "$TEXDIR/texmf-dist") + && exit(1); + + if (-x "$plat_bindir/updmap-sys$progext") { + $errcount += run_postinst_cmd("updmap-sys --nohash"); + } else { + info("not running updmap-sys (not installed)\n"); + } + + # now work through the options if specified at all + + # letter instead of a4 + if ($vars{'instopt_letter'}) { + # set paper size, but do not execute any post actions, which in this + # case would be mktexlsr and fmtutil-sys -all; clearly premature + # here in the installer. + info("setting default paper size to letter:\n"); + $errcount += run_postinst_cmd("tlmgr --no-execute-actions paper letter"); + } + + # option settings in launcher.ini + if (win32() && !$vars{'instopt_portable'}) { + if ($vars{'tlpdbopt_file_assocs'} != 1 || !$vars{'instopt_adjustpath'}) { + # create higher priority tlaunch.ini with adjusted settings + # whether or not launcher mode (desktop integration 2) + # was selected + rewrite_tlaunch_ini(); + } + } + + # now rerun mktexlsr for updmap-sys and tlmgr paper letter updates. + wsystem("re-running", "mktexlsr", $TEXMFSYSVAR, $TEXMFSYSCONFIG) && exit(1); + + if (win32() and !$vars{'instopt_portable'} and !$opt_in_place) { + if ($vars{'tlpdbopt_desktop_integration'} != 2) { + create_uninstaller($vars{'TEXDIR'}); + } else { + $errcount += wsystem ( + 'Running','tlaunch.exe', + admin() ? 'admin_inst_silent' : 'user_inst_silent'); + } + } + + # luatex/context setup. + if (exists($install{"context"}) && $install{"context"} == 1 + && -x "$plat_bindir/texlua$progext" + && !exists $ENV{"TEXLIVE_INSTALL_NO_CONTEXT_CACHE"}) { + info("setting up ConTeXt cache: "); + $errcount += run_postinst_cmd("mtxrun --generate"); + } + + # all formats option + if ($vars{'tlpdbopt_create_formats'}) { + if (-x "$plat_bindir/fmtutil-sys$progext") { + info("pre-generating all format files, be patient...\n"); + $errcount += run_postinst_cmd( + "fmtutil-sys $common_fmtutil_args --no-strict --all"); + } else { + info("not running fmtutil-sys (not installed)\n"); + } + } + + # do path adjustments: On Windows add/remove to PATH etc, + # on Unix set symlinks + # for portable, this option should be unset + # it should not be necessary to test separately for portable + $errcount += do_path_adjustments() if + $vars{'instopt_adjustpath'} and $vars{'tlpdbopt_desktop_integration'} != 2; + + # now do the system integration: + # on unix this means setting up symlinks + # on w32 this means settting registry values + # on both, we run the postaction directives of the tlpdb + # no need to test for portable or in_place: + # the menus (or profile?) should have set the required options + $errcount += do_tlpdb_postactions(); + + return $errcount; +} # do_postinst_stuff + + +# Run the post installation code in the postaction tlpsrc entries. +# Return number of errors found, or zero. + +sub do_tlpdb_postactions { + info ("running package-specific postactions\n"); + + # option settings already reflect portable- and in_place options. + my $usedtlpdb = $opt_in_place ? $tlpdb : $localtlpdb; + my $ret = 0; # n. of errors + + foreach my $package ($usedtlpdb->list_packages) { + # !!! alert: parameter 4 is menu shortcuts, parameter 5 does nothing !!! + if ($vars{'tlpdbopt_desktop_integration'}==2) { + # skip creation of shortcuts and file associations + if (!TeXLive::TLUtils::do_postaction( + "install", $usedtlpdb->get_package($package), + 0, 0, 0, $vars{'tlpdbopt_post_code'})) { $ret += 1; } + } else { + # create shortcuts and file associations + # according to corresponding options + if (!TeXLive::TLUtils::do_postaction( + "install", $usedtlpdb->get_package($package), + $vars{'tlpdbopt_file_assocs'}, + $vars{'tlpdbopt_desktop_integration'}, 0, + $vars{'tlpdbopt_post_code'})) { $ret += 1; } + } + } + # windows: alert the system about changed file associations + if (win32) { TeXLive::TLWinGoo::update_assocs(); } + info ("finished with package-specific postactions\n"); + return $ret; +} # do_tlpdb_postactions + +sub rewrite_tlaunch_ini { + # create a higher-priority copy of tlaunch.ini in TEXMFSYSVAR + # with appropriate settings in the General section + my $ret = 0; # n. of errors + + chomp( my $tmfmain = `kpsewhich -var-value=TEXMFMAIN` ) ; + chomp( my $tmfsysvar = `kpsewhich -var-value=TEXMFSYSVAR` ) ; + if (open IN, "$tmfmain/web2c/tlaunch.ini") { + my $eolsave = $/; + undef $/; + my $ini = ; + close IN; + # remove general section, if any + $ini =~ s/\r\n/\n/g; + $ini =~ s/\[general[^\[]*//si; + mkdirhier("$tmfsysvar/web2c"); + if (open OUT, ">", "$tmfsysvar/web2c/tlaunch.ini") { + my @fts = ('none', 'new', 'overwrite'); + $\ = "\n"; + print OUT $ini; + print OUT "[General]"; + print OUT "FILETYPES=$fts[$vars{'tlpdbopt_file_assocs'}]"; + print OUT "SEARCHPATH=$vars{'instopt_adjustpath'}\n"; + close OUT; + `mktexlsr $tmfsysvar`; + } else { + $ret += 1; + tlwarn("Cannot write modified tlaunch.ini\n"); + } + $/ = $eolsave; + } else { + $ret += 1; + tlwarn("Cannot open tlaunch.ini for reading\n"); + } + return $ret; +} # rewrite_tlaunch_ini + +sub do_path_adjustments { + my $ret = 0; + info ("running path adjustment actions\n"); + if (win32()) { + TeXLive::TLUtils::w32_add_to_path($vars{'TEXDIR'}.'/bin/win32', + $vars{'tlpdbopt_w32_multi_user'}); + broadcast_env(); + } else { + if ($F_OK != TeXLive::TLUtils::add_symlinks($vars{'TEXDIR'}, + $vars{'this_platform'}, + $vars{'tlpdbopt_sys_bin'}, $vars{'tlpdbopt_sys_man'}, + $vars{'tlpdbopt_sys_info'})) { + $ret = 1; + } + } + info ("finished with path adjustment actions\n"); + return $ret; +} # do_path_adjustments + + +# we have to adjust the texmf.cnf file to the paths set in the configuration! +sub do_texmf_cnf { + open(TMF,"<$vars{'TEXDIR'}/texmf-dist/web2c/texmf.cnf") + or die "$vars{'TEXDIR'}/texmf-dist/web2c/texmf.cnf not found: $!"; + my @texmfcnflines = ; + close(TMF); + + my @changedtmf = (); # install to disk: write only changed items + + my $yyyy = $TeXLive::TLConfig::ReleaseYear; + + # we have to find TEXMFLOCAL TEXMFSYSVAR and TEXMFHOME + foreach my $line (@texmfcnflines) { + if ($line =~ m/^TEXMFLOCAL\b/) { # don't find TEXMFLOCALEDIR + # by default TEXMFLOCAL = TEXDIR/../texmf-local, if this is the case + # we don't have to write a new setting. + my $deftmlocal = dirname($vars{'TEXDIR'}); + $deftmlocal .= "/texmf-local"; + if ("$vars{'TEXMFLOCAL'}" ne "$deftmlocal") { + push @changedtmf, "TEXMFLOCAL = $vars{'TEXMFLOCAL'}\n"; + } + } elsif ($line =~ m/^TEXMFSYSVAR/) { + if ("$vars{'TEXMFSYSVAR'}" ne "$vars{'TEXDIR'}/texmf-var") { + push @changedtmf, "TEXMFSYSVAR = $vars{'TEXMFSYSVAR'}\n"; + } + } elsif ($line =~ m/^TEXMFSYSCONFIG/) { + if ("$vars{'TEXMFSYSCONFIG'}" ne "$vars{'TEXDIR'}/texmf-config") { + push @changedtmf, "TEXMFSYSCONFIG = $vars{'TEXMFSYSCONFIG'}\n"; + } + } elsif ($line =~ m/^TEXMFVAR/) { + if ($vars{"TEXMFVAR"} ne "~/.texlive$yyyy/texmf-var") { + push @changedtmf, "TEXMFVAR = $vars{'TEXMFVAR'}\n"; + } + } elsif ($line =~ m/^TEXMFCONFIG/) { + if ("$vars{'TEXMFCONFIG'}" ne "~/.texlive$yyyy/texmf-config") { + push @changedtmf, "TEXMFCONFIG = $vars{'TEXMFCONFIG'}\n"; + } + } elsif ($line =~ m/^TEXMFHOME/) { + if ("$vars{'TEXMFHOME'}" ne "~/texmf") { + push @changedtmf, "TEXMFHOME = $vars{'TEXMFHOME'}\n"; + } + } elsif ($line =~ m/^OSFONTDIR/) { + if (win32()) { + push @changedtmf, "OSFONTDIR = \$SystemRoot/fonts//\n"; + } + } + } + + if ($vars{'instopt_portable'}) { + push @changedtmf, "ASYMPTOTE_HOME = \$TEXMFCONFIG/asymptote\n"; + } + + my ($TMF, $TMFLUA); + # we want to write only changes to texmf.cnf + # even for in_place installation + $TMF = ">$vars{'TEXDIR'}/texmf.cnf"; + open(TMF, $TMF) || die "open($TMF) failed: $!"; + print TMF <= 5.14; + } + print TMF <$filename"; + $fh = \*VARS; + } + foreach my $key (keys %vars) { + print $fh "$key $vars{$key}\n"; + } + close VARS if (!ref($filename)); + debug("\n%vars dumped to '$filename'.\n"); +} # dump_vars + + +# Determine which platforms are supported. +sub set_platforms_supported { + my @binaries = $tlpdb->available_architectures; + for my $binary (@binaries) { + unless (defined $vars{"binary_$binary"}) { + $vars{"binary_$binary"}=0; + } + } + for my $key (keys %vars) { + ++$vars{'n_systems_available'} if ($key=~/^binary/); + } +} # set_platforms_supported + +# Environment variables and default values on UNIX: +# TEXLIVE_INSTALL_PREFIX /usr/local/texlive => $tex_prefix +# TEXLIVE_INSTALL_TEXDIR $tex_prefix/2010 => $TEXDIR +# TEXLIVE_INSTALL_TEXMFSYSVAR $TEXDIR/texmf-var +# TEXLIVE_INSTALL_TEXMFSYSCONFIG $TEXDIR/texmf-config +# TEXLIVE_INSTALL_TEXMFLOCAL $tex_prefix/texmf-local +# TEXLIVE_INSTALL_TEXMFHOME '$HOME/texmf' +# TEXLIVE_INSTALL_TEXMFVAR ~/.texlive2010/texmf-var +# TEXLIVE_INSTALL_TEXMFCONFIG ~/.texlive2010/texmf-config + +sub set_var_from_alternatives { + my ($whatref, @alternatives) = @_; + my $final; + while (@alternatives) { + my $el = pop @alternatives; + $final = $el if ($el); + } + $$whatref = $final; +} + +sub set_standard_var { + my ($what, $envstr, $default) = @_; + # warn if a value was set from both the profile and + # via env var + my $envvar = getenv($envstr); + if ($vars{$what} && $envvar && $vars{$what} ne $envvar) { + tlwarn("Trying to define $what via conflicting settings:\n"); + tlwarn(" from envvar $envvar = $envvar($envstr)\n"); + tlwarn(" from profile = $vars{$what}\n"); + tlwarn(" Preferring the profile value!\n"); + $envvar = undef; + } + # default for most variables is in increasing priority + # - some default + # - setting from profile saved already in $vars{$what} + # - environment variable + set_var_from_alternatives( \$vars{$what}, + $envvar, + $vars{$what}, + $default); +} + +sub set_texlive_default_dirs { + my $homedir = (platform() =~ m/darwin/) ? "~/Library" : "~"; + my $yyyy = $TeXLive::TLConfig::ReleaseYear; + # + my $tlprefixenv = getenv('TEXLIVE_INSTALL_PREFIX'); + if ($tlprefixenv && $vars{'TEXDIR'}) { + # NOTE we cannot compare these two values because the one might + # contain the YYYY part (TEXDIR) while the other is the one without. + tlwarn("Trying to set up basic path using two incompatible methods:\n"); + tlwarn(" from envvar TEXLIVE_INSTALL_PREFIX = $tlprefixenv\n"); + tlwarn(" from profile TEXDIR = $vars{'TEXDIR'}\n"); + tlwarn(" Preferring the profile value!\n"); + $tlprefixenv = undef; + } + # first set $tex_prefix + my $tex_prefix; + set_var_from_alternatives( \$tex_prefix, + ($opt_in_place ? abs_path($::installerdir) : undef), + $tlprefixenv, + (win32() ? getenv('SystemDrive') . '/texlive' : '/usr/local/texlive')); + set_var_from_alternatives( \$vars{'TEXDIR'}, + $vars{'TEXDIR'}, + ($vars{'instopt_portable'} || $opt_in_place) + ? $tex_prefix : "$tex_prefix/$texlive_release"); + set_standard_var('TEXMFSYSVAR', 'TEXLIVE_INSTALL_TEXMFSYSVAR', + $vars{'TEXDIR'} . '/texmf-var'); + set_standard_var('TEXMFSYSCONFIG', 'TEXLIVE_INSTALL_TEXMFSYSCONFIG', + $vars{'TEXDIR'} . '/texmf-config'); + set_standard_var('TEXMFLOCAL', 'TEXLIVE_INSTALL_TEXMFLOCAL', + "$tex_prefix/texmf-local"); + set_standard_var('TEXMFHOME', 'TEXLIVE_INSTALL_TEXMFHOME', + "$homedir/texmf"); + set_standard_var('TEXMFVAR', 'TEXLIVE_INSTALL_TEXMFVAR', + (platform() =~ m/darwin/) + ? "$homedir/texlive/$yyyy/texmf-var" + : "$homedir/.texlive$yyyy/texmf-var"); + set_standard_var('TEXMFCONFIG', 'TEXLIVE_INSTALL_TEXMFCONFIG', + (platform() =~ m/darwin/) + ? "$homedir/texlive/$yyyy/texmf-config" + : "$homedir/.texlive$yyyy/texmf-config"); + + # for portable installation we want everything in one directory + if ($vars{'instopt_portable'}) { + $vars{'TEXMFHOME'} = "\$TEXMFLOCAL"; + $vars{'TEXMFVAR'} = "\$TEXMFSYSVAR"; + $vars{'TEXMFCONFIG'} = "\$TEXMFSYSCONFIG"; + } +} # set_texlive_default_dirs + +sub calc_depends { + # we have to reset the install hash EVERY TIME otherwise everything will + # always be installed since the default is scheme-full which selects + # all packages and never deselects it + %install=(); + my $p; + my $a; + + # initialize the %install hash with what should be installed + + if ($vars{'selected_scheme'} ne "scheme-custom") { + # First look for packages in the selected scheme. + my $scheme=$tlpdb->get_package($vars{'selected_scheme'}); + if (!defined($scheme)) { + if ($vars{'selected_scheme'}) { + # something is written in the selected scheme but not defined, that + # is strange, so warn and die + die ("Scheme $vars{'selected_scheme'} not defined, vars:\n"); + dump_vars(\*STDOUT); + } + } else { + for my $scheme_content ($scheme->depends) { + $install{"$scheme_content"}=1 unless ($scheme_content=~/^collection-/); + } + } + } + + # Now look for collections in the %vars hash. These are not + # necessarily the collections required by a scheme. The final + # decision is made in the collections/languages menu. + foreach my $key (keys %vars) { + if ($key=~/^collection-/) { + $install{$key} = 1 if $vars{$key}; + } + } + + # compute the list of archs to be installed + my @archs; + foreach (keys %vars) { + if (m/^binary_(.*)$/ ) { + if ($vars{$_}) { push @archs, $1; } + } + } + + # + # work through the addon settings in the %vars hash + #if ($vars{'addon_editor'}) { + # $install{"texworks"} = 1; + #} + + # if programs for arch=win32 are installed we also have to install + # tlperl.win32 which provides the "hidden" perl that will be used + # to run all the perl scripts. + # Furthermore we install tlgs.win32 and tlpsv.win32, too + if (grep(/^win32$/,@archs)) { + $install{"tlperl.win32"} = 1; + $install{"tlgs.win32"} = 1; + $install{"tlpsv.win32"} = 1; + } + + # loop over all the packages until it is getting stable + my $changed = 1; + while ($changed) { + # set $changed to 0 + $changed = 0; + + # collect the already selected packages + my @pre_selected = keys %install; + debug("initial number of installations: $#pre_selected\n"); + + # loop over all the pre_selected and add them + foreach $p (@pre_selected) { + ddebug("pre_selected $p\n"); + my $pkg = $tlpdb->get_package($p); + if (!defined($pkg)) { + tlwarn("$p is mentioned somewhere but not available, disabling it.\n"); + $install{$p} = 0; + next; + } + foreach my $p_dep ($tlpdb->get_package($p)->depends) { + if ($p_dep =~ m/^(.*)\.ARCH$/) { + my $foo = "$1"; + foreach $a (@archs) { + $install{"$foo.$a"} = 1 if defined($tlpdb->get_package("$foo.$a")); + } + } elsif ($p_dep =~ m/^(.*)\.win32$/) { + # a win32 package should *only* be installed if we are installing + # the win32 arch + if (grep(/^win32$/,@archs)) { + $install{$p_dep} = 1; + } + } else { + $install{$p_dep} = 1; + } + } + } + + # check for newly selected packages + my @post_selected = keys %install; + debug("number of post installations: $#post_selected\n"); + + # set repeat condition + if ($#pre_selected != $#post_selected) { + $changed = 1; + } + } + + # now do the size computation + my $size = 0; + foreach $p (keys %install) { + my $tlpobj = $tlpdb->get_package($p); + if (not(defined($tlpobj))) { + tlwarn("$p should be installed but " + . "is not in texlive.tlpdb; disabling.\n"); + $install{$p} = 0; + next; + } + $size+=$tlpobj->docsize if $vars{'tlpdbopt_install_docfiles'}; + $size+=$tlpobj->srcsize if $vars{'tlpdbopt_install_srcfiles'}; + $size+=$tlpobj->runsize; + foreach $a (@archs) { + $size += $tlpobj->binsize->{$a} if defined($tlpobj->binsize->{$a}); + } + } + $vars{'total_size'} = + sprintf "%d", ($size * $TeXLive::TLConfig::BlockSize)/1024**2; +} # calc_depends + +sub load_tlpdb { + my $master = $location; + info("Loading $master/$TeXLive::TLConfig::InfraLocation/$TeXLive::TLConfig::DatabaseName\n"); + $tlpdb = TeXLive::TLPDB->new( + root => $master, 'verify' => $opt_verify_downloads); + if (!defined($tlpdb)) { + my $do_die = 1; + # if that failed, and: + # - we are installing from the network + # - the location string does not contain "tlnet" + # then we simply add "/systems/texlive/tlnet" in case someone just + # gave an arbitrary CTAN mirror address without the full path + if ($media eq "NET" && $location !~ m/tlnet/) { + tlwarn("First attempt for net installation failed;\n"); + tlwarn(" repository url does not contain \"tlnet\",\n"); + tlwarn(" retrying with \"/systems/texlive/tlnet\" appended.\n"); + $location .= "/systems/texlive/tlnet"; + $master = $location; + # + # since we change location, we reset the error count of the + # download object + $::tldownload_server->enable if defined($::tldownload_server); + # + $tlpdb = TeXLive::TLPDB->new( + root => $master, 'verify' => $opt_verify_downloads); + if (!defined($tlpdb)) { + tlwarn("Oh well, adding tlnet did not help.\n"); + tlwarn(<option($o) + if (!defined($profiledata{"tlpdbopt_$o"})); + } + if (win32()) { + # below, we really mean (start) menu integration. + # 2016: always menu shortcuts, never desktop shortcuts, whatever the setting + # 2017: new option value 2: launcher instead of menu. + # in portable case, shortcuts sanitized away elsewhere + $vars{'tlpdbopt_desktop_integration'} = 1; + # we have to make sure that this option is set to 0 in case + # that a non-admin is running the installations program + $vars{'tlpdbopt_w32_multi_user'} = 0 if (!admin()); + } + + # select scheme: either $vars{'selected_scheme'} or $default_scheme + # check that the default scheme is actually present, otherwise switch to + # scheme-minimal + my $selscheme = defined($vars{'selected_scheme'}) ? $vars{'selected_scheme'} + : $default_scheme; + if (!defined($tlpdb->get_package($selscheme))) { + if (!defined($tlpdb->get_package("scheme-minimal"))) { + die("Aborting, cannot find either $selscheme or scheme-minimal"); + } + $default_scheme = "scheme-minimal"; + $vars{'selected_scheme'} = $default_scheme; + } + # make sure that we update %vars for collection_* if only selected_scheme + # is there, but no collection information + my $found_collection = 0; + for my $k (keys(%vars)) { + if ($k =~ m/^collection-/) { + $found_collection = 1; + last; + } + } + if (!$found_collection) { + for my $p ($tlpdb->get_package($vars{'selected_scheme'})->depends) { + $vars{$p} = 1 if ($p =~ m/^collection-/); + } + } + return 1; +} # load_tlpdb + +sub initialize_collections { + foreach my $pkg ($tlpdb->list_packages) { + my $tlpobj = $tlpdb->{'tlps'}{$pkg}; + if ($tlpobj->category eq "Collection") { + $vars{"$pkg"} = 0 if (!defined($vars{$pkg})); + ++$vars{'n_collections_available'}; + push (@collections_std, $pkg); + } + } + my $selscheme = ($vars{'selected_scheme'} || $default_scheme); + my $scheme_tlpobj = $tlpdb->get_package($selscheme); + if (defined ($scheme_tlpobj)) { + $vars{'n_collections_selected'}=0; + foreach my $dependent ($scheme_tlpobj->depends) { + if ($dependent=~/^(collection-.*)/) { + $vars{"$1"}=1; + } + } + } + for my $c (keys(%vars)) { + if ($c =~ m/^collection-/ && $vars{$c}) { + ++$vars{'n_collections_selected'}; + } + } + if ($vars{"binary_win32"}) { + $vars{"collection-wintools"} = 1; + ++$vars{'n_collections_selected'}; + } +} # initialize_collections + +sub set_install_platform { + my $detected_platform=platform; + if ($opt_custom_bin) { + $detected_platform = "custom"; + } + my $warn_nobin; + my $warn_nobin_x86_64_linux; + my $nowarn=""; + my $wp='***'; # warning prefix + + $warn_nobin="\n$wp WARNING: No binaries for your platform found. "; + $warn_nobin_x86_64_linux="$warn_nobin" . + "$wp No binaries for x86_64-linux found, using i386-linux instead.\n"; + + my $ret = $warn_nobin; + if (defined $vars{"binary_$detected_platform"}) { + $vars{"binary_$detected_platform"}=1; + $vars{'inst_platform'}=$detected_platform; + $ret = $nowarn; + } elsif ($detected_platform eq 'x86_64-linux') { + $vars{'binary_i386-linux'}=1; + $vars{'inst_platform'}='i386-linux'; + $ret = $warn_nobin_x86_64_linux; + } else { + if ($opt_custom_bin) { + $ret = "$wp Using custom binaries from $opt_custom_bin.\n"; + } else { + $ret = $warn_nobin; + } + } + foreach my $key (keys %vars) { + if ($key=~/^binary.*/) { + ++$vars{'n_systems_selected'} if $vars{$key}==1; + } + } + return($ret); +} # set_install_platform + +sub create_profile { + my $profilepath = shift; + # The file "TLprofile" is created at the beginning of the + # installation process and contains information about the current + # setup. The purpose is to allow non-interactive installations. + my $fh; + if (ref($profilepath)) { + $fh = $profilepath; + } else { + open PROFILE, ">$profilepath"; + $fh = \*PROFILE; + } + # + # determine whether the set of selected collections exactly + # agrees with the selected scheme. In this case we do *not* + # save the actual collection setting but only the selected + # scheme, as reading the profile will load all collections + # if only the scheme is given. + my %instcols; + foreach my $key (sort keys %vars) { + $instcols{$key} = 1 if $key=~/^collection/ and $vars{$key}==1; + } + # for anything but "scheme-custom" we delete the contained + # collections from the list + if ($vars{'selected_scheme'} ne "scheme-custom") { + my $scheme=$tlpdb->get_package($vars{'selected_scheme'}); + if (!defined($scheme)) { + die ("Scheme $vars{selected_scheme} not defined.\n"); + } + for my $scheme_content ($scheme->depends) { + delete($instcols{"$scheme_content"}) if ($scheme_content=~/^collection-/); + } + } + # if there are still collection left, we keep all of them + my $save_cols = (keys(%instcols) ? 1 : 0); + + # start + my $tim = gmtime(time); + print $fh "# texlive.profile written on $tim UTC\n"; + print $fh "# It will NOT be updated and reflects only the\n"; + print $fh "# installation profile at installation time.\n"; + print $fh "selected_scheme $vars{selected_scheme}\n"; + foreach my $key (sort keys %vars) { + print $fh "$key $vars{$key}\n" + if $save_cols and $key=~/^collection/ and $vars{$key}==1; + # we don't save tlpdbopt_location + next if ($key eq "tlpdbopt_location"); + print $fh "$key $vars{$key}\n" if $key =~ /^tlpdbopt_/; + print $fh "$key $vars{$key}\n" if $key =~ /^instopt_/; + print $fh "$key $vars{$key}\n" if defined($path_keys{$key}); + print $fh "$key $vars{$key}\n" if (($key =~ /^binary_/) && $vars{$key}); + } + if (!ref($profilepath)) { + close PROFILE; + } +} # create_profile + +sub read_profile { + my $profilepath = shift; + my %opts = @_; + my %keyrename = ( + 'option_doc' => 'tlpdbopt_install_docfiles', + 'option_fmt' => 'tlpdbopt_create_formats', + 'option_src' => 'tlpdbopt_install_srcfiles', + 'option_sys_bin' => 'tlpdbopt_sys_bin', + 'option_sys_info' => 'tlpdbopt_sys_info', + 'option_sys_man' => 'tlpdbopt_sys_man', + 'option_file_assocs' => 'tlpdbopt_file_assocs', + 'option_backupdir' => 'tlpdbopt_backupdir', + 'option_w32_multi_user' => 'tlpdbopt_w32_multi_user', + 'option_post_code' => 'tlpdbopt_post_code', + 'option_autobackup' => 'tlpdbopt_autobackup', + 'option_desktop_integration' => 'tlpdbopt_desktop_integration', + 'option_adjustrepo' => 'instopt_adjustrepo', + 'option_letter' => 'instopt_letter', + 'option_path' => 'instopt_adjustpath', + 'option_symlinks' => 'instopt_adjustpath', + 'portable' => 'instopt_portable', + 'option_write18_restricted' => 'instopt_write18_restricted', + ); + my %keylost = ( + 'option_menu_integration' => 1, + 'in_place' => 1, + ); + + open PROFILE, "<$profilepath" + or die "$0: Cannot open profile $profilepath for reading.\n"; + # %pro is used to see whether there are non-recognized keys, + # while %profiledata is used to make sure that the values + # from the tlpdb do not overwrite -seed-profile values. + my %pro; + while () { + chomp; + next if m/^[[:space:]]*$/; # skip empty lines + next if m/^[[:space:]]*#/; # skip comment lines + s/^[[:space:]]+//; # ignore leading (but not trailing) whitespace + my ($k,$v) = split (" ", $_, 2); # value might have spaces + # skip TEXDIRW, seems not used anymore, but might be around + # in some profiles + next if ($k eq "TEXDIRW"); + # convert old keys to new keys + $k = $keyrename{$k} if ($keyrename{$k}); + if ($keylost{$k}) { + tlwarn("Profile key `$k' is now ignored, please remove it.\n"); + next; + } + $pro{$k} = $v; + $profiledata{$k} = $v; + } + foreach (keys %vars) { + # clear out collections from var, just to be sure + if (m/^collection-/) { $vars{$_} = 0; } + } + # initialize installer and tlpdb options + foreach (keys %pro) { + if (m/^instopt_/) { + if (defined($vars{$_})) { + $vars{$_} = $pro{$_}; + delete($pro{$_}); + } + } elsif (m/^tlpdbopt_/) { + my $o = $_; + $o =~ s/^tlpdbopt_//; + # we do not support setting the location in the profile + # could be done, but might be tricky .. + next if ($o eq 'location'); + if (defined($TeXLive::TLConfig::TLPDBOptions{$o})) { + $vars{$_} = $pro{$_}; + delete($pro{$_}); + } + + } elsif (defined($path_keys{$_}) || m/^selected_scheme$/) { + if ($pro{$_}) { + $vars{$_} = $pro{$_}; + delete($pro{$_}); + } else { + tldie("$0: Quitting, profile key for path $_ must not be empty.\n"); + } + + } elsif (m/^(binary|collection-)/) { + if ($pro{$_} =~ /^[01]$/) { + $vars{$_} = $pro{$_}; + delete($pro{$_}); + } else { + tldie("$0: Quitting, profile key for $_ must be 0 or 1, not: $pro{$_}\n"); + } + } + } + #require Data::Dumper; + #$Data::Dumper::Indent = 1; + #print Data::Dumper->Dump([\%vars], [qw(vars)]); + # + # if there are still keys in the %pro array, some unknown keys have + # been written in the profile, bail out + if (my @foo = keys(%pro)) { + tlwarn("Unknown key(s) in profile $profilepath: @foo\n"); + tlwarn("Stopping here.\n"); + exit 1; + } + + # if a profile contains *only* the selected_scheme setting without + # any collection, we assume that exactely that scheme should be installed + my $coldefined = 0; + foreach my $k (keys %profiledata) { + if ($k =~ m/^collection-/) { + $coldefined = 1; + last; + } + } + # if we are in seed mode, do not try to load remote db as it is + # not initialized by now + return if $opts{'seed'}; + # + # check whether the collections are actually present in case of + # changes on the server + foreach my $k (keys %profiledata) { + if ($k =~ m/^collection-/) { + if (!defined($tlpdb->get_package($k))) { + tlwarn("The profile references a non-existing collection: $k\n"); + tlwarn("Exiting.\n"); + exit(1); + } + } + } + # if at least one collection has been defined return here + return if $coldefined; + # since no collections have been defined in the profile, we + # set those to be installed on which the scheme depends + my $scheme=$tlpdb->get_package($vars{'selected_scheme'}); + if (!defined($scheme)) { + dump_vars(\*STDOUT); + die ("Scheme $vars{selected_scheme} not defined.\n"); + } + for my $scheme_content ($scheme->depends) { + $vars{"$scheme_content"}=1 if ($scheme_content=~/^collection-/); + } +} # read_profile + +sub do_install_packages { + my @what; + foreach my $package (sort keys %install) { + push @what, $package if ($install{$package} == 1); + } + # temporary unset the localtlpdb options responsible for + # running all kind of postactions, since install_packages + # would call them without the PATH already set up + # we are doing this anyway in do_postinstall_actions + $localtlpdb->option ("desktop_integration", "0"); + $localtlpdb->option ("file_assocs", "0"); + $localtlpdb->option ("post_code", "0"); + if (!install_packages($tlpdb,$media,$localtlpdb,\@what, + $vars{'tlpdbopt_install_srcfiles'},$vars{'tlpdbopt_install_docfiles'})) { + my $profile_name = "installation.profile"; + create_profile($profile_name); + tlwarn("Installation failed.\n"); + tlwarn("Rerunning the installer will try to restart the installation.\n"); + tlwarn("Or you can restart by running the installer with:\n"); + my $repostr = ($opt_location ? " --repository $location" : ""); + if (win32()) { + tlwarn(" install-tl-windows.bat$repostr --profile $profile_name [EXTRA-ARGS]\n" + ."or\n" + ." install-tl-advanced.bat$repostr --profile $profile_name [EXTRA-ARGS]\n"); + } else { + tlwarn(" install-tl$repostr --profile $profile_name [EXTRA-ARGS]\n"); + } + flushlog(); + exit(1); + } + # restore options in tlpdb + $localtlpdb->option ( + "desktop_integration", $vars{'tlpdbopt_desktop_integration'}); + $localtlpdb->option ("file_assocs", $vars{'tlpdbopt_file_assocs'}); + $localtlpdb->option ("post_code", $vars{'tlpdbopt_post_code'} ? "1" : "0"); + $localtlpdb->save; +} # do_install_packages + +# for later complete removal we want to save some options and values +# into the local tlpdb: +# - should links be set, and if yes, the destination (bin,man,info) +# +sub save_options_into_tlpdb { + # if we are told to adjust the repository *and* we are *not* + # installing from the network already, we adjust the repository + # to the default mirror.ctan.org + if ($vars{'instopt_adjustrepo'} && ($media ne 'NET')) { + $localtlpdb->option ("location", $TeXLiveURL); + } else { + my $final_loc = ($media eq 'NET' ? $location : abs_path($location)); + $localtlpdb->option ("location", $final_loc); + } + for my $o (keys %TeXLive::TLConfig::TLPDBOptions) { + next if ($o eq "location"); # done above already + $localtlpdb->option ($o, $vars{"tlpdbopt_$o"}); + } + my @archs; + foreach (keys %vars) { + if (m/^binary_(.*)$/ ) { + if ($vars{$_}) { push @archs, $1; } + } + } + if ($opt_custom_bin) { + push @archs, "custom"; + } + if (! @archs) { + tldie("$0: No binary platform specified/available, quitting.\n"); + } + # only if we forced the platform we do save this option into the tlpdb + if (defined($opt_force_arch)) { + $localtlpdb->setting ("platform", $::_platform_); + } + $localtlpdb->setting("available_architectures", @archs); + $localtlpdb->save() unless $opt_in_place; +} # save_options_into_tlpdb + +sub import_settings_from_old_tlpdb { + my $dn = shift; + my $tlpdboldpath = + "$dn/$TeXLive::TLConfig::InfraLocation/$TeXLive::TLConfig::DatabaseName"; + my $previoustlpdb; + if (-r $tlpdboldpath) { + # we found an old installation, so read that one in and save + # the list installed collections into an array. + info ("Trying to load old TeX Live Database,\n"); + $previoustlpdb = TeXLive::TLPDB->new(root => $dn); + if ($previoustlpdb) { + info ("Importing settings from old installation in $dn\n"); + } else { + tlwarn ("Cannot load old TLPDB, continuing with normal installation.\n"); + return; + } + } else { + return; + } + ############# OLD CODE ################### + # in former times we sometimes didn't change from scheme-full + # to scheme-custom when deselecting some collections + # this is fixed now. + # + # # first import the collections + # # since the scheme is not the final word we select scheme-custom here + # # and then set the single collections by hand + # $vars{'selected_scheme'} = "scheme-custom"; + # $vars{'n_collections_selected'} = 0; + # # remove the selection of all collections + # foreach my $entry (keys %vars) { + # if ($entry=~/^(collection-.*)/) { + # $vars{"$1"}=0; + # } + # } + # for my $c ($previoustlpdb->collections) { + # my $tlpobj = $tlpdb->get_package($c); + # if ($tlpobj) { + # $vars{$c} = 1; + # ++$vars{'n_collections_selected'}; + # } + # } + ############ END OF OLD CODE ############ + + ############ NEW CODE ################### + # we simply go through all installed schemes, install + # all depending collections + # if we find scheme-full we use this as 'selected_scheme' + # otherwise we use 'scheme_custom' as we don't know + # and there is no total order on the schemes. + # + # we cannot use select_scheme from tlmgr.pl, as this one clears + # previous selctions (hmm :-( + $vars{'selected_scheme'} = "scheme-custom"; + $vars{'n_collections_selected'} = 0; + # remove the selection of all collections + foreach my $entry (keys %vars) { + if ($entry=~/^(collection-.*)/) { + $vars{"$1"}=0; + } + } + # now go over all the schemes *AND* collections and select them + foreach my $s ($previoustlpdb->schemes) { + my $tlpobj = $tlpdb->get_package($s); + if ($tlpobj) { + foreach my $e ($tlpobj->depends) { + if ($e =~ /^(collection-.*)/) { + # do not add collections multiple times + if (!$vars{$e}) { + $vars{$e} = 1; + ++$vars{'n_collections_selected'}; + } + } + } + } + } + # Now do the same for collections: + for my $c ($previoustlpdb->collections) { + my $tlpobj = $tlpdb->get_package($c); + if ($tlpobj) { + if (!$vars{$c}) { + $vars{$c} = 1; + ++$vars{'n_collections_selected'}; + } + } + } + ########### END NEW CODE ############# + + + # now take over the path + my $oldroot = $previoustlpdb->root; + my $newroot = abs_path("$oldroot/..") . "/$texlive_release"; + $vars{'TEXDIR'} = $newroot; + $vars{'TEXMFSYSVAR'} = "$newroot/texmf-var"; + $vars{'TEXMFSYSCONFIG'} = "$newroot/texmf-config"; + # only TEXMFLOCAL is treated differently, we use what is found by kpsewhich + # in 2008 and onward this is defined as + # TEXMFLOCAL = $SELFAUTOPARENT/../texmf-local + # so kpsewhich -var-value=TEXMFLOCAL returns + # ..../2008/../texmf-local + # TODO TODO TODO + chomp (my $tml = `kpsewhich -var-value=TEXMFLOCAL`); + $tml = abs_path($tml); + $vars{'TEXMFLOCAL'} = $tml; + # + # now for the settings + # set the defaults to what is specified in the tlpdb + $vars{'tlpdbopt_install_docfiles'} = + $previoustlpdb->option_pkg("00texlive.installation", + "install_docfiles"); + $vars{'tlpdbopt_install_srcfiles'} = + $previoustlpdb->option_pkg("00texlive.installation", + "install_srcfiles"); + $vars{'tlpdbopt_create_formats'} = + $previoustlpdb->option_pkg("00texlive.installation", + "create_formats"); + $vars{'tlpdbopt_desktop_integration'} = 1 if win32(); + $vars{'instopt_adjustpath'} = + $previoustlpdb->option_pkg("00texlive.installation", + "path"); + $vars{'instopt_adjustpath'} = 0 if !defined($vars{'instopt_adjustpath'}); + $vars{'instopt_adjustpath'} = 1 if win32(); + $vars{'tlpdbopt_sys_bin'} = + $previoustlpdb->option_pkg("00texlive.installation", + "sys_bin"); + $vars{'tlpdbopt_sys_man'} = + $previoustlpdb->option_pkg("00texlive.installation", + "sys_man"); + $vars{'sys_info'} = + $previoustlpdb->option_pkg("00texlive.installation", + "sys_info"); + # + # import the set of selected architectures + my @aar = $previoustlpdb->setting_pkg("00texlive.installation", + "available_architectures"); + if (@aar) { + for my $b ($tlpdb->available_architectures) { + $vars{"binary_$b"} = member( $b, @aar ); + } + $vars{'n_systems_available'} = 0; + for my $key (keys %vars) { + ++$vars{'n_systems_available'} if ($key=~/^binary/); + } + } + # + # try to import paper settings + my $xdvi_paper; + if (!win32()) { + $xdvi_paper = TeXLive::TLPaper::get_paper("xdvi"); + } + my $pdftex_paper = TeXLive::TLPaper::get_paper("pdftex"); + my $dvips_paper = TeXLive::TLPaper::get_paper("dvips"); + my $dvipdfmx_paper = TeXLive::TLPaper::get_paper("dvipdfmx"); + my $context_paper; + if (defined($previoustlpdb->get_package("context"))) { + $context_paper = TeXLive::TLPaper::get_paper("context"); + } + my $common_paper = ""; + if (defined($xdvi_paper)) { + $common_paper = $xdvi_paper; + } + $common_paper = + ($common_paper ne $context_paper ? "no-agree-on-paper" : $common_paper) + if (defined($context_paper)); + $common_paper = + ($common_paper ne $pdftex_paper ? "no-agree-on-paper" : $common_paper) + if (defined($pdftex_paper)); + $common_paper = + ($common_paper ne $dvips_paper ? "no-agree-on-paper" : $common_paper) + if (defined($dvips_paper)); + $common_paper = + ($common_paper ne $dvipdfmx_paper ? "no-agree-on-paper" : $common_paper) + if (defined($dvipdfmx_paper)); + if ($common_paper eq "no-agree-on-paper") { + tlwarn("Previous installation uses different paper settings.\n"); + tlwarn("You will need to select your preferred paper sizes manually.\n\n"); + } else { + if ($common_paper eq "letter") { + $vars{'instopt_letter'} = 1; + } elsif ($common_paper eq "a4") { + # do nothing + } else { + tlwarn( + "Previous installation has common paper setting of: $common_paper\n"); + tlwarn("After installation has finished, you will need\n"); + tlwarn(" to redo this setting by running:\n"); + } + } +} # import_settings_from_old_tlpdb + +# do everything to select a scheme +# +sub select_scheme { + my $s = shift; + # set the selected scheme to $s + $vars{'selected_scheme'} = $s; + # if we are working on scheme-custom simply return + return if ($s eq "scheme-custom"); + # remove the selection of all collections + foreach my $entry (keys %vars) { + if ($entry=~/^(collection-.*)/) { + $vars{"$1"}=0; + } + } + # select the collections belonging to the scheme + my $scheme_tlpobj = $tlpdb->get_package($s); + if (defined ($scheme_tlpobj)) { + $vars{'n_collections_selected'}=0; + foreach my $dependent ($scheme_tlpobj->depends) { + if ($dependent=~/^(collection-.*)/) { + $vars{"$1"}=1; + ++$vars{'n_collections_selected'}; + } + } + } + # we have first set all collection-* keys to zero and than + # set to 1 only those which are required by the scheme + # since now scheme asks for collection-wintools we set its vars value + # to 1 in case we are installing win32 binaries + if ($vars{"binary_win32"}) { + $vars{"collection-wintools"} = 1; + ++$vars{'n_collections_selected'}; + } + # for good measure, update the deps + calc_depends(); +} # select_scheme + +# try to give a decent order of schemes, but be so general that +# if we change names of schemes nothing bad happnes (like forgetting one) +sub schemes_ordered_for_presentation { + my @scheme_order; + my %schemes_shown; + for my $s ($tlpdb->schemes) { $schemes_shown{$s} = 0 ; } + # first try the size-name-schemes in decreasing order + for my $sn (qw/full medium small basic minimal/) { + if (defined($schemes_shown{"scheme-$sn"})) { + push @scheme_order, "scheme-$sn"; + $schemes_shown{"scheme-$sn"} = 1; + } + } + # now push all the other schemes if they are there and not already shown + for my $s (sort keys %schemes_shown) { + push @scheme_order, $s if !$schemes_shown{$s}; + } + return @scheme_order; +} # schemes_ordered_for_presentation + +sub update_numbers { + $vars{'n_collections_available'}=0; + $vars{'n_collections_selected'} = 0; + $vars{'n_systems_available'} = 0; + $vars{'n_systems_selected'} = 0; + foreach my $key (keys %vars) { + if ($key =~ /^binary/) { + ++$vars{'n_systems_available'}; + ++$vars{'n_systems_selected'} if $vars{$key} == 1; + } + if ($key =~ /^collection-/) { + ++$vars{'n_collections_available'}; + ++$vars{'n_collections_selected'} if $vars{$key} == 1; + } + } +} # update_numbers + +# to be called at exit when the installation did not complete +sub flushlog { + if (!defined($::LOGFILENAME)) { + my $fh; + my $logfile = "install-tl.log"; + if (open (LOG, ">$logfile")) { + my $pwd = Cwd::getcwd(); + $logfile = "$pwd/$logfile"; + print "$0: Writing log in current directory: $logfile\n"; + $fh = \*LOG; + } else { + $fh = \*STDERR; + print + "$0: Could not write to $logfile, so flushing messages to stderr.\n"; + } + foreach my $l (@::LOGLINES) { + print $fh $l; + } + } +} # flushlog + +sub do_cleanup { + # remove temporary files from TEXDIR/temp + if (($media eq "local_compressed") or ($media eq "NET")) { + debug("Remove temporary downloaded containers...\n"); + rmtree("$vars{'TEXDIR'}/temp") if (-d "$vars{'TEXDIR'}/temp"); + } + + # write the profile out + if ($opt_in_place) { + create_profile("$vars{'TEXDIR'}/texlive.profile"); + debug("Profile written to $vars{'TEXDIR'}/texlive.profile\n"); + } else { + create_profile("$vars{'TEXDIR'}/$InfraLocation/texlive.profile"); + debug("Profile written to $vars{'TEXDIR'}/$InfraLocation/texlive.profile\n"); + } + + # now open the log file and write out the log lines if needed. + # the user could have given the -logfile option in which case all the + # stuff is already dumped to it and $::LOGFILE defined. So do not + # redefine it. + if (!defined($::LOGFILE)) { + # no -logfile option; nothing written yet + $::LOGFILENAME = "$vars{'TEXDIR'}/install-tl.log"; + if (open(LOGF,">$::LOGFILENAME")) { + $::LOGFILE = \*LOGF; + foreach my $line(@::LOGLINES) { + print $::LOGFILE "$line"; + } + } else { + tlwarn("$0: Cannot create log file $::LOGFILENAME: $!\n" + . "Not writing out log lines.\n"); + } + } + + # Close log file if present + close($::LOGFILE) if (defined($::LOGFILE)); + if (!defined($::LOGFILENAME) and (-e "$vars{'TEXDIR'}/install-tl.log")) { + $::LOGFILENAME = "$vars{'TEXDIR'}/install-tl.log"; + } + if (!(defined($::LOGFILENAME)) or !(-e $::LOGFILENAME)) { + $::LOGFILENAME = ""; + } +} # do_cleanup + +sub check_env { + # check for tex-related envvars. + $::env_warns = ""; + for my $evar (sort keys %origenv) { + next if $evar =~ /^(_.* + |.*PWD + |ARGS + |GENDOCS_TEMPLATE_DIR + |INSTROOT + |PATH + |PERL5LIB + |SHELLOPTS + )$/x; # don't worry about these + if ("$evar $origenv{$evar}" =~ /tex/i) { # check both key and value + $::env_warns .= " $evar=$origenv{$evar}\n"; + } + } + if ($::env_warns) { + $::env_warns = <<"EOF"; + + ---------------------------------------------------------------------- + The following environment variables contain the string "tex" + (case-independent). If you're doing anything but adding personal + directories to the system paths, they may well cause trouble somewhere + while running TeX. If you encounter problems, try unsetting them. + Please ignore spurious matches unrelated to TeX. + +$::env_warns ---------------------------------------------------------------------- +EOF + } +} + + +# Create a welcome message. +sub create_welcome { + @::welcome_arr = (); + push @::welcome_arr, "\n"; + push @::welcome_arr, __("Welcome to TeX Live!"); + push @::welcome_arr, "\n"; + push @::welcome_arr, __( + "See %s/index.html for links to documentation.\nThe TeX Live web site (https://tug.org/texlive/) contains any updates and corrections. TeX Live is a joint project of the TeX user groups around the world; please consider supporting it by joining the group best for you. The list of groups is available on the web at https://tug.org/usergroups.html.", + $::vars{'TEXDIR'}); + if (win32() + || ($vars{'instopt_adjustpath'} + && $vars{'tlpdbopt_desktop_integration'} != 2)) { + ; # don't tell them to make path adjustments on Windows, + # or if they chose to "create symlinks". + } else { + push @::welcome_arr, "\n"; + push @::welcome_arr, __( + "Add %s/texmf-dist/doc/man to MANPATH.\nAdd %s/texmf-dist/doc/info to INFOPATH.\nMost importantly, add %s/bin/%s\nto your PATH for current and future sessions.", + $::vars{'TEXDIR'}, $::vars{'TEXDIR'}, $::vars{'TEXDIR'}, + $::vars{'this_platform'}); + } +} + + +# remember the warnings issued +sub install_warnlines_hook { + push @::warn_hook, sub { push @::WARNLINES, @_; }; +} + +## a summary of warnings if there were any +#sub warnings_summary { +# return '' unless @::WARNLINES; +# my $summary = <{$_} = 1; + } +} + +sub deselect_collections { + my $varref = shift; + foreach (@_) { + $varref->{$_} = 0; + } +} + +# assign \&___ to *__ if __ is not otherwise defined +sub ___ { + my $s = shift; + return wrapped (sprintf($s, @_)); +} + +sub wrapped { + my $t = shift; + my $toolong = '.{79}'; + if ($t !~ $toolong) { + return $t; + } else { + my @lines = split /\n/, $t; + foreach my $l (@lines) { + if ($l !~ $toolong) { + next; # leave $l alone + } + my @words = split /\s+/, $l; + if (! @words) { + $l = ""; + next; + } else { + my $indent = $l; + $indent =~ s/^(\s*).*$/$1/; # extract leading spaces + my @broken = (); + my $inx = 0; + while (@words) { + if (not ((defined $broken[$inx]) && ($broken[$inx] =~ /\S/))) { + $broken[$inx] = $indent . (shift @words); + } elsif (($broken[$inx] . " " . $words[0]) =~ $toolong) { + $inx++; # NO word consumed, still words remaining + } else { + $broken[$inx] = $broken[$inx] . " " . (shift @words); + } # $l =~ $toolong + } # while @words + $l = join "\n", @broken; + } # @words + } # foreach my $l + return join "\n", @lines; + } # $t =~ $toolong +} # wrapped + + + __END__ + +=head1 NAME + +install-tl - TeX Live cross-platform installer + +=head1 SYNOPSIS + +install-tl [I