diff --git a/client/solo/commands/_manage.py b/client/solo/commands/_manage.py index aa61ca4eb..199daaa92 100644 --- a/client/solo/commands/_manage.py +++ b/client/solo/commands/_manage.py @@ -20,7 +20,7 @@ from entropy.const import const_convert_to_unicode, etpConst, \ const_debug_write, const_mkstemp from entropy.i18n import _, ngettext from entropy.output import darkgreen, blue, purple, teal, brown, bold, \ - darkred, readtext, is_interactive + darkred, green, readtext, is_interactive from entropy.exceptions import EntropyPackageException, \ DependenciesCollision, DependenciesNotFound from entropy.services.client import WebService @@ -498,7 +498,7 @@ class SoloManage(SoloCommand): return None, None except DependenciesCollision as exc: - col_deps = exc.value + col_deps, pkg_revdeps = exc.value entropy_client.output( "%s:" % (blue(_("Conflicting packages were pulled in")),), @@ -507,7 +507,8 @@ class SoloManage(SoloCommand): entropy_client.output("", level="warning") for pkg_matches in col_deps: - for pkg_id, pkg_repo in pkg_matches: + for pkg_match in pkg_matches: + pkg_id, pkg_repo = pkg_match repo = entropy_client.open_repository(pkg_repo) entropy_client.output( "%s%s%s" % ( @@ -516,6 +517,33 @@ class SoloManage(SoloCommand): purple(pkg_repo),), header=brown(" # "), level="warning") + + if not pkg_revdeps[pkg_match]: + entropy_client.output( + "(%s: %s)" % ( + green(_("required by")), + # Conflicting dependencies could have been + # specified by user, in which case they were + # not pulled in by anything. + teal(_("(no reverse dependencies)")),), + header=blue(" "), + level="info") + continue + + for pkg_revdep in pkg_revdeps[pkg_match]: + pkg_revdep_id, pkg_revdep_repo = pkg_revdep + revdep_repo = \ + entropy_client.open_repository(pkg_revdep_repo) + + entropy_client.output( + "(%s: %s%s%s)" % ( + green(_("required by")), + teal(revdep_repo.retrieveAtom(pkg_revdep_id)), + darkred(etpConst['entropyrepoprefix']), + purple(pkg_revdep_repo),), + header=blue(" "), + level="info") + entropy_client.output("", level="warning") entropy_client.output( diff --git a/lib/entropy/client/interfaces/dep.py b/lib/entropy/client/interfaces/dep.py index 71f792906..52bc611c1 100644 --- a/lib/entropy/client/interfaces/dep.py +++ b/lib/entropy/client/interfaces/dep.py @@ -2287,6 +2287,7 @@ class CalculatorsMixin: # now check and report dependencies with colliding scope and in case, # raise DependenciesCollision, containing information about collisions + # and what requires the packages involved _dup_deps_collisions = {} for _level, _deps in deptree.items(): for pkg_id, pkg_repo in _deps: @@ -2295,9 +2296,18 @@ class CalculatorsMixin: ks_set.add((pkg_id, pkg_repo)) _colliding_deps = [x for x in _dup_deps_collisions.values() if \ len(x) > 1] + if _colliding_deps: + _pkg_revdeps = {} + for _pkg_matches in _colliding_deps: + for _pkg_match in _pkg_matches: + _pkg_node = graph.get_node(_pkg_match) + _pkg_revdeps[_pkg_match] = [x.origin().item() for \ + x in _pkg_node.arches() if \ + not _pkg_node.is_arch_outgoing(x)] + graph.destroy() - raise DependenciesCollision(_colliding_deps) + raise DependenciesCollision((_colliding_deps, _pkg_revdeps)) # now use the ASAP herustic to anticipate post-dependencies # as much as possible @@ -3806,8 +3816,10 @@ class CalculatorsMixin: @raise DependenciesCollision: packages pulled in conflicting depedencies perhaps sharing the same key and slot (but different version). In this case, user should mask one or the other by hand. - The value encapsulated .value object attribute contains the list of - colliding package (list of lists). + The value encapsulated .value object attribute contains tuple of two + elements: the list of colliding package (list of lists), and a + dictionary with information about each package's (first level) reverse + dependencies @raise DependenciesNotFound: one or more dependencies required are not found. The encapsulated .value object attribute contains a list of not found dependencies. diff --git a/lib/entropy/exceptions.py b/lib/entropy/exceptions.py index a376c1337..ca5ce2d4c 100644 --- a/lib/entropy/exceptions.py +++ b/lib/entropy/exceptions.py @@ -54,7 +54,9 @@ class DependenciesCollision(EntropyException): """ During dependencies calculation, dependencies were pulled in in the same "scope" (package key + package slot), - list of lists (set) of colliding dependencies are in the .value attribute + two-element tuple of: + list of lists (set) of colliding dependencies are in the .value attribute, + dictionary mapping dependency to its (first level) reverse dependency """ class DependenciesNotRemovable(EntropyException): diff --git a/rigo/RigoDaemon/app.py b/rigo/RigoDaemon/app.py index 1bbe20e30..dcd249f40 100755 --- a/rigo/RigoDaemon/app.py +++ b/rigo/RigoDaemon/app.py @@ -1914,7 +1914,7 @@ class RigoDaemonService(dbus.service.Object): except DependenciesCollision as dcol: write_output( "_process_upgrade_merge_action, DependenciesCollision: " - "%s" % (dcol,)) + "%s" % (dcol[0],)) outcome = \ AppTransactionOutcome.DEPENDENCIES_COLLISION_ERROR return outcome @@ -2241,7 +2241,7 @@ class RigoDaemonService(dbus.service.Object): except DependenciesCollision as dcol: write_output( "_process_install_action, DependenciesCollision: " - "%s" % (dcol,)) + "%s" % (dcol[0],)) # this should never happen since client executes this # before us outcome = \