224 lines
10 KiB
Diff
224 lines
10 KiB
Diff
|
From b105e7fe812da3ccaf7155c0fe14c8728b0d39a5 Mon Sep 17 00:00:00 2001
|
||
|
From: Mark Hatle <mark.hatle@windriver.com>
|
||
|
Date: Mon, 20 Jan 2014 14:30:52 +0000
|
||
|
Subject: [PATCH] Add mechanism to attempt install without failing
|
||
|
|
||
|
In OpenEmbedded, for complementary and 'attemptonly' package processing,
|
||
|
we need a way to instruct smart to try to install, but ignore any
|
||
|
failures (usually conflicts).
|
||
|
|
||
|
This option only works for the install operation.
|
||
|
|
||
|
If a complementary install fails, an actual error occurred, one that
|
||
|
we can't ignore without losing the entire attempted transaction. Keep
|
||
|
this as an error so that we can catch these cases in the futre.
|
||
|
|
||
|
Upstream-Status: Pending
|
||
|
|
||
|
Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
|
||
|
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
|
||
|
---
|
||
|
smart.py | 5 +++-
|
||
|
smart/commands/install.py | 5 ++++
|
||
|
smart/transaction.py | 65 +++++++++++++++++++++++++++++++++++------------
|
||
|
3 files changed, 58 insertions(+), 17 deletions(-)
|
||
|
|
||
|
Index: smart-1.4.1/smart/commands/install.py
|
||
|
===================================================================
|
||
|
--- smart-1.4.1.orig/smart/commands/install.py
|
||
|
+++ smart-1.4.1/smart/commands/install.py
|
||
|
@@ -50,6 +50,8 @@ def option_parser():
|
||
|
parser = OptionParser(usage=USAGE,
|
||
|
description=DESCRIPTION,
|
||
|
examples=EXAMPLES)
|
||
|
+ parser.add_option("--attempt", action="store_true",
|
||
|
+ help=_("attempt to install packages, ignore failures"))
|
||
|
parser.add_option("--stepped", action="store_true",
|
||
|
help=_("split operation in steps"))
|
||
|
parser.add_option("--urls", action="store_true",
|
||
|
@@ -80,6 +82,9 @@ def main(ctrl, opts):
|
||
|
if not opts.args:
|
||
|
raise Error, _("no package(s) given")
|
||
|
|
||
|
+ if opts.attempt:
|
||
|
+ sysconf.set("attempt-install", True, soft=True)
|
||
|
+
|
||
|
if opts.explain:
|
||
|
sysconf.set("explain-changesets", True, soft=True)
|
||
|
|
||
|
Index: smart-1.4.1/smart/transaction.py
|
||
|
===================================================================
|
||
|
--- smart-1.4.1.orig/smart/transaction.py
|
||
|
+++ smart-1.4.1/smart/transaction.py
|
||
|
@@ -555,6 +555,8 @@ class Transaction(object):
|
||
|
changeset.set(pkg, INSTALL)
|
||
|
isinst = changeset.installed
|
||
|
|
||
|
+ attempt = sysconf.has("attempt-install", soft=True)
|
||
|
+
|
||
|
# Remove packages conflicted by this one.
|
||
|
for cnf in pkg.conflicts:
|
||
|
for prv in cnf.providedby:
|
||
|
@@ -564,11 +566,16 @@ class Transaction(object):
|
||
|
if not isinst(prvpkg):
|
||
|
locked[prvpkg] = (LOCKED_CONFLICT_BY, pkg)
|
||
|
continue
|
||
|
- if prvpkg in locked:
|
||
|
- raise Failed, _("Can't install %s: conflicted package "
|
||
|
- "%s is locked") % (pkg, prvpkg)
|
||
|
- self._remove(prvpkg, changeset, locked, pending, depth)
|
||
|
- pending.append((PENDING_UPDOWN, prvpkg))
|
||
|
+ if attempt:
|
||
|
+ del changeset[pkg]
|
||
|
+ raise Failed, _("Can't install %s: it conflicts with package "
|
||
|
+ "%s") % (pkg, prvpkg)
|
||
|
+ else:
|
||
|
+ if prvpkg in locked:
|
||
|
+ raise Failed, _("Can't install %s: conflicted package "
|
||
|
+ "%s is locked") % (pkg, prvpkg)
|
||
|
+ self._remove(prvpkg, changeset, locked, pending, depth)
|
||
|
+ pending.append((PENDING_UPDOWN, prvpkg))
|
||
|
|
||
|
# Remove packages conflicting with this one.
|
||
|
for prv in pkg.provides:
|
||
|
@@ -579,12 +586,18 @@ class Transaction(object):
|
||
|
if not isinst(cnfpkg):
|
||
|
locked[cnfpkg] = (LOCKED_CONFLICT, pkg)
|
||
|
continue
|
||
|
- if cnfpkg in locked:
|
||
|
+ if attempt:
|
||
|
+ del changeset[pkg]
|
||
|
raise Failed, _("Can't install %s: it's conflicted by "
|
||
|
- "the locked package %s") \
|
||
|
- % (pkg, cnfpkg)
|
||
|
- self._remove(cnfpkg, changeset, locked, pending, depth)
|
||
|
- pending.append((PENDING_UPDOWN, cnfpkg))
|
||
|
+ "the package %s") \
|
||
|
+ % (pkg, cnfpkg)
|
||
|
+ else:
|
||
|
+ if cnfpkg in locked:
|
||
|
+ raise Failed, _("Can't install %s: it's conflicted by "
|
||
|
+ "the locked package %s") \
|
||
|
+ % (pkg, cnfpkg)
|
||
|
+ self._remove(cnfpkg, changeset, locked, pending, depth)
|
||
|
+ pending.append((PENDING_UPDOWN, cnfpkg))
|
||
|
|
||
|
# Remove packages with the same name that can't
|
||
|
# coexist with this one.
|
||
|
@@ -594,10 +607,15 @@ class Transaction(object):
|
||
|
if not isinst(namepkg):
|
||
|
locked[namepkg] = (LOCKED_NO_COEXIST, pkg)
|
||
|
continue
|
||
|
- if namepkg in locked:
|
||
|
+ if attempt:
|
||
|
+ del changeset[pkg]
|
||
|
raise Failed, _("Can't install %s: it can't coexist "
|
||
|
"with %s") % (pkg, namepkg)
|
||
|
- self._remove(namepkg, changeset, locked, pending, depth)
|
||
|
+ else:
|
||
|
+ if namepkg in locked:
|
||
|
+ raise Failed, _("Can't install %s: it can't coexist "
|
||
|
+ "with %s") % (pkg, namepkg)
|
||
|
+ self._remove(namepkg, changeset, locked, pending, depth)
|
||
|
|
||
|
# Install packages required by this one.
|
||
|
for req in pkg.requires + pkg.recommends:
|
||
|
@@ -1176,6 +1194,8 @@ class Transaction(object):
|
||
|
|
||
|
self._policy.runStarting()
|
||
|
|
||
|
+ attempt = sysconf.has("attempt-install", soft=True)
|
||
|
+
|
||
|
try:
|
||
|
changeset = self._changeset.copy()
|
||
|
isinst = changeset.installed
|
||
|
@@ -1190,7 +1210,11 @@ class Transaction(object):
|
||
|
locked[pkg] = (LOCKED_KEEP, None)
|
||
|
elif op is INSTALL:
|
||
|
if not isinst(pkg) and pkg in locked:
|
||
|
- raise Failed, _("Can't install %s: it's locked") % pkg
|
||
|
+ if attempt:
|
||
|
+ iface.warning(_("Can't install %s: it's locked") % pkg)
|
||
|
+ del changeset[pkg]
|
||
|
+ else:
|
||
|
+ raise Failed, _("Can't install %s: it's locked") % pkg
|
||
|
changeset.set(pkg, INSTALL)
|
||
|
locked[pkg] = (LOCKED_INSTALL, None)
|
||
|
elif op is REMOVE:
|
||
|
@@ -1216,9 +1240,18 @@ class Transaction(object):
|
||
|
else:
|
||
|
op = REMOVE
|
||
|
if op is INSTALL or op is REINSTALL:
|
||
|
- self._install(pkg, changeset, locked, pending)
|
||
|
- if pkg in changeset:
|
||
|
- changeset.setRequested(pkg, True)
|
||
|
+ try:
|
||
|
+ self._install(pkg, changeset, locked, pending)
|
||
|
+ if pkg in changeset:
|
||
|
+ changeset.setRequested(pkg, True)
|
||
|
+ except Failed, e:
|
||
|
+ if attempt:
|
||
|
+ iface.warning(_("Can't install %s: %s") % (pkg, e))
|
||
|
+ if pkg in changeset:
|
||
|
+ del changeset[pkg]
|
||
|
+ continue
|
||
|
+ else:
|
||
|
+ raise Failed, e
|
||
|
elif op is REMOVE:
|
||
|
self._remove(pkg, changeset, locked, pending)
|
||
|
elif op is UPGRADE:
|
||
|
Index: smart-1.4.1/smart/backends/rpm/pm.py
|
||
|
===================================================================
|
||
|
--- smart-1.4.1.orig/smart/backends/rpm/pm.py
|
||
|
+++ smart-1.4.1/smart/backends/rpm/pm.py
|
||
|
@@ -243,15 +253,48 @@ class RPMPackageManager(PackageManager):
|
||
|
cb = RPMCallback(prog, upgradednames)
|
||
|
cb.grabOutput(True)
|
||
|
probs = None
|
||
|
+ retry = 0
|
||
|
try:
|
||
|
probs = ts.run(cb, None)
|
||
|
finally:
|
||
|
del getTS.ts
|
||
|
cb.grabOutput(False)
|
||
|
+ if probs and sysconf.has("attempt-install", soft=True):
|
||
|
+ def remove_conflict(pkgNEVR):
|
||
|
+ for key in changeset.keys():
|
||
|
+ if pkgNEVR == str(key):
|
||
|
+ del changeset[key]
|
||
|
+ del pkgpaths[key]
|
||
|
+ iface.warning("Removing %s due to file %s conflicting with %s" % (pkgNEVR, fname, altNEVR))
|
||
|
+ break
|
||
|
+
|
||
|
+ retry = 1
|
||
|
+ for prob in probs:
|
||
|
+ if prob[1][0] == rpm.RPMPROB_NEW_FILE_CONFLICT:
|
||
|
+ msg = prob[0].split()
|
||
|
+ fname = msg[1]
|
||
|
+ pkgNEVR = msg[7]
|
||
|
+ altNEVR = msg[9]
|
||
|
+ pkgNEVR = pkgNEVR.rsplit('.', 1)[0] + '@' + pkgNEVR.rsplit('.', 1)[1]
|
||
|
+ altNEVR = altNEVR.rsplit('.', 1)[0] + '@' + altNEVR.rsplit('.', 1)[1]
|
||
|
+ remove_conflict(pkgNEVR)
|
||
|
+ elif prob[1][0] == rpm.RPMPROB_FILE_CONFLICT:
|
||
|
+ msg = prob[0].split()
|
||
|
+ fname = msg[1]
|
||
|
+ pkgNEVR = msg[5]
|
||
|
+ altNEVR = msg[11]
|
||
|
+ pkgNEVR = pkgNEVR.rsplit('.', 1)[0] + '@' + pkgNEVR.rsplit('.', 1)[1]
|
||
|
+ altNEVR = altNEVR.rsplit('.', 1)[0] + '@' + altNEVR.rsplit('.', 1)[1]
|
||
|
+ remove_conflict(pkgNEVR)
|
||
|
+ else:
|
||
|
+ retry = 0
|
||
|
+
|
||
|
prog.setDone()
|
||
|
- if probs:
|
||
|
+ if probs and (not retry):
|
||
|
raise Error, "\n".join([x[0] for x in probs])
|
||
|
prog.stop()
|
||
|
+ if retry and len(changeset):
|
||
|
+ self.commit(changeset, pkgpaths)
|
||
|
|
||
|
class RPMCallback:
|
||
|
def __init__(self, prog, upgradednames):
|