From 84b0b92c4e7bb884454b574e566689150d588c1e Mon Sep 17 00:00:00 2001 From: Fabio Erculiani Date: Tue, 5 Mar 2013 13:20:28 +0000 Subject: [PATCH] [entropy.client.dep] rewrite simple or dependencies resolution Selected matches was not respected by the simple or dependencies resolution code. This commit addresses the problem and fixes the virtual/glu dependendency handling. --- lib/entropy/client/interfaces/dep.py | 231 ++++++++++++++++++++++----- 1 file changed, 187 insertions(+), 44 deletions(-) diff --git a/lib/entropy/client/interfaces/dep.py b/lib/entropy/client/interfaces/dep.py index dfa8c55f5..d3e6a9c46 100644 --- a/lib/entropy/client/interfaces/dep.py +++ b/lib/entropy/client/interfaces/dep.py @@ -471,10 +471,181 @@ class CalculatorsMixin: return matches + def _resolve_or_dependencies(self, dependencies, selected_matches, + _selected_matches_cache = None): + """ + Resolve a simple or dependency like "foo;bar;baz?" by looking at the + currently installed packages and those that would be installed. + The outcome is the selected dependency, if possible. + + @param dependencies: ordered list of or dependencies, recursion not + supported. + @type dependencies: list + @param selected_matches: a list of package matches that + compose the dependency graph. + @type selected_matches: list + @return: the new dependency string + @rtype: tuple + """ + inst_repo = self._installed_repository + if _selected_matches_cache is None: + cache = {} + else: + cache = _selected_matches_cache + + def _generate_keyslot_cache(): + keyslot_map = {} + keyslot_set = set() + for package_id, repository_id in selected_matches: + repo = self.open_repository(repository_id) + keyslot = repo.retrieveKeySlot(package_id) + keyslot_set.add(keyslot) + + obj = keyslot_map.setdefault(keyslot, set()) + obj.add((package_id, repository_id)) + cache['map'] = keyslot_map + cache['set'] = keyslot_set + + selected = False + selected_matches_set = set(selected_matches) + for dep in dependencies: + + # determine if dependency has been explicitly selected + matches, _pkg_rc = self.atom_match( + dep, multi_match = True, multi_repo = True) + common = set(matches) & selected_matches_set + if common: + if const_debug_enabled(): + const_debug_write( + __name__, + "_resolve_simple_or_dependency, " + "or dependency candidate => %s, " + "has been explicitly selected. " + "Found the dependency though." % (dep,)) + dependency = dep + selected = True + break + + package_ids, _pkg_rc = inst_repo.atomMatch( + dep, multiMatch = True) + if not package_ids: + # no matches, skip this. + if const_debug_enabled(): + const_debug_write( + __name__, + "_resolve_simple_or_dependency, " + "or dependency candidate => %s, no " + "installed matches, skipping for now" % (dep,)) + continue + + if const_debug_enabled(): + const_debug_write( + __name__, + "_resolve_simple_or_dependency, " + "or dependency candidate => %s ?" % ( + dep,)) + + # generate cache now. + if not cache: + _generate_keyslot_cache() + + dep_keyslot_set = set() + for package_id in package_ids: + dep_keyslot_set.add( + inst_repo.retrieveKeySlot(package_id)) + common = cache['set'] & dep_keyslot_set + + if not common: + # there is nothing in common between the + # dependency and the selected matches. + # We found it ! + if const_debug_enabled(): + const_debug_write( + __name__, + "_resolve_simple_or_dependency, " + "or dependency candidate => %s, " + "no common keyslots between selected and this. " + "Found the dependency though." % (dep,)) + dependency = dep + selected = True + break + + if const_debug_enabled(): + const_debug_write( + __name__, + "_resolve_simple_or_dependency, " + "or dependency candidate => %s, " + "common slots with selected matches: %s " + "(selected matches: %s)" % ( + dep, common, selected_matches,)) + + if common: + common_pkg_matches = set() + for keyslot in common: + common_pkg_matches.update(cache['map'][keyslot]) + + # determining if the new packages are still matching + # the selected dependency in the or literal. + repo_matches, repo_rc = self.atom_match( + dep, multi_match = True, multi_repo = True) + common = set(repo_matches) & common_pkg_matches + + if const_debug_enabled(): + if common: + const_debug_write( + __name__, + "_resolve_simple_or_dependency, " + "or dependency candidate => %s, " + "common slots with selected matches: %s " + "(selected matches: %s)" % ( + dep, common, selected_matches,)) + else: + const_debug_write( + __name__, + "_resolve_simple_or_dependency, " + "or dependency candidate => %s, " + "installing %s would make the dependency " + "invalid." % (dep, common,)) + + if not common: + if const_debug_enabled(): + const_debug_write( + __name__, + "_resolve_simple_or_dependency, " + "or dependency candidate => %s, " + "no common packages found. Sorry." % ( + dep,)) + continue + + if const_debug_enabled(): + const_debug_write( + __name__, + "_resolve_simple_or_dependency, " + "or dependency, selected => %s, from: %s" % ( + dep, dependencies,)) + # found it, rewrite dependency and c_ids + dependency = dep + selected = True + break + + if not selected: + # then pick the first, which is considered the default + # choice. + dependency = dependencies[0] + if const_debug_enabled(): + const_debug_write( + __name__, + "_resolve_simple_or_dependency, " + "or dependency candidate => %s, will " + "pick this (the default one)" % (dependency,)) + + return dependency + DISABLE_SLOT_INTERSECTION = os.getenv("ETP_DISABLE_SLOT_INTERSECTION") def _get_unsatisfied_dependencies(self, dependencies, deep_deps = False, - relaxed_deps = False, depcache = None, match_repo = None): + relaxed_deps = False, depcache = None, + match_repo = None): inst_repo = self._installed_repository cl_settings = self._settings[self.sys_settings_client_plugin_id] @@ -484,7 +655,7 @@ class CalculatorsMixin: if self.xcache: c_data = sorted(dependencies) client_checksum = inst_repo.checksum() - c_hash = "%s|%s|%s|%s|%s|%s" % (c_data, deep_deps, + c_hash = "%s|%s|%s|%s|%s|%s|v2" % (c_data, deep_deps, client_checksum, relaxed_deps, ignore_spm_downgrades, match_repo) c_hash = "%s_%s" % ( @@ -629,28 +800,6 @@ class CalculatorsMixin: push_to_cache(dependency, True) continue - if dependency.endswith(etpConst['entropyordepquestion']): - # need to rewrite c_ids and dependency to only focus on - # the installed dep. - deps = dependency[:-1].split(etpConst['entropyordepsep']) - for dep in deps: - or_c_ids, or_c_rc = inst_repo.atomMatch( - dep, multiMatch = True) - if or_c_rc != 0: - continue - common_c_ids = c_ids & or_c_ids - if common_c_ids: - if const_debug_enabled(): - const_debug_write( - __name__, - "_get_unsatisfied_dependencies, " - "or dependency, selected => %s, from: %s" % ( - dep, deps,)) - # found it, rewrite dependency and c_ids - dependency = dep - c_ids = or_c_ids - break - # support for app-foo/foo-123~-1 # -1 revision means, always pull the latest do_rev_deep = False @@ -1015,7 +1164,7 @@ class CalculatorsMixin: conflicts.add(c_idpackage) def __generate_dependency_tree_resolve_conditional(self, unsatisfied_deps, - selected_matches): + selected_matches, selected_matches_cache): # expand list of package dependencies evaluating conditionals unsatisfied_deps = entropy.dep.expand_dependencies(unsatisfied_deps, @@ -1025,20 +1174,10 @@ class CalculatorsMixin: def _simple_or_dep_map(dependency): # simple or dependency format support. if dependency.endswith(etpConst['entropyordepquestion']): - # or dependency! deps = dependency[:-1].split(etpConst['entropyordepsep']) - for dep in deps: - matches, rc = self.atom_match(dep, multi_match = True, - multi_repo = True) - if rc == 0: - difference = set(matches) - set(selected_matches) - if len(difference) != len(matches): - # ok, there is something in the selected data - if const_debug_enabled(): - const_debug_write(__name__, - "__generate_dependency_tree_resolve_conditional" - " replaced %s with %s" % (dependency, dep,)) - return dep + return self._resolve_or_dependencies( + deps, selected_matches, + _selected_matches_cache=selected_matches_cache) return dependency return set(map(_simple_or_dep_map, unsatisfied_deps)) @@ -1048,7 +1187,7 @@ class CalculatorsMixin: def __generate_dependency_tree_analyze_deplist(self, pkg_match, repo_db, stack, graph, deps_not_found, conflicts, unsat_cache, relaxed_deps, build_deps, deep_deps, empty_deps, recursive, selected_matches, - elements_cache): + elements_cache, selected_matches_cache): pkg_id, repo_id = pkg_match # exclude build dependencies @@ -1070,7 +1209,7 @@ class CalculatorsMixin: "%s, %s, current dependency list => %s" % ( pkg_match, atom, myundeps,)) myundeps = self.__generate_dependency_tree_resolve_conditional( - myundeps, selected_matches) + myundeps, selected_matches, selected_matches_cache) if const_debug_enabled(): const_debug_write(__name__, "__generate_dependency_tree_analyze_deplist conditionals, " @@ -1151,6 +1290,8 @@ class CalculatorsMixin: # nvidia-drivers pulls in nvidia-userspace which has nvidia-drivers # listed as post-dependency post_deps = list(filter(_post_deps_filter, post_deps)) + post_deps = self.__generate_dependency_tree_resolve_conditional( + post_deps, selected_matches, selected_matches_cache) post_deps = self._get_unsatisfied_dependencies(post_deps, deep_deps = deep_deps, relaxed_deps = relaxed_deps, depcache = unsat_cache) @@ -1191,7 +1332,7 @@ class CalculatorsMixin: empty_deps = False, relaxed_deps = False, build_deps = False, only_deps = False, deep_deps = False, unsatisfied_deps_cache = None, elements_cache = None, post_deps_cache = None, recursive = True, - selected_matches = None): + selected_matches = None, selected_matches_cache = None): pkg_id, pkg_repo = matched_atom if (pkg_id == -1) or (pkg_repo == 1): @@ -1299,7 +1440,7 @@ class CalculatorsMixin: pkg_match, repo_db, stack, graph, deps_not_found, conflicts, unsatisfied_deps_cache, relaxed_deps, build_deps, deep_deps, empty_deps, recursive, - selected_matches, elements_cache) + selected_matches, elements_cache, selected_matches_cache) if post_dep_matches: obj = post_deps_cache.setdefault(pkg_match, set()) @@ -1936,7 +2077,7 @@ class CalculatorsMixin: only_deps = False, quiet = False, recursive = True): sha = hashlib.sha1() - c_hex = "%s|%s|%s|%s|%s|%s|%s|%s|%s|v2" % ( + c_hex = "%s|%s|%s|%s|%s|%s|%s|%s|%s|v3" % ( repr(sorted(package_matches)), empty_deps, deep_deps, @@ -1982,6 +2123,7 @@ class CalculatorsMixin: sort_dep_text = _("Sorting dependencies") unsat_deps_cache = {} elements_cache = set() + selected_matches_cache = {} post_deps_cache = {} matchfilter = set() for matched_atom in package_matches: @@ -2016,7 +2158,8 @@ class CalculatorsMixin: elements_cache = elements_cache, unsatisfied_deps_cache = unsat_deps_cache, post_deps_cache = post_deps_cache, - recursive = recursive, selected_matches = package_matches + recursive = recursive, selected_matches = package_matches, + selected_matches_cache = selected_matches_cache ) except DependenciesNotFound as err: deps_not_found |= err.value