From 03c92bbe61083ce86555a61af2cd0f7132847e29 Mon Sep 17 00:00:00 2001 From: Piotr Strzelczyk Date: Fri, 31 Jul 2009 08:39:38 +0000 Subject: tlmgr update --self on win32 Changes to tlmgr.pl: - check_for_critical_updates returns a critical updates list instead of 0/1 - warning message moved to critical_updates_warning() - some code clean-up, superfluous conditions removal - filtering of critical packages from @todo _after_ dependency expansion - support for tlmgr update --all --self --dry-run (recursive call to action_update) - documentation updates Changes to updater-w32: - renamed updater-w32.bat to updater-w32 (cannot be executed accidentally) - merged run-updater-w32 into updater-w32, arguments and options are passed through a subroutine appended at the end of updater-w32 at run-time - support for debug mode - reflect changes in tlmgr.bat and docs git-svn-id: svn://tug.org/texlive/trunk@14500 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf/scripts/texlive/tlmgr.pl | 220 ++++++++++++++--------------- Master/texmf/scripts/texlive/updater-w32 | 228 +++++++++++++++++++++++++++++++ 2 files changed, 339 insertions(+), 109 deletions(-) create mode 100644 Master/texmf/scripts/texlive/updater-w32 (limited to 'Master/texmf') diff --git a/Master/texmf/scripts/texlive/tlmgr.pl b/Master/texmf/scripts/texlive/tlmgr.pl index 82f7a72c531..e138e649496 100755 --- a/Master/texmf/scripts/texlive/tlmgr.pl +++ b/Master/texmf/scripts/texlive/tlmgr.pl @@ -1289,22 +1289,20 @@ sub write_w32_updater { } } return 0 if ($opts{"dry-run"}); # nothing else to do - # write the updater script file - open UPDATER, ">$root/temp/run-updater-w32" or die "Cannot create updater: $!"; - # TODO: respawn tlmgr if $restart_tlmgr is set - # TODO: using @::SAVEDARGV - print UPDATER ' -@echo off -setlocal enableextensions -(set guimode=' . $::gui_mode . ') ->nul copy /y "%~dp0..\texmf\scripts\texlive\updater-w32.bat" "%~dp0updater-w32.bat" -rem Execute with cmd.exe to avoid problems if 4NT is used -rem Keep files write open during execution to signal running state -rem For gui mode console window might be hidden, so open a new one -' . ($::gui_mode ? 'start /wait ' : '') . 'cmd.exe /c title TeX Live Manager 2009^& call "%~dp0updater-w32.bat" ' . join(' ', @updater_args) . ' 8^>^>"%~f0" 9^>^>"%~dp0updater-w32.bat" -rem Self-delete without error message about missing file -(if not errorlevel 1 start /b cmd.exe /c del "%~f0" "%~dp0updater-w32.bat") & goto :eof -'; + my @rerun_tlmgr_args; + # quote all arguments in case of manes with spaces + @rerun_tlmgr_args = map ("\"$_\"", @::SAVEDARGV) if $restart_tlmgr; + # prepare updater script + copy("$root/texmf/scripts/texlive/updater-w32", "$temp"); + open UPDATER, ">>$temp/updater-w32" or die "Cannot open updater script: $!"; + print UPDATER " +:setupenv +set w32upd_gui_mode=$::gui_mode +set w32upd_title=TeX Live Manager $TeXLive::TLConfig::ReleaseYear Update +set w32upd_debug=$::opt_verbosity +set w32upd_args=@updater_args +set w32upd_rerun_args=@rerun_tlmgr_args +"; close UPDATER; return 0; } @@ -1355,43 +1353,22 @@ sub action_update { init_tlmedia(); info("update: dry run, no changes will be made\n") if $opts{"dry-run"}; - # if the update is not for one of the critical packages then do # check for updates to tlmgr and die unless either --force or --list or --self # is given - my @pretodo = @ARGV; - push (@pretodo, @CriticalPackagesList) if $opts{"self"}; - my $other_updates_asked_for = 0; - if ($opts{"all"}) { - $other_updates_asked_for = 1; - } else { - for my $p (@pretodo) { - my $matched = 0; - for my $cp (@CriticalPackagesList) { - # we match for initial package name, that shold be fine - if ($p =~ m/^$cp/) { - $matched = 1; - last; - } - } - if (!$matched) { - $other_updates_asked_for = 1; - last; - } - } - } - if ($other_updates_asked_for && !$opts{"self"}) { - if (check_for_critical_updates($localtlpdb, $tlmediatlpdb)) { - if ($opts{"force"}) { - tlwarn("$0: Continuing due to --force.\n"); - } elsif ($opts{"list"} || $opts{"self"}) { - # do not warn here + my @critical = check_for_critical_updates($localtlpdb, $tlmediatlpdb); + my $dry_run_cont = $opts{"dry-run"} && ($opts{"dry-run"} < 0); + if ( !$dry_run_cont && !$opts{"self"} && @critical) { + critical_updates_warning(); + if ($opts{"force"}) { + tlwarn("$0: Continuing due to --force.\n"); + } elsif ($opts{"list"}) { + # do not warn here + } else { + if ($::gui_mode) { + # return here and don't do any updates + return; } else { - if ($::gui_mode) { - # return here and don't do any updates - return; - } else { - die "$0: Exiting, please read above warning.\n"; - } + die "$0: Exiting, please read above warning.\n"; } } } @@ -1523,31 +1500,33 @@ sub action_update { my @todo; if ($opts{"list"}) { - # for --list we treat all packages the same @todo = $localtlpdb->list_packages; } elsif ($opts{"self"}) { - # do only critical packages, if others are asked for, do them after tlmgr restart - @todo = @CriticalPackagesList; + @todo = @critical; + } elsif ($opts{"all"}) { + @todo = $localtlpdb->list_packages; } else { - @pretodo = $localtlpdb->list_packages if ($opts{"all"}); - # filter out win32 special packages - for my $p (@pretodo) { - push(@todo, $p) if ($p !~ m/$CriticalPackagesRegexp/); - } + @todo = @ARGV; } # don't do anything if we have been invoced in a strange way if (!@todo) { tlwarn("tlmgr update: please specify a list of packages or --all.\n"); } - # update all .ARCH dependencies, too, unless $opts{"no-depends-at-all"}: - @todo = $tlmediatlpdb->expand_dependencies("-only-arch", $localtlpdb, @todo) - unless $opts{"no-depends-at-all"}; - # - # update general deps unless $opts{"no-depends"}: - @todo = $tlmediatlpdb->expand_dependencies("-no-collections",$localtlpdb,@todo) - unless $opts{"no-depends"}; - + if (!$opts{"self"}) { + # update all .ARCH dependencies, too, unless $opts{"no-depends-at-all"}: + @todo = $tlmediatlpdb->expand_dependencies("-only-arch", $localtlpdb, @todo) + unless $opts{"no-depends-at-all"}; + # + # update general deps unless $opts{"no-depends"}: + @todo = $tlmediatlpdb->expand_dependencies("-no-collections",$localtlpdb,@todo) + unless $opts{"no-depends"}; + # + # filter out critical packages + @todo = grep (!m/$CriticalPackagesRegexp/, @todo) + unless $opts{"list"}; + } + # # we first collect the list of packages to be actually updated or installed my %updated; @@ -1905,39 +1884,61 @@ sub action_update { # # check that if updates to the critical packages are present all of # them have been successfully updated - my $infra_update_present = 0; my $infra_update_done = 1; - for my $pkg (@CriticalPackagesList) { - if (defined($updated{$pkg})) { - $infra_update_present = 1; - $infra_update_done &&= $updated{$pkg}; + for my $pkg (@critical) { + $infra_update_done &&= $updated{$pkg} if (defined($updated{$pkg})); + } + + # check if any additional updates are asked for + my $other_updates_asked_for = 0; + if ($opts{"all"}) { + $other_updates_asked_for = 1; + } else { + foreach my $p (@ARGV) { + if ($p !~ m/$CriticalPackagesRegexp/) { + $other_updates_asked_for = 1; + last; + } } } my $restart_tlmgr = 0; - # only when we are not dry-running we restart the program - if ($infra_update_present && $infra_update_done && - $other_updates_asked_for && !$opts{"dry-run"}) { + if ($opts{"self"} && $infra_update_done && $other_updates_asked_for) { # weed out the --self argument from the saved arguments - @::SAVEDARGV = grep {!m;^--self$;} @::SAVEDARGV;; - @::SAVEDARGV = grep {!m;^-self$;} @::SAVEDARGV;; + @::SAVEDARGV = grep (!m/^-?-self$/, @::SAVEDARGV); $restart_tlmgr = 1; } + # infra update and tlmgr restart on win32 is done by the updater batch script if (win32() && !$opts{"list"} && @w32_updated) { info("Preparing TeX Live infrastructure update...\n"); my $ret = write_w32_updater($restart_tlmgr, @w32_updated); - tlwarn ("Aborting infrastructure update.\n") if ($ret); + if ($ret) { + tlwarn ("Aborting infrastructure update.\n"); + $restart_tlmgr = 0 if ($opts{"dry-run"}); + } } - # temporarily disable tlmgr restart after infra update (needs more work) - if (!win32() && $restart_tlmgr) { + # only when we are not dry-running we restart the program + if (!win32() && $restart_tlmgr && !$opts{"dry-run"}) { info ("Restarting tlmgr to complete update ...\n"); debug("restarting tlmgr @::SAVEDARGV\n"); exec("tlmgr", @::SAVEDARGV); die "Cannot restart tlmgr, please retry updating."; } + # for --dry-run we cannot restart tlmgr (no way to fake successful infra update) + # instead we call action_update() again and signal this by $opts{"dry-run"} = -1 + if ($opts{"dry-run"} && !$opts{"list"} && $restart_tlmgr) { + $opts{"self"} = 0; + $opts{"dry-run"} = -1; + $localtlmedia = undef; + $tlmediatlpdb = undef; + info ("Restarting tlmgr to complete update ...\n"); + action_update(); + return; + } + if (!(@new || @updated || @w32_updated) && ($tlmediasrc->media ne "NET") && $opts{"all"}) { # for all but net updates we warn if nothing is updated tlwarn("\nNothing to update.\n"); @@ -1990,6 +1991,7 @@ sub action_install { # check for updates to tlmgr itself, and die unless --force is given if (check_for_critical_updates( $localtlpdb, $tlmediatlpdb)) { + critical_updates_warning(); if ($opts{"force"}) { tlwarn("Continuing due to --force\n"); } else { @@ -3414,6 +3416,7 @@ sub check_for_critical_updates my $criticalupdate = 0; my @critical = $localtlpdb->expand_dependencies("-no-collections", $localtlpdb, @CriticalPackagesList); + my @critical_upd; for my $pkg (sort @critical) { my $tlp = $localtlpdb->get_package($pkg); if (!defined($tlp)) { @@ -3430,24 +3433,23 @@ sub check_for_critical_updates next; } my $remoterev = $mtlp->revision; - if ($remoterev > $localrev) { - $criticalupdate = 1; - last; - } - } - if ($criticalupdate) { - tlwarn("Updates for tlmgr itself are present.\n"); - tlwarn("=" x 79, "\n"); - tlwarn("Please update the package manager first, via either\n"); - tlwarn(" tlmgr update --self\n"); - tlwarn("or by getting the latest updater for Unix-ish systems:\n"); - tlwarn(" $TeXLiveURL/update-tlmgr-latest.sh\n"); - tlwarn("and/or Windows systems:\n"); - tlwarn(" $TeXLiveURL/update-tlmgr-latest.exe\n"); - tlwarn("Then continue with other updates.\n"); - tlwarn("=" x 79, "\n"); + push (@critical_upd, $pkg) if ($remoterev > $localrev); } - return($criticalupdate); + return(@critical_upd); +} + +sub critical_updates_warning +{ + tlwarn("Updates for tlmgr itself are present.\n"); + tlwarn("=" x 79, "\n"); + tlwarn("Please update the package manager first, via either\n"); + tlwarn(" tlmgr update --self\n"); + tlwarn("or by getting the latest updater for Unix-ish systems:\n"); + tlwarn(" $TeXLiveURL/update-tlmgr-latest.sh\n"); + tlwarn("and/or Windows systems:\n"); + tlwarn(" $TeXLiveURL/update-tlmgr-latest.exe\n"); + tlwarn("Then continue with other updates.\n"); + tlwarn("=" x 79, "\n"); } 1; @@ -3602,11 +3604,10 @@ must be specified. Options: =item B<--all> -Update all installed packages but the tlmgr itself. -For packages contained in an installed -collection, also install new packages and remove deleted packages in -those collections. Packages not contained in an installed collection -are not considered for addition or removal. +Update all installed packages except for tlmgr itself. For packages +contained in an installed collection, also install new packages and +remove deleted packages in those collections. Packages not contained +in an installed collection are not considered for addition or removal. More precisely, if both the server and the local installation have a collection C, and C on the server contains a package A that is not @@ -3615,16 +3616,17 @@ contains a package B, but B is no longer in the server's C, then B will be deleted. In case that updates to tlmgr itself are present this will not work -unless also the option B<--self> is given. +unless also the option B<--force> or B<--self> is given. =item B<--self> -Updates tlmgr itself if updates are present. On Windows this includes -updates to the perl interpreter shipped by TeX Live. +Update tlmgr itself (that is, the infrastructure packages) if updates +are present. On Windows this includes updates to the perl interpreter +shipped by TeX Live. -If this option is given together with B<--all> then first tlmgr will -be updated, and after that tlmgr restarted to complete with the rest -of the updates. +If this option is given together with B<--all> or a list of packages +then tlmgr will be updated first and, if this update succeeds, it will +be restarted to complete the rest of the updates. =item B<--list> @@ -3638,8 +3640,8 @@ written to the terminal. This is a more detailed report than C<--list>. =item B<--force> -If updates to tlmgr itself (that is, the infrastructure packages) are -present, tlmgr will not perform the update unless this option is given. +Force update of packages even if updates to tlmgr itself are present. +Updates to tlmgr are not performed unless B<--self> option is also given. (C<--list> is still performed regardless of this option.) =item B<--no-remove> diff --git a/Master/texmf/scripts/texlive/updater-w32 b/Master/texmf/scripts/texlive/updater-w32 new file mode 100644 index 00000000000..59f6788bf3e --- /dev/null +++ b/Master/texmf/scripts/texlive/updater-w32 @@ -0,0 +1,228 @@ +@echo off +rem Update script for TeX Live infrastructure +rem called automatically if there are any infrastructure updates +rem +rem Copyright 2009 by Tomasz M. Trzeciak +rem This file is licensed under the GNU General Public License version 2 +rem or any later version. +rem + +:: =================================================================== +:: INFRASTRUCTURE UPDATE ON WINDOWS +:: =================================================================== +:: Infrastructure files cannot be updated directly from the tlmgr.pl +:: script due to file locking problem on Windows---files that are in +:: use (either open or executing) cannot be removed or replaced. +:: Therefore the update process is performed by a batch script outside +:: of tlmgr.pl. +:: There are three pieces involved in the update: tlmgr.bat launcher, +:: write_w32_updater subroutine in tlmgr.pl and this script. Their +:: roles are as follows: +:: * tlmgr.bat is a watchdog, it launches tlmgr.pl and watches for +:: a trigger script that is to be executed. If trigger script exists +:: before tlmgr.pl is launched, it will be removed or tlmgr.bat will +:: abort if it fails to do so. This means that the trigger script +:: has to be created by the current invocation of tlmgr.pl +:: * write_w32_updater subroutine in tlmgr.pl prepares the update +:: and writes the trigger script. Packages in .xz archives are +:: dowloaded/copied and uncompressed to .tar files. Also .tar +:: backups of the current packages are made. If everything is +:: successful, the trigger script is created from the template +:: appended with evironment settings needed for the update. +:: Otherwise the update is aborted. +:: * updater-w32.bat, which is this script, triggers and executes +:: the actual update. During the execution of an update it locks +:: itself up write open to prevent tlmgr.bat from starting another +:: instance of tlmgr and/or update. The list of packages to update +:: and any additional information is passed through evironment, +:: including agrmuments for tlmgr restart needed to complete the +:: update of remaining packages. +:: The list of infrastructure packages consists of triples: +:: package-name old-revision-number new-revision-number +:: For each package-name a .tar archive package-name.tar is expected +:: (also for .source and .doc pseudo packages---those are for +:: distribution only and don't appear separately in tlpdb). +:: Additionally a backup archive is expected: +:: __BACKUP_package-name.rXXX.tar, +:: where XXX is (old) revision number (.source and .doc are included +:: in the backup). +:: The update itself proceeds as follows: +:: (1) untar all package archives +:: (2) include .tlpobj files into tlpdb +:: (3) print update info +:: (4) optionally restart tlmgr to complete the update +:: Any error during (1) or (2) triggers the rollback action: +:: (1) print failed update info +:: (2) untar all package backups +:: (3) include .tlpobj files (from backup) into tlpdb +:: (4) print restore info +:: Any error during (2) or (3) and we go into panic mode. At this +:: point there is no guarantee that the installation is still +:: working. There is not much we can do but to print failed restore +:: info and give instructions to download and run +:: 'update-tlmgr-latest.exe' to repair the installation. +:: =================================================================== + + +rem Make environment changes local & enable cmd extensions +rem (we don't want delayed expansion becasue it's hard to echo bang !) +setlocal enableextensions disabledelayedexpansion +rem Make sure that the environment is clean and that we can use dynamic variables +for %%I in (errorlevel date time w32upd_args) do (set %%I=) + +rem Set up the evironment for update +call :setupenv +if not defined w32upd_args ( + echo updater-w32: update arguments not defined, please report this to: tex-live@tug.org>&2 + exit /b 1 +) +set dbg=rem +if /i %w32upd_debug%0 GTR 0 set dbg=echo D:updater-w32: + +if "%~1"=="main" goto :main + +rem Restart in cmd.exe to avoid problems if 4NT is used +set respawn=cmd.exe /c call +rem For gui mode console window might be hidden, so open a new one +if /i %w32upd_gui_mode%0==10 set respawn=start /wait %respawn% +rem Lock this file write-open during execution to signal running state +call %dbg% respawning in cmd.exe ... +%respawn% "%~f0" main 9^>^>"%~f0" +set exitcode=%errorlevel% +if /i %exitcode%0 NEQ 0 ( + call %dbg% update script left for inspection at: %~f0.txt + copy /y "%~f0" "%~f0.txt" >nul + exit /b %exitcode% +) +rem Self-delete without error message about missing file +start /b cmd.exe /c del "%~f0" & exit /b %exitcode% + +:main +title %w32upd_title% + +rem Get TL installation root and log file location +for /f "tokens=1-2 delims=;" %%I in ( + 'kpsewhich -expand-var=$SELFAUTOPARENT/.;$TEXMFSYSVAR/web2c' +) do ( + set tlroot=%%~fI + set logdir=%%~fJ +) +if not exist "%tlroot%\texmf\scripts\texlive\tlmgr.pl" ( + echo updater-w32: cannot locate installation root.>&2 + exit /b 1 +) +call %dbg% TL root: %tlroot% +if not exist "%logdir%\" (set logdir=%tlroot%\temp) +call %dbg% log dir: %logdir% + +rem Make a private copy of tar.exe +copy /y "%tlroot%\tlpkg\installer\tar.exe" "%tlroot%\temp\tar-w32.exe" >nul +if errorlevel 1 ( + echo updater-w32: cannot copy tar.exe>&2 + exit /b 1 +) + +rem Set up perl environment +set PERL5LIB=%tlroot%\tlpkg\tlperl\lib + +rem Get package list +set pkgpartslist=%w32upd_args% +rem Construct package list without .source and .doc packages +rem Count the total number of packages while doing that +set pkgcount=0 +(set pkglist=) +call :foreachpkg remove_src_doc pkglist %pkgpartslist% + +rem Begin update +set exitcode=0 +echo DO NOT CLOSE THIS WINDOW! +echo TeX Live infrastructure update in progress... +call :foreachpkg untar "%tlroot%\temp\" %pkgpartslist% +if errorlevel 1 goto :rollback +call :foreachpkg incltlpobj "%tlroot%\tlpkg\tlpobj\" %pkgpartslist% +if errorlevel 1 goto :rollback +call :foreachpkg updateinfo "update" %pkglist% +if not defined w32upd_rerun_args goto :finishmsg +echo Restarting tlmgr to complete update ... +call %dbg% tlmgr %w32upd_rerun_args% +"%tlroot%\tlpkg\tlperl\bin\perl.exe" "%tlroot%\texmf\scripts\texlive\tlmgr.pl" %w32upd_rerun_args% +set exitcode=%errorlevel% +goto :finishmsg + +:rollback +call :foreachpkg updateinfo "failed update" %pkglist% >&2 +echo Rolling back to previous version... +call :foreachpkg untar "%tlroot%\temp\__BACKUP_" %pkglist% +if errorlevel 1 goto :panic +call :foreachpkg incltlpobj "%tlroot%\tlpkg\tlpobj\" %pkglist% +if errorlevel 1 goto :panic +call :foreachpkg restoreinfo "restore" %pkglist% +call :printmsg%w32upd_gui_mode% "TeX Live infrastructure update failed.\nPrevious version has been restored." 16 "ERROR: %w32upd_title%" >&2 +set exitcode=1 +goto :finish + +:panic +call :foreachpkg restoreinfo "failed restore" %pkglist% >&2 +call :printmsg%w32upd_gui_mode% "TeX Live infrastructure update failed and backup recovery failed as well.\nYour TeX Live installation may be broken, to repair it download and run:\nhttp://mirror.ctan.org/systems/texlive/tlnet/update-tlmgr-latest.exe" 16 "FATAL ERROR: %w32upd_title%" >&2 +rem Evil has taken over - emit adequate exit code +exit /b 666 + +:finishmsg +call :printmsg%w32upd_gui_mode% "TeX Live infrastructure update complete." 0 "%w32upd_title%" +:finish +del "%tlroot%\temp\tar-w32.exe" "%tlroot%\temp\__BACKUP_*" >nul 2>&1 +exit /b %exitcode% + +REM SUBROUTINES + +:foreachpkg cmd prefix pkgname oldrev newrev ... +set pkgnr=0 +:pkgloop +if [%3]==[] exit /b 0 +call :%1 %2 %3 %4 %5 +if errorlevel 1 exit /b 1 +shift /3 & shift /3 & shift /3 +goto :pkgloop + +:remove_src_doc +if /i %~x2.==.source. exit /b 0 +if /i %~x2.==.doc. exit /b 0 +set %1=%pkglist% %2 %3 %4 +set /a pkgcount+=1 +exit /b 0 + +:untar %tlroot%\temp\[__BACKUP_] pkgname +set "suffix=tar" +if %~n1_==__BACKUP__ set "suffix=r%~3.tar" +call %dbg% untar: "%~1%~2.%suffix%" +"%tlroot%\temp\tar-w32.exe" -C "%tlroot%" -f "%~1%~2.%suffix%" -x && del "%~1%~2.%suffix%" +exit /b %errorlevel% + +:incltlpobj %tlroot%\tlpkg\tlpobj\ pkgname +call %dbg% merge into tlpdb: "%~1%~2.tlpobj" +"%tlroot%\tlpkg\tlperl\bin\perl.exe" "%tlroot%\texmf\scripts\texlive\tlmgr.pl" _include_tlpobj "%~1%~2.tlpobj" +exit /b %errorlevel% + +:updateinfo header pkgname oldrev newrev +set "infomsg= -> %~4" +:restoreinfo header pkgname oldrev newrev +set "infomsg=%~1: %~2 (%~3%infomsg%)" +set "infomsg=%infomsg:>=^>%" +set /a pkgnr+=1 +echo [%pkgnr%/%pkgcount%] %infomsg% +echo [%date% %time%] %infomsg%>>"%logdir%\tlmgr.log" +set "infomsg=" +exit /b 0 + +:printmsg0 "message text" +set infomsg=%~1 +for %%I in ("%infomsg:\n=" "%") do echo.%%~I +exit /b 0 + +:printmsg1 "message text" icon "title string" +echo MsgBox Replace("%~1", "\n", Chr(10)), %2, "%~3" >"%tlroot%\temp\dialogpopup.vbs" +cscript -nologo "%tlroot%\temp\dialogpopup.vbs" +del "%tlroot%\temp\dialogpopup.vbs" +exit /b %errorlevel% + +rem Everything below is generated automatically by tlmgr on each infrastructure update -- cgit v1.2.3