blob: 43210b4bc382f52e7b95df31184a6900b05a22d7 [file] [log] [blame]
# Copyright 1999-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
import functools
import logging
import portage
from portage import os
from portage.dbapi._MergeProcess import MergeProcess
from portage.exception import UnsupportedAPIException
from portage.util._async.AsyncTaskFuture import AsyncTaskFuture
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 for ebuild-locks phases
such as pkg_setup, pkg_prerm, and pkg_postrm.
"""
__slots__ = ("world_atom", "ldpath_mtimes", "opts",
"pkg", "settings", "_builddir_lock")
def _start(self):
vardb = self.pkg.root_config.trees["vartree"].dbapi
dbdir = vardb.getpath(self.pkg.cpv)
if not os.path.exists(dbdir):
# Apparently the package got uninstalled
# already, so we can safely return early.
self.returncode = os.EX_OK
self._async_wait()
return
self.settings.setcpv(self.pkg)
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
self._builddir_lock = EbuildBuildDir(
scheduler=self.scheduler, settings=self.settings)
self._start_task(
AsyncTaskFuture(future=self._builddir_lock.async_lock()),
self._start_unmerge)
def _start_unmerge(self, lock_task):
self._assert_current(lock_task)
if lock_task.cancelled:
self._default_final_exit(lock_task)
return
lock_task.future.result()
portage.prepare_build_dirs(
settings=self.settings, cleanup=True)
# Output only gets logged if it comes after prepare_build_dirs()
# which initializes PORTAGE_LOG_FILE.
retval, pkgmap = _unmerge_display(self.pkg.root_config,
self.opts, "unmerge", [self.pkg.cpv], clean_delay=0,
writemsg_level=self._writemsg_level)
if retval != os.EX_OK:
self._async_unlock_builddir(returncode=retval)
return
self._writemsg_level(">>> Unmerging %s...\n" % (self.pkg.cpv,),
noiselevel=-1)
self._emergelog("=== Unmerging... (%s)" % (self.pkg.cpv,))
cat, pf = portage.catsplit(self.pkg.cpv)
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._async_unlock_builddir(returncode=self.returncode)
def _async_unlock_builddir(self, returncode=None):
"""
Release the lock asynchronously, and if a returncode parameter
is given then set self.returncode and notify exit listeners.
"""
if returncode is not None:
# The returncode will be set after unlock is complete.
self.returncode = None
self._start_task(
AsyncTaskFuture(future=self._builddir_lock.async_unlock()),
functools.partial(self._unlock_builddir_exit, returncode=returncode))
def _unlock_builddir_exit(self, unlock_task, returncode=None):
self._assert_current(unlock_task)
if unlock_task.cancelled and returncode is not None:
self._default_final_exit(unlock_task)
return
# Normally, async_unlock should not raise an exception here.
unlock_task.future.cancelled() or unlock_task.future.result()
if returncode is not None:
self.returncode = returncode
self._async_wait()
def _emergelog(self, msg):
emergelog("notitles" not in self.settings.features, msg)
def _writemsg_level(self, msg, level=0, noiselevel=0):
log_path = self.settings.get("PORTAGE_LOG_FILE")
background = self.background
if log_path is None:
if not (background and level < logging.WARNING):
portage.util.writemsg_level(msg,
level=level, noiselevel=noiselevel)
else:
self.scheduler.output(msg, log_path=log_path,
level=level, noiselevel=noiselevel)