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.

Upstream-Status: Pending

Signed-off-by: Mark Hatle <mark.hatle@windriver.com>
Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>

For complementary and 'attemptonly' package processing, we should
make sure the warn rather than error reported.
Signed-off-by: Hongxu Jia <hongxu.jia@windriver.com>
---
 smart.py                  |  5 +++-
 smart/commands/install.py |  5 ++++
 smart/transaction.py      | 65 +++++++++++++++++++++++++++++++++++------------
 3 files changed, 58 insertions(+), 17 deletions(-)

diff --git a/smart.py b/smart.py
index c5c7a02..7e7fd34 100755
--- a/smart.py
+++ b/smart.py
@@ -179,7 +179,10 @@ def main(argv):
         if opts and opts.log_level == "debug":
             import traceback
             traceback.print_exc()
-        if iface.object:
+        if iface.object and sysconf.has("attempt-install", soft=True):
+            iface.warning(unicode(e))
+            exitcode = 0
+        elif iface.object:
             iface.error(unicode(e))
         else:
             sys.stderr.write(_("error: %s\n") % e)
diff --git a/smart/commands/install.py b/smart/commands/install.py
index 590222c..6ef9682 100644
--- a/smart/commands/install.py
+++ b/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)
 
diff --git a/smart/transaction.py b/smart/transaction.py
index 5730a42..e3e61c6 100644
--- a/smart/transaction.py
+++ b/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:
-- 
1.9.1

