#!/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='.'; } 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); use TeXLive::TLPOBJ; use TeXLive::TLPDB; use TeXLive::TLConfig; use Getopt::Long; use strict; # used variables # # $install{$packagename} == 1 if it should be installed my %install; # place where the profile should be saved my $profilepath = './texlive.profile'; # 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" eq "CD") { $::_media_ = "CD"; } elsif ("$opt_media" eq "NET") { $::_media_ = "NET"; } elsif ("$opt_media" eq "DVD") { $::_media_ = "DVD"; } if ("$opt_profile" ne "") { if (-r "$opt_profile") { $profilepath = "$opt_profile"; print "Using automated installation using $profilepath!\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/$TeXLive::TLConfig::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 "Media: ", 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('./vars'); exit(1); } if ($ret == $MENU_ABORT) { # NO do_cleanup()! dump_vars('./vars'); exit(2); } if ($ret != $MENU_INSTALL) { warn "Unknown return value of run_menu: $ret\n"; exit(3); } } else { read_profile(); } # do the actual installation calc_depends(); prepare_installation(); do_install_packages(); parse_executes_and_create_files(); do_postinst_stuff(); dump_vars('./vars'); # if we installed from a profile we don't need to write it out if ($opt_profile eq "") { create_profile(); print "Profile written to $profilepath\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('CommonProgramFiles')) { $tex_prefix||=getenv('CommonProgramFiles') . '/texlive'; } else { $tex_prefix||=getenv('ProgramFiles') . '/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 calc_depends { my $size; # 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 = (); # 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. my @collections; foreach my $key (keys %vars) { if ($key=~/^collection-/) { push @collections, $key if $vars{$key}; } } # Now we look for packages in collections. A collection can contain # other collections. In this case we push it to the @collections # list if it doesn't exist already. # 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; } } } my @workcoll = @collections; my @allcollections = @collections; while (@workcoll) { my @tmp; foreach my $selected_collection (@workcoll) { my $collection=$tlpdb->get_package($selected_collection); for my $package ($collection->depends) { my $tlpobj = $tlpdb->get_package($package); if (not(defined($tlpobj))) { die "Cannot find $package in texlive.tlpdb, strange!\n"; } if ($tlpobj->category eq "Collection") { push @tmp, $package unless ((member($package, @allcollections)) || (member($package, @tmp))); } elsif (($tlpobj->category eq "Package") || ($tlpobj->category eq "Documentation")) { $install{"$package"}=1; $size+=$tlpobj->docsize if $vars{'option_doc'}; $size+=$tlpobj->srcsize if $vars{'option_src'}; $size+=$tlpobj->runsize; } elsif ($tlpobj->category eq "TLCore") { $install{"$package"} = 1; $size+=$tlpobj->docsize if $vars{'option_doc'}; $size+=$tlpobj->srcsize if $vars{'option_src'}; $size+=$tlpobj->runsize; # add the sizes of the binfiles of the package itself # this should not be necessary for texlive internal packages # because all of them are split, but someone might install # an external package that ships all binaries in one foreach my $a (@archs) { # might be non existing ... if (defined($tlpobj->binsize->{$a})) { $size += $tlpobj->binsize->{$a}; } } # take care for doc splitting if (grep(/^$package.ARCH/, $tlpobj->depends)) { foreach my $a (@archs) { $install{"$package.$a"} = 1; my $bintlp = $tlpdb->get_package("$package.$a"); if (not($bintlp)) { die "Cannot find $package.$a in texlive.tlpdb, exit!\n"; } # $package.$arch package are supposed to contain ONLY binfiles $::opt_debug = 1; # debug("$package.$a: binsize=",$bintlp->binsize->{$a},"\n"); $::opt_debug = 0; $size+=$bintlp->binsize->{$a} if defined($bintlp->binsize->{$a}); } } } elsif ($tlpobj->category eq "Scheme") { warn "We shouldn't work on Schemes here?!? package = $package\n"; } else { die "Unknown category ", $tlpobj->category, " of package $package -- ex!"; } } } push @allcollections, @tmp; @workcoll = @tmp; } $vars{'total_size'}= sprintf "%d", ($size * $TeXLive::TLConfig::BlockSize)/1024**2; # printf "Total: %d MB\n", $size/1024**2; } sub load_tlpdb { my $database; if (media eq 'NET') { $database="$::texlive_url/$TeXLive::TLConfig::InfraLocation/texlive.tlpdb"; } else { $database="$::installerdir/$TeXLive::TLConfig::InfraLocation/texlive.tlpdb"; } print "Loading $database ...\n"; $tlpdb=TeXLive::TLPDB->new(location => "$database"); } 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 { # 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"; 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 { 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{$_}; } } } # # prepare_installation sub prepare_installation { my $infralocation="$TeXLive::TLConfig::InfraLocation"; my $localtlpdbfile="$vars{'TEXDIR'}/$infralocation/texlive.tlpdb"; mkdirhier "$vars{'TEXDIR'}/$infralocation"; make_var_skeleton "$vars{'TEXMFSYSVAR'}"; make_local_skeleton "$vars{'TEXMFLOCAL'}"; $localtlpdb=new TeXLive::TLPDB; $localtlpdb->location("$localtlpdbfile"); } sub do_install_packages { foreach my $package (sort keys %install) { print "INSTALL $package\n" if ($install{$package} == 1); my $tlpobj=$tlpdb->get_package($package); if (&media eq 'DVD') { my @allfiles = $tlpobj->all_files; install_package(@allfiles); } elsif (&media eq 'CD') { my $container = "$package"; if (-r "$::installerdir/$::diskarchive/$package.zip") { $container = "$package.zip"; } elsif (-r "$::installerdir/$::diskarchive/$package.tar.lzma") { $container = "$package.tar.lzma"; } else { # ok, it could be a binary package which should be installed from # the net my $donefromnet = 0; foreach my $k (keys %vars) { if ($k =~ m/^netbin_(.*)$/) { my $a = $1; if (($vars{$k}) && ($package =~ m/\.$a$/)) { # ok this is a binary we should install from the net $::_media_ = 'NET'; install_package("$package.$TeXLive::TLConfig::DefaultContainerExtension"); $::_media_ = 'CD'; $donefromnet = 1; last; } } } if (!$donefromnet) { # for now only warn and return, should (?) be die!? warn "Cannot find a package $package (.zip or .lzma) in $::installerdir/$::diskarchive\n"; } next; } install_package($container); } elsif (&media eq 'NET') { install_package("$package.$TeXLive::TLConfig::DefaultContainerExtension"); } $localtlpdb->add_tlpobj($tlpobj); } $localtlpdb->save; } sub parse_executes_and_create_files { my %maps; my %fmtcnffiles; my %hypcnffiles; foreach my $p ($localtlpdb->list_packages) { my $obj = $tlpdb->get_package ($p); die "No TeX Live package named $p, strange.\n" if ! $obj; foreach my $e ($obj->executes) { if ($e =~ m/addMap (.*)$/) { $maps{$1} = 1; } elsif ($e =~ m/addMixedMap (.*)$/) { $maps{$1} = 2; } elsif ($e =~ m/BuildFormat (.*)$/) { $fmtcnffiles{$1} = 1; } elsif ($e =~ m/BuildLanguageDat (.*)$/) { $hypcnffiles{$1} = 1; } else { # die "Unknown execute for package $p: $e\n"; } } } # # updmap.cfg -> TEXMFSYSVAR/web2c my $updmapcfgpath = "$vars{'TEXMFSYSVAR'}/web2c/updmap.cfg"; open(UPDHDR,"<$vars{'TEXDIR'}/texmf/web2c/updmap-hdr.cfg") or die("Cannot find $vars{'TEXDIR'}/texmf/web2c/updmap-hdr.cfg!\n"); open(UPDMAPCFG,">$updmapcfgpath") or die("Cannot open $updmapcfgpath for writing!\n"); foreach () { print UPDMAPCFG ; } close(UPDHDR); foreach (sort keys %maps) { print UPDMAPCFG "Mixed" if ($maps{$_} == 2); print UPDMAPCFG "Map $_\n"; } close(UPDMAPCFG); # # fmtutil.cnf -> TEXMFSYSVAR/web2c my @formatlines; open(INFILE,"<$vars{'TEXDIR'}/texmf/fmtutil/fmtutil-hdr.cnf") or die("Cannot open $vars{'TEXDIR'}/texmf/fmtutil/fmtutil-hdr.cnf\n"); my @tmp = ; close (INFILE); push @formatlines, @tmp; foreach my $f (keys %fmtcnffiles) { open(INFILE,"<$vars{'TEXDIR'}/texmf/fmtutil/format.$f.cnf") or die("Cannot open $vars{'TEXDIR'}/texmf/fmtutil/format.$f.cnf"); @tmp = ; close(INFILE); push @formatlines, @tmp; } if ($#formatlines >= 0) { my $fmtutilpath = "$vars{'TEXMFSYSVAR'}/web2c/fmtutil.cnf"; open(OUTFILE,">$fmtutilpath") or die("Cannot open $fmtutilpath for writing!\n"); foreach (@formatlines) { print OUTFILE; } close(OUTFILE); } # # hyphenation patterns my @languagelines = (); # we first have to add language.us open(INFILE,"<$vars{'TEXDIR'}/texmf/tex/generic/config/language.us") or die("Cannot open $vars{'TEXDIR'}/texmf/tex/generic/config/language.us"); my @tmp = ; close(INFILE); push @languagelines, @tmp; # now do the other languages foreach my $f (keys %hypcnffiles) { open(INFILE,"<$vars{'TEXDIR'}/texmf/tex/generic/config/language.$f.dat") or die("Cannot open $vars{'TEXDIR'}/texmf/tex/generic/config/language.$f.dat"); my @tmp = ; close(INFILE); push @languagelines, @tmp; } if ($#languagelines >= 0) { `mkdir -p $vars{'TEXMFSYSVAR'}/tex/generic/config`; open(OUTFILE, ">$vars{'TEXMFSYSVAR'}/tex/generic/config/language.dat") or die("Cannot open $vars{'TEXMFSYSVAR'}/tex/generic/config/language.dat for writing"); foreach (@languagelines) { print OUTFILE; } close(OUTFILE); } } # # 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 .... # quick and dirty for now # we need to set the path correct!!!! `$vars{'TEXDIR'}/bin/$vars{'this_platform'}/mktexlsr $vars{'TEXDIR'}/texmf-dist $vars{'TEXDIR'}/texmf $vars{'TEXDIR'}/texmf-local`; `$vars{'TEXDIR'}/bin/$vars{'this_platform'}/updmap-sys --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 `; `$vars{'TEXDIR'}/bin/$vars{'this_platform'}/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: #