diff options
-rw-r--r-- | pym/_emerge/PackageUninstall.py | 57 | ||||
-rw-r--r-- | pym/portage/dbapi/_MergeProcess.py | 30 | ||||
-rw-r--r-- | pym/portage/dbapi/vartree.py | 36 |
3 files changed, 93 insertions, 30 deletions
diff --git a/pym/_emerge/PackageUninstall.py b/pym/_emerge/PackageUninstall.py index 0e9130713..0829f50b7 100644 --- a/pym/_emerge/PackageUninstall.py +++ b/pym/_emerge/PackageUninstall.py @@ -4,17 +4,45 @@ import logging import portage from portage import os +from portage.dbapi._MergeProcess import MergeProcess +from portage.exception import UnsupportedAPIException +from _emerge.EbuildBuildDir import EbuildBuildDir from _emerge.emergelog import emergelog from _emerge.CompositeTask import CompositeTask from _emerge.unmerge import _unmerge_display class PackageUninstall(CompositeTask): + """ + Uninstall a package asynchronously in a subprocess. When + both parallel-install and ebuild-locks FEATURES are enabled, + it is essential for the ebuild-locks code to execute in a + subprocess, since the portage.locks module does not behave + as desired if we try to lock the same file multiple times + concurrently from the same process. + """ __slots__ = ("world_atom", "ldpath_mtimes", "opts", - "pkg", "settings") + "pkg", "settings", "_builddir_lock") def _start(self): + self.settings.setcpv(self.pkg) + vardb = self.pkg.root_config.trees["vartree"].dbapi + dbdir = vardb.getpath(self.pkg.cpv) + cat, pf = portage.catsplit(self.pkg.cpv) + myebuildpath = os.path.join(dbdir, pf + ".ebuild") + + try: + portage.doebuild_environment(myebuildpath, "prerm", + settings=self.settings, db=vardb) + except UnsupportedAPIException: + # This is safe to ignore since this function is + # guaranteed to set PORTAGE_BUILDDIR even though + # it raises UnsupportedAPIException. The error + # will be logged when it prevents the pkg_prerm + # and pkg_postrm phases from executing. + pass + retval, pkgmap = _unmerge_display(self.pkg.root_config, self.opts, "unmerge", [self.pkg.cpv], clean_delay=0, writemsg_level=self._writemsg_level) @@ -29,18 +57,31 @@ class PackageUninstall(CompositeTask): self._emergelog("=== Unmerging... (%s)" % (self.pkg.cpv,)) cat, pf = portage.catsplit(self.pkg.cpv) - retval = portage.unmerge(cat, pf, settings=self.settings, - vartree=self.pkg.root_config.trees["vartree"], - ldpath_mtimes=self.ldpath_mtimes, - scheduler=self.scheduler) - if retval != os.EX_OK: + self._builddir_lock = EbuildBuildDir( + scheduler=self.scheduler, settings=self.settings) + self._builddir_lock.lock() + + portage.prepare_build_dirs( + settings=self.settings, cleanup=True) + + unmerge_task = MergeProcess( + mycat=cat, mypkg=pf, settings=self.settings, + treetype="vartree", vartree=self.pkg.root_config.trees["vartree"], + scheduler=self.scheduler, background=self.background, + mydbapi=self.pkg.root_config.trees["vartree"].dbapi, + prev_mtimes=self.ldpath_mtimes, + logfile=self.settings.get("PORTAGE_LOG_FILE"), unmerge=True) + + self._start_task(unmerge_task, self._unmerge_exit) + + def _unmerge_exit(self, unmerge_task): + if self._final_exit(unmerge_task) != os.EX_OK: self._emergelog(" !!! unmerge FAILURE: %s" % (self.pkg.cpv,)) else: self._emergelog(" >>> unmerge success: %s" % (self.pkg.cpv,)) self.world_atom(self.pkg) - - self.returncode = retval + self._builddir_lock.unlock() self.wait() def _emergelog(self, msg): diff --git a/pym/portage/dbapi/_MergeProcess.py b/pym/portage/dbapi/_MergeProcess.py index c2286067c..43bec7294 100644 --- a/pym/portage/dbapi/_MergeProcess.py +++ b/pym/portage/dbapi/_MergeProcess.py @@ -3,6 +3,7 @@ import shutil import signal +import sys import tempfile import traceback @@ -26,7 +27,7 @@ class MergeProcess(SpawnProcess): __slots__ = ('mycat', 'mypkg', 'settings', 'treetype', 'vartree', 'scheduler', 'blockers', 'pkgloc', 'infloc', 'myebuild', - 'mydbapi', 'prev_mtimes', '_elog_reader_fd', '_elog_reg_id', + 'mydbapi', 'prev_mtimes', 'unmerge', '_elog_reader_fd', '_elog_reg_id', '_buf', '_elog_keys', '_locked_vdb') def _start(self): @@ -45,7 +46,8 @@ class MergeProcess(SpawnProcess): settings.reset() settings.setcpv(cpv, mydb=self.mydbapi) - self._handle_self_reinstall() + if not self.unmerge: + self._handle_self_reinstall() super(MergeProcess, self)._start() def _lock_vdb(self): @@ -170,7 +172,9 @@ class MergeProcess(SpawnProcess): # FEATURES=parallel-install skips this lock in order to # improve performance, and the risk is practically negligible. self._lock_vdb() - counter = self.vartree.dbapi.counter_tick() + counter = None + if not self.unmerge: + counter = self.vartree.dbapi.counter_tick() pid = os.fork() if pid != 0: @@ -213,7 +217,7 @@ class MergeProcess(SpawnProcess): # already be opened by the parent process, so we set the # "subprocess" value for use in conditional logging code # involving PORTAGE_LOG_FILE. - if self.settings.get("PORTAGE_BACKGROUND") == "1": + if not self.unmerge and self.settings.get("PORTAGE_BACKGROUND") == "1": # unmerge phases have separate logs self.settings["PORTAGE_BACKGROUND_UNMERGE"] = "1" self.settings.backup_changes("PORTAGE_BACKGROUND_UNMERGE") @@ -222,9 +226,21 @@ class MergeProcess(SpawnProcess): rval = 1 try: - rval = mylink.merge(self.pkgloc, self.infloc, - myebuild=self.myebuild, mydbapi=self.mydbapi, - prev_mtimes=self.prev_mtimes, counter=counter) + if self.unmerge: + if not mylink.exists(): + rval = os.EX_OK + elif mylink.unmerge( + ldpath_mtimes=self.prev_mtimes) == os.EX_OK: + mylink.lockdb() + try: + mylink.delete() + finally: + mylink.unlockdb() + rval = os.EX_OK + else: + rval = mylink.merge(self.pkgloc, self.infloc, + myebuild=self.myebuild, mydbapi=self.mydbapi, + prev_mtimes=self.prev_mtimes, counter=counter) except SystemExit: raise except: diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index 1867cf299..a9d2d147c 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -1604,6 +1604,7 @@ class dblink(object): DeprecationWarning, stacklevel=2) background = False + log_path = None if self._scheduler is None: # We create a scheduler instance and use it to # log unmerge output separately from merge output. @@ -1614,7 +1615,8 @@ class dblink(object): self.settings.backup_changes("PORTAGE_BACKGROUND") background = True else: - self.settings.pop("PORTAGE_BACKGROUND", None) + # Our output is redirected and logged by the parent process. + log_path = self.settings.pop("PORTAGE_LOG_FILE", None) elif self.settings.get("PORTAGE_BACKGROUND") == "1": background = True @@ -1647,7 +1649,6 @@ class dblink(object): myebuildpath = None failures = 0 ebuild_phase = "prerm" - log_path = None mystuff = os.listdir(self.dbdir) for x in mystuff: if x.endswith(".ebuild"): @@ -1659,7 +1660,11 @@ class dblink(object): write_atomic(os.path.join(self.dbdir, "PF"), self.pkg+"\n") break - self.settings.setcpv(self.mycpv, mydb=self.vartree.dbapi) + if self.mycpv != self.settings.mycpv or \ + "SLOT" not in self.settings.configdict["pkg"]: + # We avoid a redundant setcpv call here when + # the caller has already taken care of it. + self.settings.setcpv(self.mycpv, mydb=self.vartree.dbapi) if myebuildpath: try: doebuild_environment(myebuildpath, "prerm", @@ -1677,21 +1682,22 @@ class dblink(object): self._prune_plib_registry(unmerge=True, needed=needed, preserve_paths=preserve_paths) + builddir_locked = "PORTAGE_BUILDIR_LOCKED" in self.settings builddir_lock = None - log_path = None scheduler = self._scheduler retval = os.EX_OK try: if myebuildpath: - # Only create builddir_lock if doebuild_environment - # succeeded, since that's needed to initialize - # PORTAGE_BUILDDIR. - builddir_lock = EbuildBuildDir( - scheduler=scheduler, - settings=self.settings) - builddir_lock.lock() - prepare_build_dirs(settings=self.settings, cleanup=True) - log_path = self.settings.get("PORTAGE_LOG_FILE") + # Only create builddir_lock if the caller + # has not already acquired the lock. + if not builddir_locked: + builddir_lock = EbuildBuildDir( + scheduler=scheduler, + settings=self.settings) + builddir_lock.lock() + builddir_locked = True + prepare_build_dirs(settings=self.settings, cleanup=True) + log_path = self.settings.get("PORTAGE_LOG_FILE") phase = EbuildPhase(background=background, phase=ebuild_phase, scheduler=scheduler, @@ -1728,7 +1734,7 @@ class dblink(object): finally: self.vartree.dbapi._bump_mtime(self.mycpv) - if builddir_lock: + if builddir_locked: try: if myebuildpath: if retval != os.EX_OK: @@ -1771,7 +1777,7 @@ class dblink(object): self._elog_process(phasefilter=("prerm", "postrm")) - if retval == os.EX_OK and builddir_lock is not None: + if retval == os.EX_OK and builddir_locked: # myebuildpath might be None, so ensure # it has a sane value for the clean phase, # even though it won't really be sourced. |