#!/usr/bin/env perl # $Id$ # install-tl.pl # # Copyright 2007 Reinhard Kotucha, Norbert Preining # This file is licensed under the GNU General Public License version 2 # or any later version. our $texlive_release="2008"; BEGIN { $^W = 1; my $me=$0; $me=~s!\\!/!g if $^O=~/^MSWin(32|64)$/i; if ($me=~m!/!) { ($::installerdir=$me)=~s!(.*)/.*$!$1!; } else { $::installerdir='.'; } # The installer uses a minimal subset of Perl for Windows. In order # to avoid conflicts with other versions of Perl already installed on # the system, we simply ignore them. The installer has to execute # $::installerdir/tlpkg/bin/perl.exe and should not rely on any other # Perl executable. if ($^O=~/^MSWin(32|64)$/i) { $ENV{'PERL5LIB'}=undef; @INC="$::installerdir/tlpkg/lib/Perl5_lib-TL_inst"; } unshift (@INC, "$::installerdir/tlpkg"); } use TeXLive::TLUtils qw(initialize_installer media platform platform_desc debug which getenv win32 unix program_exists architectures_available additional_architectures_available_from_net get_system_tmpdir member mkdirhier make_var_skeleton make_local_skeleton install_package copy create_fmtutil create_updmap create_language install_packages dirname); use TeXLive::TLPOBJ; use TeXLive::TLPDB; use TeXLive::TLConfig qw($DefaultContainerExtension $InfraLocation); use Getopt::Long; use strict; # used variables # # $install{$packagename} == 1 if it should be installed my %install; # if we should try to get additional binaries from the net # needs our since for menu ... our $trynet=0; # the url of net location, can be changed with -url .... $::texlive_url = 'http://tug.org/svn/texlive/trunk/Master'; # the default scheme to be installed my $default_scheme='scheme-full'; # some arrays where the lists of collections to be installed are saved # our for menus our @collections_std; our @collections_lang; our @collections_lang_doc; # The maximum number of binary systems on the CD. This number must be # smaller than the number of binaries available from the network. our $max_bins_on_CD=7; # 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 our %vars=( # 'n_' means 'number of'. 'this_platform' => platform(), 'n_systems_available' => 0, 'n_systems_selected' => 0, 'n_collections_selected' => 0, 'n_collections_available' => 0, 'total_size' => 0, 'option_symlinks' => 0, 'sys_bin' => '/usr/local/bin', 'sys_man' => '/usr/local/man', 'sys_info' => '/usr/local/info', 'option_doc' => 1, 'option_src' => 1, 'option_fmt' => 0, 'option_letter' => 0, 'selected_scheme' => $default_scheme, ); # option handling my $opt_media = ""; my $opt_url = ""; my $opt_profile = ""; $::netarchive = "archive"; $::diskarchive = "archive"; GetOptions("media=s" => \$opt_media, "profile=s"=> \$opt_profile, "netarchive=s" => \$::netarchive, "diskarchive=s" => \$::diskarchive, "url=s" => \$opt_url ) or die("No pod doc till now!"); if ("$opt_media" =~ m/^CD/i) { $::_media_ = "CD"; } elsif ("$opt_media" =~ m/^NET/i) { $::_media_ = "NET"; } elsif ("$opt_media" =~ m/^DVD/i) { $::_media_ = "DVD"; } if ("$opt_profile" ne "") { if (-r "$opt_profile") { print "Using automated installation using $opt_profile!\n"; } else { $opt_profile = ""; warn "Profile $opt_profile not readable, continuing in interactive mode!\n"; } } if ("$opt_url" ne "") { $::texlive_url = "$opt_url"; } $::texlive_db_url="$::texlive_url/$InfraLocation"; # 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 $system_tmpdir=get_system_tmpdir(); print "Platform: ", platform, " => \'", platform_desc(platform), "\'\n"; print "Distribution: "; if (media() eq "DVD") { print "live (uncompressed)\n"; } elsif (media() eq "CD") { print "inst (compressed)\n"; } elsif (media() eq "NET") { print "net (downloading)\n"; } else { print media(), "\n"; } print "Directory for Temporary Files: '", $system_tmpdir, "\'\n"; print "Installer Directory: '$::installerdir'\n"; initialize_installer(); load_tlpdb(); set_platforms_supported(); set_texlive_default_dirs(); initialize_collections(); set_install_platform(); if ($opt_profile eq "") { # 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 should implement a function run_menu() 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; require("TeXLive/install-menu-text.pl"); my $ret = run_menu(); if ($ret == $MENU_QUIT) { # do_cleanup(); MISSING dump_vars("$system_tmpdir/texlive.vars"); exit(1); } if ($ret == $MENU_ABORT) { # NO do_cleanup()! dump_vars("$system_tmpdir/texlive.vars"); exit(2); } if ($ret != $MENU_INSTALL) { warn "Unknown return value of run_menu: $ret\n"; exit(3); } } else { read_profile("$opt_profile"); } # do the actual installation calc_depends(); prepare_installation(); do_install_packages(); create_fmtutil($localtlpdb,$vars{'TEXMFSYSVAR'},$vars{'TEXMFLOCAL'}); create_updmap($localtlpdb,$vars{'TEXMFSYSVAR'},$vars{'TEXMFLOCAL'}); create_language($localtlpdb,$vars{'TEXMFSYSVAR'},$vars{'TEXMFLOCAL'}); do_postinst_stuff(); dump_vars("$system_tmpdir/texlive.vars"); # if we installed from a profile we don't need to write it out if (-r "$vars{'TEXDIR'}/$InfraLocation/texlive.profile") { warn "Not overwritting already present profile in $vars{'TEXDIR'}/$InfraLocation/texlive.profile\n"; } else { create_profile("$vars{'TEXDIR'}/$InfraLocation/texlive.profile"); print "Profile written to $vars{'TEXDIR'}/$InfraLocation/texlive.profile\n"; } exit(0); ################################################################### # # FROM HERE ON ONLY SUBROUTINES # NO VARIABLE DECLARATIONS # ################################################################### sub dump_vars { my $filename=shift; open VARS, ">$filename"; foreach my $key (keys %vars) { print VARS "$key $vars{$key}\n"; } close VARS; print "\n%vars dumped to '$filename'.\n"; } # Determine which platforms are supported. sub set_platforms_supported { my @binaries=architectures_available; for my $binary (@binaries) { unless (defined $vars{"diskbin_$binary"}) { $vars{"diskbin_$binary"}=0; } } if ($trynet==1) { my @binaries=additional_architectures_available_from_net \%vars; for my $binary (@binaries) { $vars{"netbin_$binary"}=0; } } } # Environment variables and default values on UNIX: # TEXLIVE_INSTALL_PREFIX /usr/local/texlive => $tex_prefix # TEXLIVE_INSTALL_TEXDIR $tex_prefix/2007 => $TEXDIR # TEXLIVE_INSTALL_TEXMFSYSVAR $TEXDIR/texmf-var # TEXLIVE_INSTALL_TEXMFLOCAL $tex_prefix/texmf-local # TEXLIVE_INSTALL_TEXMFHOME '$HOME/texmf' sub set_texlive_default_dirs { my $tex_prefix; my $texmfsysvar; my $texmflocal; my $texmfhome; $tex_prefix=getenv('TEXLIVE_INSTALL_PREFIX'); if (win32) { if (-w getenv('ProgramFiles')) { $tex_prefix||=getenv('ProgramFiles') . '/texlive'; } else { $tex_prefix||=getenv('USERPROFILE') . '/texlive'; } } else { $tex_prefix||='/usr/local/texlive'; } $vars{'TEXDIR'}="$tex_prefix/$texlive_release"; $texmfsysvar=getenv('TEXLIVE_INSTALL_TEXMFSYSVAR'); $texmfsysvar||=$vars{'TEXDIR'} . '/texmf-var'; $vars{'TEXMFSYSVAR'}="$texmfsysvar"; $texmflocal=getenv('TEXLIVE_INSTALL_TEXMFLOCAL'); $texmflocal||="$tex_prefix/texmf-local"; $vars{'TEXMFLOCAL'}="$texmflocal"; $texmfhome=getenv('TEXLIVE_INSTALL_TEXMFHOME'); if (win32) { $texmfhome||=getenv('USERPROFILE'); } else { $texmfhome||=getenv('HOME'); } $vars{'TEXMFHOME'}="$texmfhome/texmf"; } sub is_useful_perl_installed { if (which("perl")) { return 1; } else { return 0; } } 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=(); # initialize the %install hash with what should be installed # First look for packages in the selected scheme. my $scheme=$tlpdb->get_package($vars{'selected_scheme'}); 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}; } } # we have to check for a decent perl installed if (!is_useful_perl_installed()) { $install{"bin-perl"} = 1; } # compute the list of archs to be installed my @archs; foreach (keys %vars) { if (m/^diskbin_(.*)$/ ) { if ($vars{$_}) { push @archs, $1; } } if (m/^netbin_(.*)$/ ) { if ($vars{$_}) { push @archs, $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 my $p (@pre_selected) { debug "pre_selected $p\n"; foreach my $p_dep ($tlpdb->get_package($p)->depends) { if ($p_dep =~ m/^(.*)\.ARCH$/) { my $foo = "$1"; foreach my $a (@archs) { $install{"$foo.$a"} = 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; } } # install source packages, they don't contain *any* dependencies if ($vars{'option_src'}) { foreach my $p (keys %install) { my $tlpobj = $tlpdb->get_package("$p.SOURCES"); if (defined($tlpobj)) { $install{"$p.SOURCES"} = 1; } } } # now do the size computation my $size = 0; foreach my $p (keys %install) { my $tlpobj = $tlpdb->get_package($p); if (not(defined($tlpobj))) { die "Cannot find $p in texlive.tlpdb, strange!\n"; } $size+=$tlpobj->docsize if $vars{'option_doc'}; $size+=$tlpobj->srcsize if $vars{'option_src'}; $size+=$tlpobj->runsize; foreach my $a (@archs) { $size += $tlpobj->binsize->{$a} if defined($tlpobj->binsize->{$a}); } } $vars{'total_size'} = sprintf "%d", ($size * $TeXLive::TLConfig::BlockSize)/1024**2; } sub load_tlpdb { my $master; if (media eq 'NET') { $master="$::texlive_url"; } else { $master="$::installerdir"; } print "Loading $master/$TeXLive::TLConfig::InfraLocation/$TeXLive::TLConfig::DatabaseName ...\n"; $tlpdb=TeXLive::TLPDB->new(root => "$master"); } sub initialize_collections { foreach my $pkg ($tlpdb->list_packages) { my $tlpobj = $tlpdb->{'tlps'}{$pkg}; if ($tlpobj->category eq "Collection") { $vars{"$pkg"}=0; ++$vars{'n_collections_available'}; if ($pkg=~/collection-lang/) { push @collections_lang, $pkg; } elsif ($pkg=~/documentation/) { if ($pkg=~/documentation-base/) { push @collections_std, $pkg; } else { push @collections_lang_doc, $pkg; } } else { push @collections_std, $pkg; } } my $scheme_tlpobj = $tlpdb->get_package($default_scheme); if (defined ($scheme_tlpobj)) { foreach my $dependent ($scheme_tlpobj->depends) { if ($dependent=~/^(collection-.*)/) { $vars{"$1"}=1; } } } } } sub set_install_platform { my @available_platforms; my $detected_platform=platform; 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! "; if (media eq 'CD') { $warn_nobin.="More binaries are available\n$wp on the DVD. The binary " . "systems menu allows you to download extra binaries\n" . "$wp from the network.\n"; } $warn_nobin_x86_64_linux="$warn_nobin" . "$wp No binaries for x86_64-linux found, using i386-linux instead.\n"; if (defined $vars{"diskbin_$detected_platform"}) { $vars{"diskbin_$detected_platform"}=1; $vars{'inst_platform'}=$detected_platform; return $nowarn; } elsif (defined $vars{"netbin_$detected_platform"}) { $vars{"netbin_$detected_platform"}=1; $vars{'inst_platform'}=$detected_platform; return $nowarn; } elsif ($detected_platform eq 'x86_64-linux') { $vars{'diskbin_i386-linux'}=1; $vars{'inst_platform'}='i386-linux'; return $warn_nobin_x86_64_linux; } else { return $warn_nobin; } } 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. open PROFILE, ">$profilepath"; my $tim = gmtime(time); print PROFILE "# texlive.profile written on $tim UTC\n"; print PROFILE "# it will NOT be overwritten and reflects only the\n"; print PROFILE "# installation profile at installation time\n"; print PROFILE "selected_scheme $vars{selected_scheme}\n"; foreach my $key (keys %vars) { print PROFILE "$key $vars{$key}\n" if $key=~/^collection/ and $vars{$key}==1; print PROFILE "$key $vars{$key}\n" if $key=~/^option_letter/; print PROFILE "$key $vars{$key}\n" if $key=~/^option_doc/; print PROFILE "$key $vars{$key}\n" if $key=~/^option_fmt/; print PROFILE "$key $vars{$key}\n" if $key=~/^option_src/; print PROFILE "$key $vars{$key}\n" if $key=~/^option_symlinks/; print PROFILE "$key $vars{$key}\n" if $key=~/^TEXDIR/; print PROFILE "$key $vars{$key}\n" if $key=~/^TEXMFSYSVAR/; print PROFILE "$key $vars{$key}\n" if $key=~/^TEXMFLOCAL/; print PROFILE "$key $vars{$key}\n" if $key=~/^TEXMFHOME/; } close PROFILE; } sub read_profile { my $profilepath = shift; open PROFILE, "<$profilepath" or die "Cannot open profile $profilepath for reading!\n"; my %pro; while () { next if m/^[[:space:]]*$/; # skip empty lines next if m/^[[:space:]]*#/; # skip comment lines my ($k,$v) = split; $pro{$k} = $v; } foreach (keys %vars) { # clear out collections from var if (m/^collection-/) { $vars{$_} = 0; } if (defined($pro{$_})) { $vars{$_} = $pro{$_}; } } } sub prepare_installation { make_var_skeleton "$vars{'TEXMFSYSVAR'}"; make_local_skeleton "$vars{'TEXMFLOCAL'}"; mkdirhier "$vars{'TEXDIR'}/texmf-config"; $localtlpdb=new TeXLive::TLPDB; $localtlpdb->root("$vars{'TEXDIR'}"); } sub do_install_packages { my @what; foreach my $package (sort keys %install) { push @what, $package if ($install{$package} == 1); } my @netarchs; foreach my $k (keys %vars) { if ($k =~ m/^netbin_(.*)$/) { my $a = $1; if ($vars{$k}) { push @netarchs, $a; } } } install_packages($tlpdb,$localtlpdb,\@what,\@netarchs,$vars{'option_src'},$vars{'option_doc'}); } # do_postinst_stuff has to fix up the texmf tree and install some missing # files. The things to do are taken from the install-live.sh installer # of former times, and should be critically checked. sub do_postinst_stuff { # create useful skeletions make_var_skeleton("$vars{'TEXMFSYSVAR'}"); make_local_skeleton("$vars{'TEXDIR'}/texmf-local"); # install some copies from texmf(-dist) into texmf-var copy ("$vars{'TEXDIR'}/texmf-dist/tex/plain/config/language.def", "$vars{'TEXMFSYSVAR'}/tex/plain/config") if (-f "$vars{'TEXDIR'}/texmf-dist/tex/plain/config/language.def"); copy ("$vars{'TEXDIR'}/texmf/xdvi/XDvi", "$vars{'TEXMFSYSVAR'}/xdvi") if (-f "$vars{'TEXDIR'}/texmf/xdvi/XDvi"); # those files must exist copy ("$vars{'TEXDIR'}/texmf/dvips/config/config.ps", "$vars{'TEXMFSYSVAR'}/dvips/config"); copy ("$vars{'TEXDIR'}/texmf/dvipdfm/config/config", "$vars{'TEXMFSYSVAR'}/dvipdfm/config"); copy ("$vars{'TEXDIR'}/texmf/web2c/mktex.cnf", "$vars{'TEXMFSYSVAR'}/web2c"); # the old installer copied from CDDIR, but shouldn't this be installed # in ANY case since it is in bin-pdftex??? copy ("$vars{'TEXDIR'}/texmf/tex/generic/config/pdftexconfig.tex", "$vars{'TEXMFSYSVAR'}/tex/generic/config"); # old installer did this, should we do this, TOO???? #cp $CDDIR/texmf-dist/tex/context/config/cont-usr.tex $TEXMFSYSVAR/tex/context/config/cont-usr.tex # MISSING # we need to run mktexlsr and updmap-sys .... # 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 my @TMFVARS = qw(VARTEXFONTS TEXMFMAIN TEXMFDIST TEXMFLOCAL TEXMFSYSVAR TEXMFSYSCONFIG TEXMFHOME TEXMFVAR TEXMFCONFIG TEXMF SYSTEXMF VARTEXFONTS TEXMFDBS WEB2C TEXINPUTS TEXFORMATS MFBASES MPMEMS TEXPOOL MFPOOL MPPOOL PSHEADERS TEXFONTMAPS TEXPSHEADERS TEXCONFIG TEXMFCNF); foreach my $tmv (@TMFVARS) { $ENV{$tmv} = undef if (defined($ENV{$tmv})); } # Step 2: Setup the PATH my $path = ""; if (defined($ENV{'PATH'})) { $path = $ENV{'PATH'}; } if (win32()) { $path = "$vars{'TEXDIR'}/bin/$vars{'this_platform'};$path"; } else { $path = "$vars{'TEXDIR'}/bin/$vars{'this_platform'}:$path"; } $ENV{'PATH'} = "$path"; # Step 3: Make Perl find the right modules my $perlint = which("perl"); if (dirname($perlint) =~ m;$vars{'TEXDIR'}/bin/$vars{'this_platform'};i) { # we are working with our version of perl # thus, we have to set the PERLLIB envvar right my $perllib = ""; if (defined($ENV{'PERLLIB'})) { $perllib = $ENV{'PERLLIB'}; } if (win32()) { $perllib .= ";$vars{'TEXDIR'}/perltl/lib"; } else { $perllib .= ":$vars{'TEXDIR'}/perltl/lib"; } $ENV{'PERLLIB'} = $perllib; } # # Step 4: run the programs my $mktexlsr = (win32() ? "mktexlsr.exe" : "mktexlsr"); my $updmap = (win32() ? "updmap.exe" : "updmap-sys"); `$mktexlsr $vars{'TEXDIR'}/texmf-dist $vars{'TEXDIR'}/texmf $vars{'TEXDIR'}/texmf-local`; `$updmap --nohash --cnffile=$vars{'TEXMFSYSVAR'}/web2c/updmap.cfg --dvipsoutputdir=$vars{'TEXMFSYSVAR'}/fonts/map/dvips/updmap \ --dvipdfmoutputdir=$vars{'TEXMFSYSVAR'}/fonts/map/dvipdfm/updmap --pdftexoutputdir=$vars{'TEXMFSYSVAR'}/fonts/map/pdftex/updmap `; `$mktexlsr $vars{'TEXMFSYSVAR'}`; # old installer: #$config && texconfig-sys init #$config || echo "PLEASE RUN texconfig or texconfig-sys to make new formats." } __END__ ### Local Variables: ### perl-indent-level: 2 ### tab-width: 2 ### indent-tabs-mode: nil ### End: # vim:set tabstop=2 expandtab: #