summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2012-07-01 00:53:52 -0700
committerZac Medico <zmedico@gentoo.org>2012-07-01 00:53:52 -0700
commitf807bb54317db5f8073f8904897cf4b9d87bf2cd (patch)
tree7ca9992ec8b9a98942c293a374afac3489520614
parentman/emerge.1: note that --resume re-uses options (diff)
downloadportage-f807bb54317db5f8073f8904897cf4b9d87bf2cd.tar.gz
portage-f807bb54317db5f8073f8904897cf4b9d87bf2cd.tar.bz2
portage-f807bb54317db5f8073f8904897cf4b9d87bf2cd.zip
Support FEATURES={downgrade,unmerge}-backup
This will fix bug #156282 and bug #424275.
-rwxr-xr-xbin/quickpkg15
-rw-r--r--man/make.conf.513
-rw-r--r--pym/portage/const.py4
-rw-r--r--pym/portage/dbapi/vartree.py93
4 files changed, 108 insertions, 17 deletions
diff --git a/bin/quickpkg b/bin/quickpkg
index d908c0346..a6bd4d4bd 100755
--- a/bin/quickpkg
+++ b/bin/quickpkg
@@ -1,5 +1,5 @@
#!/usr/bin/python
-# Copyright 1999-2010 Gentoo Foundation
+# Copyright 1999-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
from __future__ import print_function
@@ -68,11 +68,14 @@ def quickpkg_atom(options, infos, arg, eout):
bintree.prevent_collision(cpv)
dblnk = vardb._dblink(cpv)
have_lock = False
- try:
- dblnk.lockdb()
- have_lock = True
- except PermissionDenied:
- pass
+
+ if "__PORTAGE_INHERIT_VARDB_LOCK" not in settings:
+ try:
+ dblnk.lockdb()
+ have_lock = True
+ except PermissionDenied:
+ pass
+
try:
if not dblnk.exists():
# unmerged by a concurrent process
diff --git a/man/make.conf.5 b/man/make.conf.5
index 7d07344bd..876a8a330 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -1,4 +1,4 @@
-.TH "MAKE.CONF" "5" "May 2012" "Portage VERSION" "Portage"
+.TH "MAKE.CONF" "5" "Jul 2012" "Portage VERSION" "Portage"
.SH "NAME"
make.conf \- custom settings for Portage
.SH "SYNOPSIS"
@@ -293,6 +293,12 @@ strangely configured Samba server (oplocks off, NFS re\-export). A tool
/usr/lib/portage/bin/clean_locks exists to help handle lock issues
when a problem arises (normally due to a crash or disconnect).
.TP
+.B downgrade\-backup
+When a package is downgraded to a lower version, call \fBquickpkg\fR(1)
+in order to create a backup of the installed version before it is
+unmerged (if a binary package of the same version does not already
+exist). Also see the related \fIunmerge\-backup\fR feature.
+.TP
.B ebuild\-locks
Use locks to ensure that unsandboxed ebuild phases never execute
concurrently. Also see \fIparallel\-install\fR.
@@ -514,6 +520,11 @@ continue to execute the remaining phases as if the failure had not occurred.
Note that the test phase for a specific package may be disabled by masking
the "test" \fBUSE\fR flag in \fBpackage.use.mask\fR (see \fBportage\fR(5)).
.TP
+.B unmerge\-backup
+Call \fBquickpkg\fR(1) to create a backup of each package before it is
+unmerged (if a binary package of the same version does not already exist).
+Also see the related \fIdowngrade\-backup\fR feature.
+.TP
.B unmerge\-logs
Keep logs from successful unmerge phases. This is relevant only when
\fBPORT_LOGDIR\fR is set.
diff --git a/pym/portage/const.py b/pym/portage/const.py
index 3607df0e0..4a077102e 100644
--- a/pym/portage/const.py
+++ b/pym/portage/const.py
@@ -90,7 +90,8 @@ SUPPORTED_FEATURES = frozenset([
"ccache", "chflags", "clean-logs",
"collision-protect", "compress-build-logs", "compressdebug",
"config-protect-if-modified",
- "digest", "distcc", "distcc-pump", "distlocks", "ebuild-locks", "fakeroot",
+ "digest", "distcc", "distcc-pump", "distlocks",
+ "downgrade-backup", "ebuild-locks", "fakeroot",
"fail-clean", "force-mirror", "force-prefix", "getbinpkg",
"installsources", "keeptemp", "keepwork", "fixlafiles", "lmirror",
"metadata-transfer", "mirror", "multilib-strict", "news",
@@ -103,6 +104,7 @@ SUPPORTED_FEATURES = frozenset([
"sign", "skiprocheck", "split-elog", "split-log", "splitdebug",
"strict", "stricter", "suidctl", "test", "test-fail-continue",
"unknown-features-filter", "unknown-features-warn",
+ "unmerge-backup",
"unmerge-logs", "unmerge-orphans", "userfetch", "userpriv",
"usersandbox", "usersync", "webrsync-gpg", "xattr"])
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index 34098eab1..0d7327ad4 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -21,6 +21,7 @@ portage.proxy.lazyimport.lazyimport(globals(),
'portage.package.ebuild.doebuild:doebuild_environment,' + \
'_merge_unicode_error', '_spawn_phase',
'portage.package.ebuild.prepare_build_dirs:prepare_build_dirs',
+ 'portage.package.ebuild._ipc.QueryCommand:QueryCommand',
'portage.update:fixdbentries',
'portage.util:apply_secpass_permissions,ConfigProtect,ensure_dirs,' + \
'writemsg,writemsg_level,write_atomic,atomic_ofstream,writedict,' + \
@@ -62,6 +63,7 @@ from _emerge.EbuildPhase import EbuildPhase
from _emerge.emergelog import emergelog
from _emerge.PollScheduler import PollScheduler
from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
+from _emerge.SpawnProcess import SpawnProcess
import errno
import fnmatch
@@ -1777,6 +1779,11 @@ class dblink(object):
showMessage = self._display_merge
if self.vartree.dbapi._categories is not None:
self.vartree.dbapi._categories = None
+
+ # When others_in_slot is not None, the backup has already been
+ # handled by the caller.
+ caller_handles_backup = others_in_slot is not None
+
# When others_in_slot is supplied, the security check has already been
# done for this slot, so it shouldn't be repeated until the next
# replacement or unmerge operation.
@@ -1842,6 +1849,11 @@ class dblink(object):
prepare_build_dirs(settings=self.settings, cleanup=True)
log_path = self.settings.get("PORTAGE_LOG_FILE")
+ if not caller_handles_backup:
+ retval = self._pre_unmerge_backup(background)
+ if retval != os.EX_OK:
+ return retval
+
# Log the error after PORTAGE_LOG_FILE is initialized
# by prepare_build_dirs above.
if eapi_unsupported:
@@ -3833,6 +3845,20 @@ class dblink(object):
self.delete()
ensure_dirs(self.dbtmpdir)
+ downgrade = False
+ if self._installed_instance is not None and \
+ vercmp(self.mycpv.version,
+ self._installed_instance.mycpv.version) < 0:
+ downgrade = True
+
+ if self._installed_instance is not None:
+ rval = self._pre_merge_backup(self._installed_instance, downgrade)
+ if rval != os.EX_OK:
+ showMessage(_("!!! FAILED preinst: ") +
+ "quickpkg: %s\n" % rval,
+ level=logging.ERROR, noiselevel=-1)
+ return rval
+
# run preinst script
showMessage(_(">>> Merging %(cpv)s to %(destroot)s\n") % \
{"cpv":self.mycpv, "destroot":destroot})
@@ -3866,20 +3892,15 @@ class dblink(object):
#if we have a file containing previously-merged config file md5sums, grab it.
self.vartree.dbapi._fs_lock()
try:
+ # Always behave like --noconfmem is enabled for downgrades
+ # so that people who don't know about this option are less
+ # likely to get confused when doing upgrade/downgrade cycles.
cfgfiledict = grabdict(self.vartree.dbapi._conf_mem_file)
- if "NOCONFMEM" in self.settings:
+ if "NOCONFMEM" in self.settings or downgrade:
cfgfiledict["IGNORE"]=1
else:
cfgfiledict["IGNORE"]=0
- # Always behave like --noconfmem is enabled for downgrades
- # so that people who don't know about this option are less
- # likely to get confused when doing upgrade/downgrade cycles.
- for other in others_in_slot:
- if vercmp(self.mycpv.version, other.mycpv.version) < 0:
- cfgfiledict["IGNORE"] = 1
- break
-
rval = self._merge_contents(srcroot, destroot, cfgfiledict)
if rval != os.EX_OK:
return rval
@@ -4682,6 +4703,60 @@ class dblink(object):
"Is this a regular package (does it have a CATEGORY file? A dblink can be virtual *and* regular)"
return os.path.exists(os.path.join(self.dbdir, "CATEGORY"))
+ def _pre_merge_backup(self, backup_dblink, downgrade):
+
+ if ("unmerge-backup" in self.settings.features or
+ (downgrade and "downgrade-backup" in self.settings.features)):
+ return self._quickpkg_dblink(backup_dblink, False, None)
+
+ return os.EX_OK
+
+ def _pre_unmerge_backup(self, background):
+
+ if "unmerge-backup" in self.settings.features :
+ logfile = None
+ if self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
+ logfile = self.settings.get("PORTAGE_LOG_FILE")
+ return self._quickpkg_dblink(self, background, logfile)
+
+ return os.EX_OK
+
+ def _quickpkg_dblink(self, backup_dblink, background, logfile):
+
+ trees = QueryCommand.get_db()[self.settings["EROOT"]]
+ bintree = trees["bintree"]
+ binpkg_path = bintree.getname(backup_dblink.mycpv)
+ if os.path.exists(binpkg_path) and \
+ backup_dblink.mycpv not in bintree.invalids:
+ return os.EX_OK
+
+ self.lockdb()
+ try:
+
+ if not backup_dblink.exists():
+ # It got unmerged by a concurrent process.
+ return os.EX_OK
+
+ # Call quickpkg for support of QUICKPKG_DEFAULT_OPTS and stuff.
+ quickpkg_binary = os.path.join(self.settings["PORTAGE_BIN_PATH"],
+ "quickpkg")
+
+ # Let quickpkg inherit the global vartree config's env.
+ env = dict(self.vartree.settings.items())
+ env["__PORTAGE_INHERIT_VARDB_LOCK"] = "1"
+
+ quickpkg_proc = SpawnProcess(
+ args=[portage._python_interpreter, quickpkg_binary,
+ "=%s" % (backup_dblink.mycpv,)],
+ background=background, env=env,
+ scheduler=self._scheduler, logfile=logfile)
+ quickpkg_proc.start()
+
+ return quickpkg_proc.wait()
+
+ finally:
+ self.unlockdb()
+
def merge(mycat, mypkg, pkgloc, infloc,
myroot=None, settings=None, myebuild=None,
mytree=None, mydbapi=None, vartree=None, prev_mtimes=None, blockers=None,