--- toast 2004/02/21 20:57:20 1.299 +++ toast 2004/02/25 05:32:38 1.300 @@ -229,34 +229,36 @@ "storedir" => (superuser || !$ENV{HOME}) ? "/toast" : "$ENV{HOME}/.toast", "armdir" => superuser ? "/usr/local" : "armed", + "altarmdirs" => "", "username" => "toast", "postarmprog" => superuser ? "/sbin/ldconfig" : "", "editprog" => "", "defaultcmd" => "help", "httpproxy" => exists($ENV{http_proxy}) ? $ENV{http_proxy} : "", "ftpproxy" => exists($ENV{ftp_proxy}) ? $ENV{ftp_proxy} : "", - "quiet" => false, - "autofind" => true, - "autochange" => true, - "autorename" => true, - "autoclean" => true, - "autopurge" => false, - "autoarm" => true, - "autodisarm" => true, - "autodemolish" => true, - "autoremove" => false, - "crossversion" => false, - "strictpreload" => true, - "useflock" => $^O !~ /win/i, - "reconfigure" => true, - "fixliblinks" => true, - "stoponerror" => true, - "ignorecase" => true, - "showurls" => true, - "infodir" => true, - "protect" => true, - "relative" => false, - "debugrewrite" => false, + "quiet" => "false", + "autofind" => "true", + "autochange" => "true", + "autorename" => "true", + "autoclean" => "true", + "autopurge" => "false", + "autoarm" => "true", + "autodisarm" => "true", + "autodemolish" => "true", + "autoremove" => "false", + "crossversion" => "false", + "skipmismatched" => "true", + "strictpreload" => "true", + "useflock" => $^O =~ /win/i ? "false" : "true", + "reconfigure" => "true", + "fixliblinks" => "true", + "stoponerror" => "true", + "ignorecase" => "true", + "showurls" => "true", + "infodir" => "true", + "protect" => "true", + "relative" => "false", + "debugrewrite" => "false", ); sub envopt($) @@ -289,7 +291,7 @@ my($name) = @_; return false unless isopt($name); my($def) = $optdefault{$name}; - return defined($def) && ($def eq true || $def eq false); + return defined($def) && ($def eq "true" || $def eq "false"); } sub checkoptname($) @@ -366,6 +368,7 @@ sub archivedir() { "archive" } sub editdir() { "edit" } sub urlfile() { "url" } +sub armdirlink() { "armdir" } sub srcdir() { "src" } sub helperdir() { "helpers" } sub rootdir() { "root" } @@ -1598,9 +1601,9 @@ defined($name) || error; defined($version) || error; return undef unless isversion($name, $version); - for $build (allbuilds($name, $version, $build)) + for $build (reverse(allbuilds($name, $version, $build))) { - return $build if isbuilt($name, $version, $build); + return $build if isbuiltmatch($name, $version, $build); } return undef; } @@ -1662,33 +1665,95 @@ return !-d(path(pkgpath($name, $version, $build), srcdir)); } +sub isbuildarmedin($$$$) +{ + my($armdir, $name, $version, $build) = @_; + $build || error; + my($rootdir) = path(pkgpath($name, $version, $build), rootdir); + return -d($rootdir) && !dfs + ( + $rootdir, + sub { true }, + sub + { + my($rel) = @_; + my($armfile) = path($armdir, $rel); + while(-e($armfile) || -l($armfile)) + { + return false if optsamefile($_, $armfile); + $armfile = addoff($armfile); + } + return true; + }, + sub { true } + ); +} + +sub allarmdirs() +{ + my(@armdirs, %seendi); + for (armdir, split(/:/, altarmdirs)) + { + my($armdir) = m!^/! ? $_ : path(storedir, $_); + my($device, $inode) = stat($armdir); + next unless defined($device); + next unless -d(_); + my($di) = "$device $inode"; + next if exists($seendi{$di}); + $seendi{$di} = true; + push(@armdirs, $armdir); + } + return @armdirs; +} + sub isarmed(@) { return !whilebuild { my($name, $version, $build) = @_; - my($rootdir) = path(pkgpath($name, $version, $build), rootdir); - my($armed) = -d($rootdir) && !dfs - ( - $rootdir, - sub { true }, - sub - { - my($rel) = @_; - my($armfile) = path(armdir, $rel); - while(-e($armfile) || -l($armfile)) - { - return false if optsamefile($_, $armfile); - $armfile = addoff($armfile); - } - return true; - }, - sub { true } - ); - !$armed; + for (allarmdirs) + { + return false if isbuildarmedin($_, $name, $version, $build); + } + return true; } @_; } +sub isarmedmatch(@) +{ + return isarmed(@_) if !skipmismatched; + return !whilebuild + { + my($name, $version, $build) = @_; + return !isbuildarmedin(armdir, $name, $version, $build); + }; +} + +sub ismismatched($$$) +{ + my($name, $version, $build) = @_; + $build || error; + my($armdirlink) = path(pkgpath($name, $version, $build), armdirlink); + my($armdirisdir, $linkisdir) = (-d(armdir), -d($armdirlink)); + return !samefile($armdirlink, armdir) if $armdirisdir && $linkisdir; + return true if $armdirisdir && !$linkisdir; + return true if !$armdirisdir && $linkisdir; + my($target) = readlink($armdirlink); + return false unless defined($target); + return $target ne armdir; +} + +sub isbuiltmatch(@) +{ + return isbuilt(@_) if !skipmismatched; + my($name, $version, $build) = @_; + return !whilebuild + { + my($name, $version, $build) = @_; + return !isbuilt(@_) || ismismatched($name, $version, $build); + } @_; +} + ############################################################################## sub lookslikepkgurl($;$;$) @@ -3114,7 +3179,9 @@ my($srcdir) = path($builddir, srcdir); my($helperdir) = path($builddir, helperdir); my($rootdir) = path($builddir, rootdir); + my($armdirlink) = path($builddir, armdirlink); + ln(armdir, $armdirlink); md($srcdir); extract($archivedir, $srcdir); compile($srcdir, $rootdir, $helperdir); @@ -3172,19 +3239,29 @@ clean($name, $version, $build) if autoclean; purge($name, $version) if autopurge; - arm($name, $version, $build) if autoarm && isarmed($name, $version); + arm($name, $version, $build) if autoarm && isarmedmatch($name, $version); if(autodemolish || autoremove) { my($aversion, $abuild); for $aversion (allversions($name, crossversion ? undef : $version)) { - for $abuild (allbuilds($name, $aversion)) + if(autodemolish) { - demolish($name, $aversion, $abuild) - if autodemolish && ($aversion ne $version || $abuild != $build); + for $abuild (allbuilds($name, $aversion)) + { + next if $aversion eq $version && $abuild == $build; + next if skipmismatched && ismismatched($name, $aversion, $abuild); + next if !autodisarm && isarmed($name, $aversion, $abuild); + demolish($name, $aversion, $abuild); + } } - remove($name, $version) if autoremove && $aversion ne $version; + if(autoremove) + { + next if $aversion eq $version; + next if !autodisarm && isarmed($name, $aversion); + remove($name, $aversion); + } } } @@ -3265,9 +3342,10 @@ } } -sub rebuildinfodir() +sub rebuildinfodir($) { - my($dir) = path(armdir, "info"); + my($armdir) = @_; + my($dir) = path($armdir, "info"); return true unless -d($dir); my($dirfile) = path($dir, "dir"); @@ -3304,10 +3382,13 @@ safechmod($mode, $dir); } -sub postarm() +sub postarm(;$$) { - rebuildinfodir; - run(postarmprog) if postarmprog; + my($armdir, $postarmprog) = @_; + $armdir = armdir unless defined($armdir); + $postarmprog = postarmprog unless defined($postarmprog); + rebuildinfodir($armdir); + run($postarmprog) if $postarmprog; return true; } @@ -3394,60 +3475,65 @@ sub disarm(@) { - my($name, $version, $build, @urls) = @_; + my($name, $version, $build) = @_; - lock(armdir); - - whilebuild + my(@armdirs) = allarmdirs; + my($i, $armdir); + for $armdir (@armdirs) { - my($name, $version, $build) = @_; - my($rootdir) = path(pkgpath($name, $version, $build), rootdir); - my(@dirmodes); - -d($rootdir) && dfs # ignore broken packages - ( - $rootdir, - sub - { - my($rel) = @_; - my($armdir) = optpath(armdir, $rel); - if(-d($armdir) && !-l($armdir)) + lock($armdir); + + whilebuild + { + my($name, $version, $build) = @_; + + my($rootdir) = path(pkgpath($name, $version, $build), rootdir); + my(@dirmodes); + -d($rootdir) && dfs # ignore broken packages + ( + $rootdir, + sub { - push(@dirmodes, getmode($armdir)); - safechmod(0777, $armdir); - } - return true; - }, - sub - { - my($rel) = @_; - my($armfile) = path(armdir, $rel); # BUG: $rel is sometimes undefined? - while(-e($armfile) || -l($armfile)) + my($rel) = @_; + my($armsubdir) = optpath($armdir, $rel); + if(-d($armsubdir) && !-l($armsubdir)) + { + push(@dirmodes, getmode($armsubdir)); + safechmod(0777, $armsubdir); + } + return true; + }, + sub { - return replace($armfile) if optsamefile($armfile, $_); - $armfile = addoff($armfile); - } - return true; - }, - sub - { - my($rel) = @_; - my($armdir) = optpath(armdir, $rel); - if(-d($armdir) && !-l($armdir)) + my($rel) = @_; + my($armfile) = path($armdir, $rel); # BUG: $rel is sometimes undef? + while(-e($armfile) || -l($armfile)) + { + return replace($armfile) if optsamefile($armfile, $_); + $armfile = addoff($armfile); + } + return true; + }, + sub { - my($mode) = pop(@dirmodes); - isempty($armdir) ? rd($armdir) : safechmod($mode, $armdir); + my($rel) = @_; + my($armsubdir) = optpath($armdir, $rel); + if(-d($armsubdir) && !-l($armsubdir)) + { + my($mode) = pop(@dirmodes); + isempty($armsubdir) ? rd($armsubdir) : safechmod($mode, $armsubdir); + } + return true; } - return true; - } - ); - error if @dirmodes; - true; - } @_; + ); + error if @dirmodes; + return true; + } ($name, $version, $build); - postarm; + postarm($armdir, ++$i == scalar(@armdirs) ? postarmprog : ""); + unlock($armdir); + } - unlock(armdir); - return true; } @@ -3538,11 +3624,11 @@ } my(@cmdargs) = ($name, $newver, undef, @newurls); - if(isarmed($name, $version) && autoarm) + if(autoarm && isarmedmatch($name, $version)) { return arm(@cmdargs); } - elsif(isbuilt($name, $version)) + elsif(isbuiltmatch($name, $version)) { return build(@cmdargs); } @@ -3584,36 +3670,21 @@ ############################################################################## -sub verstatus($$) -{ - my($name, $version) = @_; - return "stored" if isstored($name, $version); - return false; -} - -sub buildstatus($$$) -{ - my($name, $version, $build) = @_; - return "broken" if isbroken($name, $version, $build); - return "building" unless isbuilt($name, $version, $build); - my($nc) = isclean($name, $version, $build) ? "" : " (not clean)"; - return "armed$nc" if isarmed($name, $version, $build); - return "built$nc"; -} - sub status(@) { my($name, $version, $build, @urls) = @_; my($result) = true; + my(@armdirs) = allarmdirs; + for $name (allnames($name)) { print("$name\n"); for $version (allversions($name, $version)) { - my($vs) = verstatus($name, $version); - print(" version $version", $vs ? ": $vs\n" : "\n"); + print(" version $version", + isstored($name, $version) ? ": stored\n" : "\n"); if(showurls || @urls) { @@ -3630,8 +3701,50 @@ for $build (allbuilds($name, $version, $build)) { - print(" build $build: ", - buildstatus($name, $version, $build), "\n"); + my($status, @notes, @armedin); + my($normalarmdir) = armdir; + if(isbroken($name, $version, $build)) + { + $status = "broken"; + } + elsif(!isbuilt($name, $version, $build)) + { + $status = "building"; + } + else + { + push(@notes, "not clean") unless isclean($name, $version, $build); + for(@armdirs) + { + push(@armedin, $_) if isbuildarmedin($_, $name, $version, $build); + } + if(ismismatched($name, $version, $build)) + { + $status = "mismatched"; + my($armdirlink) = + path(pkgpath($name, $version, $build), armdirlink); + my($builtfor) = readlink($armdirlink); + push(@notes, "built for $builtfor") if defined($builtfor); + push(@notes, "armed") if @armedin; + $normalarmdir = $armdirlink; + } + elsif(@armedin) + { + $status = "armed"; + } + else + { + $status = "built"; + } + } + my($notetext) = @notes ? " (" . join("; ", @notes) . ")" : ""; + print(" build $build: $status$notetext\n"); + if(@armedin && (scalar(@armedin) > 1 || + !optsamefile($armedin[0], $normalarmdir))) + { + print(" armed in:\n"); + print(" $_\n") for @armedin; + } } } } @@ -5301,12 +5414,14 @@ =item S<B<toast disarm> I<BUILD> | I<PACKAGE> ...> -Deletes symlinks created by B<toast arm>. This works by removing -symbolic links to the given build and replacing any links that had -been moved out of the way. No error occurs if no such links exist. -If no build number is given, all C<armed> builds are disarmed. If the -package version number is also omitted, all C<armed> builds belonging -to packages with the given name are disarmed. +Deletes symlinks created by B<toast arm>. This works by removing symbolic +links to the given build and replacing any links that had been moved +out of the way. No error occurs if no such links exist. If no build +number is given, all C<armed> builds, including C<mismatched> builds +reported to be C<armed>, are disarmed from all directories specified +by the B<armdir> and B<altarmdirs> options. If the package version +number is also omitted, all C<armed> builds belonging to packages with +the given name are disarmed. =item S<B<toast clean> [ I<BUILD> | I<PACKAGE> ...]> @@ -5539,6 +5654,17 @@ may not work if installed in a different location. Default: C</usr/local> if invoked by root, C<armed> otherwise. +=item B<--altarmdirs=>I<ALTARMDIR>[:I<ALTARMDIR>]... + +Specifies an optional colon-separated list of alternate directories to +search for armed packages after searching I<ARMDIR>. It is strongly +recommended that this list include any directory where a package is +likely to be armed. is searched to determine whether a package is +currently armed. When disarming a package, links will be removed from +each I<ALTARMDIR> in addition to I<ARMDIR> itself. If an I<ALTARMDIR> +is not an absolute path, it is taken to be relative to I<STOREDIR>. +Default: empty list. + =item B<--username=>I<USER> When invoked as root, B<toast build> will unpack, compile, and install @@ -5672,11 +5798,11 @@ =item S<B<--autoremove> | B<--noautoremove>> -When both B<autoremove> and B<crossversion> are enabled, B<toast build> -performs an implicit B<toast remove> on every other package with the -same name as the package containing a newly-created, non-broken build. -If B<crossversion> is disabled, this option has no effect. Default: -disabled. +When both B<autoremove> and B<crossversion> are enabled, every time a +command creates a new non-broken build, it will also perform an implicit +B<toast remove> on every other package with the same name as the package +containing a newly-created, non-broken build. If B<crossversion> is +disabled, this option has no effect. Default: disabled. =item S<B<--crossversion> | B<--nocrossversion>> @@ -5685,6 +5811,22 @@ the same name when appropriate. See the descriptions of those options for details. Default: disabled. +=item S<B<--skipmismatched> | B<--noskipmismatched>> + +When B<skipmismatched> is enabled, some operations will ignore any +pre-existing build that was built with a value of B<armdir> different +than the current one and is therefore reported as C<mismatched> by +B<toast status>. Specifically, B<toast build> will ignore mismatched +builds when deciding whether or not to create a new build; B<toast +upgrade> will ignore mismatched builds when deciding whether to build or +arm the new version; and the B<autodemolish> option will not demolish +mismatched builds. All other checks are unaffected by this option. +For example, B<toast remove> will never remove an armed build without +completely disarming it first, even if thie build is mismatched and this +option is enabled. When B<skipmismatched> is disabled, mismatched builds +are never treated specially. See also the B<disarmmismatched> option. +Default: enabled. + =item S<B<--strictpreload> | B<--nostrictpreload>> When B<strictpreload> is enabled, B<toast build> will fail unless it @@ -5905,11 +6047,10 @@ wxpython - autofind chooses Linux binaries over source for doxygen - build fails for: jikes, sirc, netcat, lcab, gv, bittorrent - - "toast --autoremove --crossversion upgrade toast" breaks everything - - if x/1/1 is armed and x/1/2 is built, "toast arm x" does nothing - - if x/1/1 is armed, "toast --noautodisarm rebuild x" arms then fails - "toast add foo/bar-1.2.tar.gz" guesses "foo version bar-1.2.tar.gz" - openprog() still emits redundant exec() warnings + - toast build hangs in md5sum if a package has no archives or urls + - in some environments, toast env spews warnings if MANPATH is unset Wish list: