def check_uses(ruse, uselist, sw, package): act = [] # check_required_use doesn't like -flag entries for pos in range(len(uselist)): if ((2**pos) & sw): act.append(uselist[pos]) if bool(check_required_use(ruse, " ".join(act), all_valid_flags)): return True else: print(" " + package.packageString() + ": ignoring invalid USE flag combination", act) return False
def check_uses(ruse, uselist, sw, package): act = [] # check_required_use doesn't like -flag entries for pos in range(len(uselist)): if ((2**pos) & sw): act.append(uselist[pos]) if bool(check_required_use(ruse, " ".join(act), all_valid_flags)): return True else: print( " " + package.packageString() + ": ignoring invalid USE flag combination", act) return False
def testCheckRequiredUseFilterSatisfied(self): """ Test filtering of satisfied parts of REQUIRED_USE, in order to reduce noise for bug #353234. """ test_cases = ( ("bindist? ( !amr !faac !win32codecs ) cdio? ( !cdparanoia !cddb ) dvdnav? ( dvd )", ("cdio", "cdparanoia"), "cdio? ( !cdparanoia )"), ("|| ( !amr !faac !win32codecs ) cdio? ( !cdparanoia !cddb ) ^^ ( foo bar )", ["cdio", "cdparanoia", "foo"], "cdio? ( !cdparanoia )"), ("^^ ( || ( a b ) c )", ("a", "b", "c"), "^^ ( || ( a b ) c )"), ("^^ ( || ( ( a b ) ) ( c ) )", ("a", "b", "c"), "^^ ( ( a b ) c )"), ("a? ( ( c e ) ( b d ) )", ("a", "c", "e"), "a? ( b d )"), ("a? ( ( c e ) ( b d ) )", ("a", "b", "c", "e"), "a? ( d )"), ("a? ( ( c e ) ( c e b c d e c ) )", ("a", "c", "e"), "a? ( b d )"), ("^^ ( || ( a b ) ^^ ( b c ) )", ("a", "b"), "^^ ( || ( a b ) ^^ ( b c ) )"), ("^^ ( || ( a b ) ^^ ( b c ) )", ["a", "c"], "^^ ( || ( a b ) ^^ ( b c ) )"), ("^^ ( || ( a b ) ^^ ( b c ) )", ["b", "c"], ""), ("^^ ( || ( a b ) ^^ ( b c ) )", ["a", "b", "c"], ""), ("^^ ( ( a b c ) ( b c d ) )", ["a", "b", "c"], ""), ("^^ ( ( a b c ) ( b c d ) )", ["a", "b", "c", "d"], "^^ ( ( a b c ) ( b c d ) )"), ("^^ ( ( a b c ) ( b c !d ) )", ["a", "b", "c"], "^^ ( ( a b c ) ( b c !d ) )"), ("^^ ( ( a b c ) ( b c !d ) )", ["a", "b", "c", "d"], ""), ("( ( ( a ) ) ( ( ( b c ) ) ) )", [""], "a b c"), ("|| ( ( ( ( a ) ) ( ( ( b c ) ) ) ) )", [""], "a b c"), ("|| ( ( a ( ( ) ( ) ) ( ( ) ) ( b ( ) c ) ) )", [""], "a b c"), ("|| ( ( a b c ) ) || ( ( d e f ) )", [""], "a b c d e f"), ) for required_use, use, expected in test_cases: result = check_required_use(required_use, use, lambda k: True).tounicode() self.assertEqual(result, expected, "REQUIRED_USE = '%s', USE = '%s', '%s' != '%s'" % \ (required_use, " ".join(use), result, expected))
def _check_solution(self, config, all_involved_flags, all_conflict_atoms_by_slotatom): """ Given a configuartion and all involved flags, all possible settings for the involved flags are checked if they solve the slot conflict. """ _pkg_use_enabled = self.depgraph._pkg_use_enabled if self.debug: #The code is a bit verbose, because the states might not #be a string, but a _value_helper. msg = "Solution candidate: " msg += "[" first = True for involved_flags in all_involved_flags: if first: first = False else: msg += ", " msg += "{" inner_first = True for flag, state in involved_flags.items(): if inner_first: inner_first = False else: msg += ", " msg += flag + ": %s" % (state,) msg += "}" msg += "]\n" writemsg(msg, noiselevel=-1) required_changes = {} for id, pkg in enumerate(config): if not pkg.installed: #We can't change the USE of installed packages. for flag in all_involved_flags[id]: if not pkg.iuse.is_valid_flag(flag): continue state = all_involved_flags[id][flag] self._force_flag_for_package(required_changes, pkg, flag, state) #Go through all (parent, atom) pairs for the current slot conflict. for ppkg, atom in all_conflict_atoms_by_slotatom[id]: use = atom.unevaluated_atom.use if not use: #No need to force something for an atom without USE conditionals. #These atoms are already satisfied. continue for flag in all_involved_flags[id]: state = all_involved_flags[id][flag] if flag not in use.required or not use.conditional: continue if flag in use.conditional.enabled: #[flag?] if state == "enabled": #no need to change anything, the atom won't #force -flag on pkg pass elif state == "disabled": #if flag is enabled we get [flag] -> it must be disabled self._force_flag_for_package(required_changes, ppkg, flag, "disabled") elif flag in use.conditional.disabled: #[!flag?] if state == "enabled": #if flag is enabled we get [-flag] -> it must be disabled self._force_flag_for_package(required_changes, ppkg, flag, "disabled") elif state == "disabled": #no need to change anything, the atom won't #force +flag on pkg pass elif flag in use.conditional.equal: #[flag=] if state == "enabled": #if flag is disabled we get [-flag] -> it must be enabled self._force_flag_for_package(required_changes, ppkg, flag, "enabled") elif state == "disabled": #if flag is enabled we get [flag] -> it must be disabled self._force_flag_for_package(required_changes, ppkg, flag, "disabled") elif flag in use.conditional.not_equal: #[!flag=] if state == "enabled": #if flag is enabled we get [-flag] -> it must be disabled self._force_flag_for_package(required_changes, ppkg, flag, "disabled") elif state == "disabled": #if flag is disabled we get [flag] -> it must be enabled self._force_flag_for_package(required_changes, ppkg, flag, "enabled") is_valid_solution = True for pkg in required_changes: for state in required_changes[pkg].values(): if not state in ("enabled", "disabled"): is_valid_solution = False if not is_valid_solution: return None #Check if all atoms are satisfied after the changes are applied. for id, pkg in enumerate(config): new_use = _pkg_use_enabled(pkg) if pkg in required_changes: old_use = pkg.use.enabled new_use = set(new_use) for flag, state in required_changes[pkg].items(): if state == "enabled": new_use.add(flag) elif state == "disabled": new_use.discard(flag) if not new_use.symmetric_difference(old_use): #avoid copying the package in findAtomForPackage if possible new_use = old_use for ppkg, atom in all_conflict_atoms_by_slotatom[id]: if not hasattr(ppkg, "use"): #It's a SetArg or something like that. continue ppkg_new_use = set(_pkg_use_enabled(ppkg)) if ppkg in required_changes: for flag, state in required_changes[ppkg].items(): if state == "enabled": ppkg_new_use.add(flag) elif state == "disabled": ppkg_new_use.discard(flag) new_atom = atom.unevaluated_atom.evaluate_conditionals(ppkg_new_use) i = InternalPackageSet(initial_atoms=(new_atom,)) if not i.findAtomForPackage(pkg, new_use): #We managed to create a new problem with our changes. is_valid_solution = False if self.debug: writemsg(("new conflict introduced: %s" " does not match %s from %s\n") % (pkg, new_atom, ppkg), noiselevel=-1) break if not is_valid_solution: break #Make sure the changes don't violate REQUIRED_USE for pkg in required_changes: required_use = pkg._metadata.get("REQUIRED_USE") if not required_use: continue use = set(_pkg_use_enabled(pkg)) for flag, state in required_changes[pkg].items(): if state == "enabled": use.add(flag) else: use.discard(flag) if not check_required_use(required_use, use, pkg.iuse.is_valid_flag): is_valid_solution = False break if is_valid_solution and required_changes: return required_changes else: return None
def _find_suggestions(self): if not self.shortest_cycle: return None, None suggestions = [] final_solutions = {} for pos, pkg in enumerate(self.shortest_cycle): parent = self.shortest_cycle[pos-1] priorities = self.graph.nodes[parent][0][pkg] parent_atoms = self.all_parent_atoms.get(pkg) if priorities[-1].buildtime: dep = parent.metadata["DEPEND"] elif priorities[-1].runtime: dep = parent.metadata["RDEPEND"] for ppkg, atom in parent_atoms: if ppkg == parent: changed_parent = ppkg parent_atom = atom.unevaluated_atom break try: affecting_use = extract_affecting_use(dep, parent_atom, eapi=parent.metadata["EAPI"]) except InvalidDependString: if not parent.installed: raise affecting_use = set() # Make sure we don't want to change a flag that is # a) in use.mask or use.force # b) changed by autounmask usemask, useforce = self._get_use_mask_and_force(parent) autounmask_changes = self._get_autounmask_changes(parent) untouchable_flags = frozenset(chain(usemask, useforce, autounmask_changes)) affecting_use.difference_update(untouchable_flags) #If any of the flags we're going to touch is in REQUIRED_USE, add all #other flags in REQUIRED_USE to affecting_use, to not lose any solution. required_use_flags = get_required_use_flags( parent.metadata.get("REQUIRED_USE", "")) if affecting_use.intersection(required_use_flags): # TODO: Find out exactly which REQUIRED_USE flags are # entangled with affecting_use. We have to limit the # number of flags since the number of loops is # exponentially related (see bug #374397). total_flags = set() total_flags.update(affecting_use, required_use_flags) total_flags.difference_update(untouchable_flags) if len(total_flags) <= 10: affecting_use = total_flags affecting_use = tuple(affecting_use) if not affecting_use: continue #We iterate over all possible settings of these use flags and gather #a set of possible changes #TODO: Use the information encoded in REQUIRED_USE solutions = set() for use_state in product(("disabled", "enabled"), repeat=len(affecting_use)): current_use = set(self.depgraph._pkg_use_enabled(parent)) for flag, state in zip(affecting_use, use_state): if state == "enabled": current_use.add(flag) else: current_use.discard(flag) try: reduced_dep = use_reduce(dep, uselist=current_use, flat=True) except InvalidDependString: if not parent.installed: raise reduced_dep = None if reduced_dep is not None and \ parent_atom not in reduced_dep: #We found an assignment that removes the atom from 'dep'. #Make sure it doesn't conflict with REQUIRED_USE. required_use = parent.metadata.get("REQUIRED_USE", "") if check_required_use(required_use, current_use, parent.iuse.is_valid_flag): use = self.depgraph._pkg_use_enabled(parent) solution = set() for flag, state in zip(affecting_use, use_state): if state == "enabled" and \ flag not in use: solution.add((flag, True)) elif state == "disabled" and \ flag in use: solution.add((flag, False)) solutions.add(frozenset(solution)) for solution in solutions: ignore_solution = False for other_solution in solutions: if solution is other_solution: continue if solution.issuperset(other_solution): ignore_solution = True if ignore_solution: continue #Check if a USE change conflicts with use requirements of the parents. #If a requiremnet is hard, ignore the suggestion. #If the requirment is conditional, warn the user that other changes might be needed. followup_change = False parent_parent_atoms = self.depgraph._dynamic_config._parent_atoms.get(changed_parent) for ppkg, atom in parent_parent_atoms: atom = atom.unevaluated_atom if not atom.use: continue for flag, state in solution: if flag in atom.use.enabled or flag in atom.use.disabled: ignore_solution = True break elif atom.use.conditional: for flags in atom.use.conditional.values(): if flag in flags: followup_change = True break if ignore_solution: break if ignore_solution: continue changes = [] for flag, state in solution: if state: changes.append(colorize("red", "+"+flag)) else: changes.append(colorize("blue", "-"+flag)) msg = "- %s (Change USE: %s)\n" \ % (parent.cpv, " ".join(changes)) if followup_change: msg += " (This change might require USE changes on parent packages.)" suggestions.append(msg) final_solutions.setdefault(pkg, set()).add(solution) return final_solutions, suggestions
def _find_suggestions(self): if not self.shortest_cycle: return None, None suggestions = [] final_solutions = {} for pos, pkg in enumerate(self.shortest_cycle): parent = self.shortest_cycle[pos - 1] priorities = self.graph.nodes[parent][0][pkg] parent_atoms = self.all_parent_atoms.get(pkg) if priorities[-1].buildtime: dep = parent.metadata["DEPEND"] elif priorities[-1].runtime: dep = parent.metadata["RDEPEND"] for ppkg, atom in parent_atoms: if ppkg == parent: changed_parent = ppkg parent_atom = atom.unevaluated_atom break try: affecting_use = extract_affecting_use( dep, parent_atom, eapi=parent.metadata["EAPI"]) except InvalidDependString: if not parent.installed: raise affecting_use = set() # Make sure we don't want to change a flag that is # a) in use.mask or use.force # b) changed by autounmask usemask, useforce = self._get_use_mask_and_force(parent) autounmask_changes = self._get_autounmask_changes(parent) untouchable_flags = frozenset( chain(usemask, useforce, autounmask_changes)) affecting_use.difference_update(untouchable_flags) #If any of the flags we're going to touch is in REQUIRED_USE, add all #other flags in REQUIRED_USE to affecting_use, to not lose any solution. required_use_flags = get_required_use_flags( parent.metadata.get("REQUIRED_USE", "")) if affecting_use.intersection(required_use_flags): # TODO: Find out exactly which REQUIRED_USE flags are # entangled with affecting_use. We have to limit the # number of flags since the number of loops is # exponentially related (see bug #374397). total_flags = set() total_flags.update(affecting_use, required_use_flags) total_flags.difference_update(untouchable_flags) if len(total_flags) <= 10: affecting_use = total_flags affecting_use = tuple(affecting_use) if not affecting_use: continue #We iterate over all possible settings of these use flags and gather #a set of possible changes #TODO: Use the information encoded in REQUIRED_USE solutions = set() for use_state in product(("disabled", "enabled"), repeat=len(affecting_use)): current_use = set(self.depgraph._pkg_use_enabled(parent)) for flag, state in zip(affecting_use, use_state): if state == "enabled": current_use.add(flag) else: current_use.discard(flag) try: reduced_dep = use_reduce(dep, uselist=current_use, flat=True) except InvalidDependString: if not parent.installed: raise reduced_dep = None if reduced_dep is not None and \ parent_atom not in reduced_dep: #We found an assignment that removes the atom from 'dep'. #Make sure it doesn't conflict with REQUIRED_USE. required_use = parent.metadata.get("REQUIRED_USE", "") if check_required_use(required_use, current_use, parent.iuse.is_valid_flag): use = self.depgraph._pkg_use_enabled(parent) solution = set() for flag, state in zip(affecting_use, use_state): if state == "enabled" and \ flag not in use: solution.add((flag, True)) elif state == "disabled" and \ flag in use: solution.add((flag, False)) solutions.add(frozenset(solution)) for solution in solutions: ignore_solution = False for other_solution in solutions: if solution is other_solution: continue if solution.issuperset(other_solution): ignore_solution = True if ignore_solution: continue #Check if a USE change conflicts with use requirements of the parents. #If a requiremnet is hard, ignore the suggestion. #If the requirment is conditional, warn the user that other changes might be needed. followup_change = False parent_parent_atoms = self.depgraph._dynamic_config._parent_atoms.get( changed_parent) for ppkg, atom in parent_parent_atoms: atom = atom.unevaluated_atom if not atom.use: continue for flag, state in solution: if flag in atom.use.enabled or flag in atom.use.disabled: ignore_solution = True break elif atom.use.conditional: for flags in atom.use.conditional.values(): if flag in flags: followup_change = True break if ignore_solution: break if ignore_solution: continue changes = [] for flag, state in solution: if state: changes.append(colorize("red", "+" + flag)) else: changes.append(colorize("blue", "-" + flag)) msg = "- %s (Change USE: %s)\n" \ % (parent.cpv, " ".join(changes)) if followup_change: msg += " (This change might require USE changes on parent packages.)" suggestions.append(msg) final_solutions.setdefault(pkg, set()).add(solution) return final_solutions, suggestions
def testCheckRequiredUse(self): test_cases = ( ( "|| ( a b )", [], ["a", "b"], False), ( "|| ( a b )", ["a"], ["a", "b"], True), ( "|| ( a b )", ["b"], ["a", "b"], True), ( "|| ( a b )", ["a", "b"], ["a", "b"], True), ( "^^ ( a b )", [], ["a", "b"], False), ( "^^ ( a b )", ["a"], ["a", "b"], True), ( "^^ ( a b )", ["b"], ["a", "b"], True), ( "^^ ( a b )", ["a", "b"], ["a", "b"], False), ( "^^ ( || ( a b ) c )", [], ["a", "b", "c"], False), ( "^^ ( || ( a b ) c )", ["a"], ["a", "b", "c"], True), ( "^^ ( || ( ( a b ) ) ( c ) )", [], ["a", "b", "c"], False), ( "( ^^ ( ( || ( ( a ) ( b ) ) ) ( ( c ) ) ) )", ["a"], ["a", "b", "c"], True), ( "a || ( b c )", ["a"], ["a", "b", "c"], False), ( "|| ( b c ) a", ["a"], ["a", "b", "c"], False), ( "|| ( a b c )", ["a"], ["a", "b", "c"], True), ( "|| ( a b c )", ["b"], ["a", "b", "c"], True), ( "|| ( a b c )", ["c"], ["a", "b", "c"], True), ( "^^ ( a b c )", ["a"], ["a", "b", "c"], True), ( "^^ ( a b c )", ["b"], ["a", "b", "c"], True), ( "^^ ( a b c )", ["c"], ["a", "b", "c"], True), ( "^^ ( a b c )", ["a", "b"], ["a", "b", "c"], False), ( "^^ ( a b c )", ["b", "c"], ["a", "b", "c"], False), ( "^^ ( a b c )", ["a", "c"], ["a", "b", "c"], False), ( "^^ ( a b c )", ["a", "b", "c"], ["a", "b", "c"], False), ( "a? ( ^^ ( b c ) )", [], ["a", "b", "c"], True), ( "a? ( ^^ ( b c ) )", ["a"], ["a", "b", "c"], False), ( "a? ( ^^ ( b c ) )", ["b"], ["a", "b", "c"], True), ( "a? ( ^^ ( b c ) )", ["c"], ["a", "b", "c"], True), ( "a? ( ^^ ( b c ) )", ["a", "b"], ["a", "b", "c"], True), ( "a? ( ^^ ( b c ) )", ["a", "b", "c"], ["a", "b", "c"], False), ( "^^ ( a? ( !b ) !c? ( d ) )", [], ["a", "b", "c", "d"], False), ( "^^ ( a? ( !b ) !c? ( d ) )", ["a"], ["a", "b", "c", "d"], True), ( "^^ ( a? ( !b ) !c? ( d ) )", ["c"], ["a", "b", "c", "d"], True), ( "^^ ( a? ( !b ) !c? ( d ) )", ["a", "c"], ["a", "b", "c", "d"], True), ( "^^ ( a? ( !b ) !c? ( d ) )", ["a", "b", "c"], ["a", "b", "c", "d"], False), ( "^^ ( a? ( !b ) !c? ( d ) )", ["a", "b", "d"], ["a", "b", "c", "d"], True), ( "^^ ( a? ( !b ) !c? ( d ) )", ["a", "b", "d"], ["a", "b", "c", "d"], True), ( "^^ ( a? ( !b ) !c? ( d ) )", ["a", "d"], ["a", "b", "c", "d"], False), ( "|| ( ^^ ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"], False), ( "|| ( ^^ ( a b ) ^^ ( b c ) )", ["a"], ["a", "b", "c"], True), ( "|| ( ^^ ( a b ) ^^ ( b c ) )", ["b"], ["a", "b", "c"], True), ( "|| ( ^^ ( a b ) ^^ ( b c ) )", ["c"], ["a", "b", "c"], True), ( "|| ( ^^ ( a b ) ^^ ( b c ) )", ["a", "b"], ["a", "b", "c"], True), ( "|| ( ^^ ( a b ) ^^ ( b c ) )", ["a", "c"], ["a", "b", "c"], True), ( "|| ( ^^ ( a b ) ^^ ( b c ) )", ["b", "c"], ["a", "b", "c"], True), ( "|| ( ^^ ( a b ) ^^ ( b c ) )", ["a", "b", "c"], ["a", "b", "c"], False), ( "^^ ( || ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"], False), ( "^^ ( || ( a b ) ^^ ( b c ) )", ["a"], ["a", "b", "c"], True), ( "^^ ( || ( a b ) ^^ ( b c ) )", ["b"], ["a", "b", "c"], False), ( "^^ ( || ( a b ) ^^ ( b c ) )", ["c"], ["a", "b", "c"], True), ( "^^ ( || ( a b ) ^^ ( b c ) )", ["a", "b"], ["a", "b", "c"], False), ( "^^ ( || ( a b ) ^^ ( b c ) )", ["a", "c"], ["a", "b", "c"], False), ( "^^ ( || ( a b ) ^^ ( b c ) )", ["b", "c"], ["a", "b", "c"], True), ( "^^ ( || ( a b ) ^^ ( b c ) )", ["a", "b", "c"], ["a", "b", "c"], True), ( "|| ( ( a b ) c )", ["a", "b", "c"], ["a", "b", "c"], True), ( "|| ( ( a b ) c )", ["b", "c"], ["a", "b", "c"], True), ( "|| ( ( a b ) c )", ["a", "c"], ["a", "b", "c"], True), ( "|| ( ( a b ) c )", ["a", "b"], ["a", "b", "c"], True), ( "|| ( ( a b ) c )", ["a"], ["a", "b", "c"], False), ( "|| ( ( a b ) c )", ["b"], ["a", "b", "c"], False), ( "|| ( ( a b ) c )", ["c"], ["a", "b", "c"], True), ( "|| ( ( a b ) c )", [], ["a", "b", "c"], False), ( "^^ ( ( a b ) c )", ["a", "b", "c"], ["a", "b", "c"], False), ( "^^ ( ( a b ) c )", ["b", "c"], ["a", "b", "c"], True), ( "^^ ( ( a b ) c )", ["a", "c"], ["a", "b", "c"], True), ( "^^ ( ( a b ) c )", ["a", "b"], ["a", "b", "c"], True), ( "^^ ( ( a b ) c )", ["a"], ["a", "b", "c"], False), ( "^^ ( ( a b ) c )", ["b"], ["a", "b", "c"], False), ( "^^ ( ( a b ) c )", ["c"], ["a", "b", "c"], True), ( "^^ ( ( a b ) c )", [], ["a", "b", "c"], False), ) test_cases_xfail = ( ( "^^ ( || ( a b ) ^^ ( b c ) )", [], ["a", "b"]), ( "^^ ( || ( a b ) ^^ ( b c )", [], ["a", "b", "c"]), ( "^^( || ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"]), ( "^^ || ( a b ) ^^ ( b c )", [], ["a", "b", "c"]), ( "^^ ( ( || ) ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"]), ( "^^ ( || ( a b ) ) ^^ ( b c ) )", [], ["a", "b", "c"]), ) for required_use, use, iuse, expected in test_cases: self.assertEqual(check_required_use(required_use, use, iuse.__contains__), \ expected, required_use + ", USE = " + " ".join(use)) for required_use, use, iuse in test_cases_xfail: self.assertRaisesMsg(required_use + ", USE = " + " ".join(use), \ InvalidDependString, check_required_use, required_use, use, iuse.__contains__)
def testCheckRequiredUseFilterSatisfied(self): """ Test filtering of satisfied parts of REQUIRED_USE, in order to reduce noise for bug #353234. """ test_cases = ( ( "bindist? ( !amr !faac !win32codecs ) cdio? ( !cdparanoia !cddb ) dvdnav? ( dvd )", ("cdio", "cdparanoia"), "cdio? ( !cdparanoia )" ), ( "|| ( !amr !faac !win32codecs ) cdio? ( !cdparanoia !cddb ) ^^ ( foo bar )", ["cdio", "cdparanoia", "foo"], "cdio? ( !cdparanoia )" ), ( "^^ ( || ( a b ) c )", ("a", "b", "c"), "^^ ( || ( a b ) c )" ), ( "^^ ( || ( ( a b ) ) ( c ) )", ("a", "b", "c"), "^^ ( ( a b ) c )" ), ( "a? ( ( c e ) ( b d ) )", ("a", "c", "e"), "a? ( b d )" ), ( "a? ( ( c e ) ( b d ) )", ("a", "b", "c", "e"), "a? ( d )" ), ( "a? ( ( c e ) ( c e b c d e c ) )", ("a", "c", "e"), "a? ( b d )" ), ( "^^ ( || ( a b ) ^^ ( b c ) )", ("a", "b"), "^^ ( || ( a b ) ^^ ( b c ) )" ), ( "^^ ( || ( a b ) ^^ ( b c ) )", ["a", "c"], "^^ ( || ( a b ) ^^ ( b c ) )" ), ( "^^ ( || ( a b ) ^^ ( b c ) )", ["b", "c"], "" ), ( "^^ ( || ( a b ) ^^ ( b c ) )", ["a", "b", "c"], "" ), ( "^^ ( ( a b c ) ( b c d ) )", ["a", "b", "c"], "" ), ( "^^ ( ( a b c ) ( b c d ) )", ["a", "b", "c", "d"], "^^ ( ( a b c ) ( b c d ) )" ), ( "^^ ( ( a b c ) ( b c !d ) )", ["a", "b", "c"], "^^ ( ( a b c ) ( b c !d ) )" ), ( "^^ ( ( a b c ) ( b c !d ) )", ["a", "b", "c", "d"], "" ), ( "( ( ( a ) ) ( ( ( b c ) ) ) )", [""], "a b c" ), ( "|| ( ( ( ( a ) ) ( ( ( b c ) ) ) ) )", [""], "a b c" ), ( "|| ( ( a ( ( ) ( ) ) ( ( ) ) ( b ( ) c ) ) )", [""], "a b c" ), ( "|| ( ( a b c ) ) || ( ( d e f ) )", [""], "a b c d e f" ), ) for required_use, use, expected in test_cases: result = check_required_use(required_use, use, lambda k: True).tounicode() self.assertEqual(result, expected, "REQUIRED_USE = '%s', USE = '%s', '%s' != '%s'" % \ (required_use, " ".join(use), result, expected))
def _validate_deps(self): """ Validate deps. This does not trigger USE calculation since that is expensive for ebuilds and therefore we want to avoid doing it unnecessarily (like for masked packages). """ eapi = self.eapi dep_eapi = eapi dep_valid_flag = self.iuse.is_valid_flag if self.installed: # Ignore EAPI.incompatible and conditionals missing # from IUSE for installed packages since these issues # aren't relevant now (re-evaluate when new EAPIs are # deployed). dep_eapi = None dep_valid_flag = None validated_atoms = [] for k in self._dep_keys: v = self._metadata.get(k) if not v: continue try: atoms = use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag, token_class=Atom, flat=True) except InvalidDependString as e: self._metadata_exception(k, e) else: validated_atoms.extend(atoms) if not self.built: for atom in atoms: if not isinstance(atom, Atom): continue if atom.slot_operator_built: e = InvalidDependString( _("Improper context for slot-operator " "\"built\" atom syntax: %s") % (atom.unevaluated_atom,)) self._metadata_exception(k, e) self._validated_atoms = tuple(set(atom for atom in validated_atoms if isinstance(atom, Atom))) for k in self._use_conditional_misc_keys: v = self._metadata.get(k) if not v: continue try: use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag) except InvalidDependString as e: self._metadata_exception(k, e) k = 'REQUIRED_USE' v = self._metadata.get(k) if v and not self.built: if not _get_eapi_attrs(eapi).required_use: self._invalid_metadata('EAPI.incompatible', "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi) else: try: check_required_use(v, (), self.iuse.is_valid_flag, eapi=eapi) except InvalidDependString as e: self._invalid_metadata(k + ".syntax", "%s: %s" % (k, e)) k = 'SRC_URI' v = self._metadata.get(k) if v: try: use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True, is_valid_flag=self.iuse.is_valid_flag) except InvalidDependString as e: if not self.installed: self._metadata_exception(k, e) if self.built: k = 'PROVIDES' try: self._provides = frozenset( parse_soname_deps(self._metadata[k])) except InvalidData as e: self._invalid_metadata(k + ".syntax", "%s: %s" % (k, e)) k = 'REQUIRES' try: self._requires = frozenset( parse_soname_deps(self._metadata[k])) except InvalidData as e: self._invalid_metadata(k + ".syntax", "%s: %s" % (k, e))
def _validate_deps(self): """ Validate deps. This does not trigger USE calculation since that is expensive for ebuilds and therefore we want to avoid doing in unnecessarily (like for masked packages). """ eapi = self.metadata['EAPI'] dep_eapi = eapi dep_valid_flag = self.iuse.is_valid_flag if self.installed: # Ignore EAPI.incompatible and conditionals missing # from IUSE for installed packages since these issues # aren't relevant now (re-evaluate when new EAPIs are # deployed). dep_eapi = None dep_valid_flag = None for k in self._dep_keys: v = self.metadata.get(k) if not v: continue try: use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag, token_class=Atom) except InvalidDependString as e: self._metadata_exception(k, e) k = 'PROVIDE' v = self.metadata.get(k) if v: try: use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag, token_class=Atom) except InvalidDependString as e: self._invalid_metadata("PROVIDE.syntax", _unicode_decode("%s: %s") % (k, e)) for k in self._use_conditional_misc_keys: v = self.metadata.get(k) if not v: continue try: use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag) except InvalidDependString as e: self._metadata_exception(k, e) k = 'REQUIRED_USE' v = self.metadata.get(k) if v: if not eapi_has_required_use(eapi): self._invalid_metadata( 'EAPI.incompatible', "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi) else: try: check_required_use(v, (), self.iuse.is_valid_flag) except InvalidDependString as e: # Force unicode format string for python-2.x safety, # ensuring that PortageException.__unicode__() is used # when necessary. self._invalid_metadata(k + ".syntax", _unicode_decode("%s: %s") % (k, e)) k = 'SRC_URI' v = self.metadata.get(k) if v: try: use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True, is_valid_flag=self.iuse.is_valid_flag) except InvalidDependString as e: if not self.installed: self._metadata_exception(k, e)
def _validate_deps(self): """ Validate deps. This does not trigger USE calculation since that is expensive for ebuilds and therefore we want to avoid doing it unnecessarily (like for masked packages). """ eapi = self.eapi dep_eapi = eapi dep_valid_flag = self.iuse.is_valid_flag if self.installed: # Ignore EAPI.incompatible and conditionals missing # from IUSE for installed packages since these issues # aren't relevant now (re-evaluate when new EAPIs are # deployed). dep_eapi = None dep_valid_flag = None validated_atoms = [] for k in self._dep_keys: v = self._metadata.get(k) if not v: continue try: atoms = use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag, token_class=Atom, flat=True) except InvalidDependString as e: self._metadata_exception(k, e) else: validated_atoms.extend(atoms) if not self.built: for atom in atoms: if not isinstance(atom, Atom): continue if atom.slot_operator_built: e = InvalidDependString( _("Improper context for slot-operator " "\"built\" atom syntax: %s") % (atom.unevaluated_atom,)) self._metadata_exception(k, e) self._validated_atoms = tuple(set(atom for atom in validated_atoms if isinstance(atom, Atom))) k = 'PROVIDE' v = self._metadata.get(k) if v: try: use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag, token_class=Atom) except InvalidDependString as e: self._invalid_metadata("PROVIDE.syntax", "%s: %s" % (k, e)) for k in self._use_conditional_misc_keys: v = self._metadata.get(k) if not v: continue try: use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag) except InvalidDependString as e: self._metadata_exception(k, e) k = 'REQUIRED_USE' v = self._metadata.get(k) if v and not self.built: if not _get_eapi_attrs(eapi).required_use: self._invalid_metadata('EAPI.incompatible', "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi) else: try: check_required_use(v, (), self.iuse.is_valid_flag, eapi=eapi) except InvalidDependString as e: self._invalid_metadata(k + ".syntax", "%s: %s" % (k, e)) k = 'SRC_URI' v = self._metadata.get(k) if v: try: use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True, is_valid_flag=self.iuse.is_valid_flag) except InvalidDependString as e: if not self.installed: self._metadata_exception(k, e) if self.built: k = 'PROVIDES' try: self._provides = frozenset( parse_soname_deps(self._metadata[k])) except InvalidData as e: self._invalid_metadata(k + ".syntax", "%s: %s" % (k, e)) k = 'REQUIRES' try: self._requires = frozenset( parse_soname_deps(self._metadata[k])) except InvalidData as e: self._invalid_metadata(k + ".syntax", "%s: %s" % (k, e))
def _validate_deps(self): """ Validate deps. This does not trigger USE calculation since that is expensive for ebuilds and therefore we want to avoid doing in unnecessarily (like for masked packages). """ eapi = self.metadata['EAPI'] dep_eapi = eapi dep_valid_flag = self.iuse.is_valid_flag if self.installed: # Ignore EAPI.incompatible and conditionals missing # from IUSE for installed packages since these issues # aren't relevant now (re-evaluate when new EAPIs are # deployed). dep_eapi = None dep_valid_flag = None for k in self._dep_keys: v = self.metadata.get(k) if not v: continue try: use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag, token_class=Atom) except InvalidDependString as e: self._metadata_exception(k, e) k = 'PROVIDE' v = self.metadata.get(k) if v: try: use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag, token_class=Atom) except InvalidDependString as e: self._invalid_metadata("PROVIDE.syntax", _unicode_decode("%s: %s") % (k, e)) for k in self._use_conditional_misc_keys: v = self.metadata.get(k) if not v: continue try: use_reduce(v, eapi=dep_eapi, matchall=True, is_valid_flag=dep_valid_flag) except InvalidDependString as e: self._metadata_exception(k, e) k = 'REQUIRED_USE' v = self.metadata.get(k) if v: if not eapi_has_required_use(eapi): self._invalid_metadata('EAPI.incompatible', "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi) else: try: check_required_use(v, (), self.iuse.is_valid_flag) except InvalidDependString as e: # Force unicode format string for python-2.x safety, # ensuring that PortageException.__unicode__() is used # when necessary. self._invalid_metadata(k + ".syntax", _unicode_decode("%s: %s") % (k, e)) k = 'SRC_URI' v = self.metadata.get(k) if v: try: use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True, is_valid_flag=self.iuse.is_valid_flag) except InvalidDependString as e: if not self.installed: self._metadata_exception(k, e)
def testCheckRequiredUse(self): test_cases = ( ("|| ( a b )", [], ["a", "b"], False), ("|| ( a b )", ["a"], ["a", "b"], True), ("|| ( a b )", ["b"], ["a", "b"], True), ("|| ( a b )", ["a", "b"], ["a", "b"], True), ("^^ ( a b )", [], ["a", "b"], False), ("^^ ( a b )", ["a"], ["a", "b"], True), ("^^ ( a b )", ["b"], ["a", "b"], True), ("^^ ( a b )", ["a", "b"], ["a", "b"], False), ("?? ( a b )", ["a", "b"], ["a", "b"], False), ("?? ( a b )", ["a"], ["a", "b"], True), ("?? ( a b )", ["b"], ["a", "b"], True), ("?? ( a b )", [], ["a", "b"], True), ("?? ( )", [], [], True), ("^^ ( || ( a b ) c )", [], ["a", "b", "c"], False), ("^^ ( || ( a b ) c )", ["a"], ["a", "b", "c"], True), ("^^ ( || ( ( a b ) ) ( c ) )", [], ["a", "b", "c"], False), ("( ^^ ( ( || ( ( a ) ( b ) ) ) ( ( c ) ) ) )", ["a"], ["a", "b", "c"], True), ("a || ( b c )", ["a"], ["a", "b", "c"], False), ("|| ( b c ) a", ["a"], ["a", "b", "c"], False), ("|| ( a b c )", ["a"], ["a", "b", "c"], True), ("|| ( a b c )", ["b"], ["a", "b", "c"], True), ("|| ( a b c )", ["c"], ["a", "b", "c"], True), ("^^ ( a b c )", ["a"], ["a", "b", "c"], True), ("^^ ( a b c )", ["b"], ["a", "b", "c"], True), ("^^ ( a b c )", ["c"], ["a", "b", "c"], True), ("^^ ( a b c )", ["a", "b"], ["a", "b", "c"], False), ("^^ ( a b c )", ["b", "c"], ["a", "b", "c"], False), ("^^ ( a b c )", ["a", "c"], ["a", "b", "c"], False), ("^^ ( a b c )", ["a", "b", "c"], ["a", "b", "c"], False), ("a? ( ^^ ( b c ) )", [], ["a", "b", "c"], True), ("a? ( ^^ ( b c ) )", ["a"], ["a", "b", "c"], False), ("a? ( ^^ ( b c ) )", ["b"], ["a", "b", "c"], True), ("a? ( ^^ ( b c ) )", ["c"], ["a", "b", "c"], True), ("a? ( ^^ ( b c ) )", ["a", "b"], ["a", "b", "c"], True), ("a? ( ^^ ( b c ) )", ["a", "b", "c"], ["a", "b", "c"], False), ("^^ ( a? ( !b ) !c? ( d ) )", [], ["a", "b", "c", "d"], False), ("^^ ( a? ( !b ) !c? ( d ) )", ["a"], ["a", "b", "c", "d"], True), # note: this one is EAPI-dependent, it used to be True for EAPI <7 ("^^ ( a? ( !b ) !c? ( d ) )", ["c"], ["a", "b", "c", "d"], False), ("^^ ( a? ( !b ) !c? ( d ) )", ["a", "c"], ["a", "b", "c", "d"], True), ("^^ ( a? ( !b ) !c? ( d ) )", ["a", "b", "c"], ["a", "b", "c", "d"], False), ("^^ ( a? ( !b ) !c? ( d ) )", ["a", "b", "d"], ["a", "b", "c", "d"], True), ("^^ ( a? ( !b ) !c? ( d ) )", ["a", "b", "d"], ["a", "b", "c", "d"], True), ("^^ ( a? ( !b ) !c? ( d ) )", ["a", "d"], ["a", "b", "c", "d"], False), ("|| ( ^^ ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"], False), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["a"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["b"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["c"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["a", "b"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["a", "c"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["b", "c"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["a", "b", "c"], ["a", "b", "c"], False), ("^^ ( || ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"], False), ("^^ ( || ( a b ) ^^ ( b c ) )", ["a"], ["a", "b", "c"], True), ("^^ ( || ( a b ) ^^ ( b c ) )", ["b"], ["a", "b", "c"], False), ("^^ ( || ( a b ) ^^ ( b c ) )", ["c"], ["a", "b", "c"], True), ("^^ ( || ( a b ) ^^ ( b c ) )", ["a", "b"], ["a", "b", "c"], False), ("^^ ( || ( a b ) ^^ ( b c ) )", ["a", "c"], ["a", "b", "c"], False), ("^^ ( || ( a b ) ^^ ( b c ) )", ["b", "c"], ["a", "b", "c"], True), ("^^ ( || ( a b ) ^^ ( b c ) )", ["a", "b", "c"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", ["a", "b", "c"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", ["b", "c"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", ["a", "c"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", ["a", "b"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", ["a"], ["a", "b", "c"], False), ("|| ( ( a b ) c )", ["b"], ["a", "b", "c"], False), ("|| ( ( a b ) c )", ["c"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", [], ["a", "b", "c"], False), ("^^ ( ( a b ) c )", ["a", "b", "c"], ["a", "b", "c"], False), ("^^ ( ( a b ) c )", ["b", "c"], ["a", "b", "c"], True), ("^^ ( ( a b ) c )", ["a", "c"], ["a", "b", "c"], True), ("^^ ( ( a b ) c )", ["a", "b"], ["a", "b", "c"], True), ("^^ ( ( a b ) c )", ["a"], ["a", "b", "c"], False), ("^^ ( ( a b ) c )", ["b"], ["a", "b", "c"], False), ("^^ ( ( a b ) c )", ["c"], ["a", "b", "c"], True), ("^^ ( ( a b ) c )", [], ["a", "b", "c"], False), ) test_cases_xfail = ( ("^^ ( || ( a b ) ^^ ( b c ) )", [], ["a", "b"]), ("^^ ( || ( a b ) ^^ ( b c )", [], ["a", "b", "c"]), ("^^( || ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"]), ("^^ || ( a b ) ^^ ( b c )", [], ["a", "b", "c"]), ("^^ ( ( || ) ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"]), ("^^ ( || ( a b ) ) ^^ ( b c ) )", [], ["a", "b", "c"]), ) test_cases_xfail_eapi = (("?? ( a b )", [], ["a", "b"], "4"), ) for required_use, use, iuse, expected in test_cases: self.assertEqual(bool(check_required_use(required_use, use, iuse.__contains__)), \ expected, required_use + ", USE = " + " ".join(use)) for required_use, use, iuse in test_cases_xfail: self.assertRaisesMsg(required_use + ", USE = " + " ".join(use), \ InvalidDependString, check_required_use, required_use, use, iuse.__contains__) for required_use, use, iuse, eapi in test_cases_xfail_eapi: self.assertRaisesMsg(required_use + ", USE = " + " ".join(use), \ InvalidDependString, check_required_use, required_use, use, iuse.__contains__, eapi=eapi)
def _find_suggestions(self): if not self.shortest_cycle: return None, None suggestions = [] final_solutions = {} for pos, pkg in enumerate(self.shortest_cycle): parent = self.shortest_cycle[pos-1] priorities = self.graph.nodes[parent][0][pkg] parent_atoms = self.all_parent_atoms.get(pkg) if priorities[-1].buildtime: dep = parent.metadata["DEPEND"] elif priorities[-1].runtime: dep = parent.metadata["RDEPEND"] for ppkg, atom in parent_atoms: if ppkg == parent: changed_parent = ppkg parent_atom = atom.unevaluated_atom break affecting_use = extract_affecting_use(dep, parent_atom) # Make sure we don't want to change a flag that is # a) in use.mask or use.force # b) changed by autounmask usemask, useforce = self._get_use_mask_and_force(parent) autounmask_changes = self._get_autounmask_changes(parent) untouchable_flags = frozenset(chain(usemask, useforce, autounmask_changes)) affecting_use.difference_update(untouchable_flags) #If any of the flags we're going to touch is in REQUIRED_USE, add all #other flags in REQUIRED_USE to affecting_use, to not lose any solution. required_use_flags = get_required_use_flags(parent.metadata["REQUIRED_USE"]) if affecting_use.intersection(required_use_flags): affecting_use.update(required_use_flags) affecting_use.difference_update(untouchable_flags) affecting_use = tuple(affecting_use) if not affecting_use: continue #We iterate over all possible settings of these use flags and gather #a set of possible changes #TODO: Use the information encoded in REQUIRED_USE use_state = [] for flag in affecting_use: use_state.append("disabled") def _next_use_state(state, id=None): if id is None: id = len(state)-1 if id == 0 and state[0] == "enabled": return False if state[id] == "disabled": state[id] = "enabled" for i in range(id+1,len(state)): state[i] = "disabled" return True else: return _next_use_state(state, id-1) solutions = set() while(True): current_use = set(self.depgraph._pkg_use_enabled(parent)) for flag, state in zip(affecting_use, use_state): if state == "enabled": current_use.add(flag) else: current_use.discard(flag) reduced_dep = use_reduce(dep, uselist=current_use, flat=True) if parent_atom not in reduced_dep: #We found an assignment that removes the atom from 'dep'. #Make sure it doesn't conflict with REQUIRED_USE. required_use = parent.metadata["REQUIRED_USE"] if check_required_use(required_use, current_use, parent.iuse.is_valid_flag): use = self.depgraph._pkg_use_enabled(parent) solution = set() for flag, state in zip(affecting_use, use_state): if state == "enabled" and \ flag not in use: solution.add((flag, True)) elif state == "disabled" and \ flag in use: solution.add((flag, False)) solutions.add(frozenset(solution)) if not _next_use_state(use_state): break for solution in solutions: ignore_solution = False for other_solution in solutions: if solution is other_solution: continue if solution.issuperset(other_solution): ignore_solution = True if ignore_solution: continue #Check if a USE change conflicts with use requirements of the parents. #If a requiremnet is hard, ignore the suggestion. #If the requirment is conditional, warn the user that other changes might be needed. followup_change = False parent_parent_atoms = self.depgraph._dynamic_config._parent_atoms.get(changed_parent) for ppkg, atom in parent_parent_atoms: atom = atom.unevaluated_atom if not atom.use: continue for flag, state in solution: if flag in atom.use.enabled or flag in atom.use.disabled: ignore_solution = True break elif atom.use.conditional: for flags in atom.use.conditional.values(): if flag in flags: followup_change = True break if ignore_solution: break if ignore_solution: continue changes = [] for flag, state in solution: if state: changes.append(colorize("red", "+"+flag)) else: changes.append(colorize("blue", "-"+flag)) msg = "- %s (Change USE: %s)\n" \ % (parent.cpv, " ".join(changes)) if followup_change: msg += " (This change might require USE changes on parent packages.)" suggestions.append(msg) final_solutions.setdefault(pkg, set()).add(solution) return final_solutions, suggestions
def testCheckRequiredUse(self): test_cases = ( ("|| ( a b )", [], ["a", "b"], False), ("|| ( a b )", ["a"], ["a", "b"], True), ("|| ( a b )", ["b"], ["a", "b"], True), ("|| ( a b )", ["a", "b"], ["a", "b"], True), ("^^ ( a b )", [], ["a", "b"], False), ("^^ ( a b )", ["a"], ["a", "b"], True), ("^^ ( a b )", ["b"], ["a", "b"], True), ("^^ ( a b )", ["a", "b"], ["a", "b"], False), ("?? ( a b )", ["a", "b"], ["a", "b"], False), ("?? ( a b )", ["a"], ["a", "b"], True), ("?? ( a b )", ["b"], ["a", "b"], True), ("?? ( a b )", [], ["a", "b"], True), ("?? ( )", [], [], True), ("^^ ( || ( a b ) c )", [], ["a", "b", "c"], False), ("^^ ( || ( a b ) c )", ["a"], ["a", "b", "c"], True), ("^^ ( || ( ( a b ) ) ( c ) )", [], ["a", "b", "c"], False), ("( ^^ ( ( || ( ( a ) ( b ) ) ) ( ( c ) ) ) )", ["a"], ["a", "b", "c"], True), ("a || ( b c )", ["a"], ["a", "b", "c"], False), ("|| ( b c ) a", ["a"], ["a", "b", "c"], False), ("|| ( a b c )", ["a"], ["a", "b", "c"], True), ("|| ( a b c )", ["b"], ["a", "b", "c"], True), ("|| ( a b c )", ["c"], ["a", "b", "c"], True), ("^^ ( a b c )", ["a"], ["a", "b", "c"], True), ("^^ ( a b c )", ["b"], ["a", "b", "c"], True), ("^^ ( a b c )", ["c"], ["a", "b", "c"], True), ("^^ ( a b c )", ["a", "b"], ["a", "b", "c"], False), ("^^ ( a b c )", ["b", "c"], ["a", "b", "c"], False), ("^^ ( a b c )", ["a", "c"], ["a", "b", "c"], False), ("^^ ( a b c )", ["a", "b", "c"], ["a", "b", "c"], False), ("a? ( ^^ ( b c ) )", [], ["a", "b", "c"], True), ("a? ( ^^ ( b c ) )", ["a"], ["a", "b", "c"], False), ("a? ( ^^ ( b c ) )", ["b"], ["a", "b", "c"], True), ("a? ( ^^ ( b c ) )", ["c"], ["a", "b", "c"], True), ("a? ( ^^ ( b c ) )", ["a", "b"], ["a", "b", "c"], True), ("a? ( ^^ ( b c ) )", ["a", "b", "c"], ["a", "b", "c"], False), ("^^ ( a? ( !b ) !c? ( d ) )", [], ["a", "b", "c", "d"], False), ("^^ ( a? ( !b ) !c? ( d ) )", ["a"], ["a", "b", "c", "d"], True), # note: this one is EAPI-dependent, it used to be True for EAPI <7 ("^^ ( a? ( !b ) !c? ( d ) )", ["c"], ["a", "b", "c", "d"], False), ("^^ ( a? ( !b ) !c? ( d ) )", ["a", "c"], ["a", "b", "c", "d"], True), ("^^ ( a? ( !b ) !c? ( d ) )", ["a", "b", "c"], ["a", "b", "c", "d"], False), ("^^ ( a? ( !b ) !c? ( d ) )", ["a", "b", "d"], ["a", "b", "c", "d"], True), ("^^ ( a? ( !b ) !c? ( d ) )", ["a", "b", "d"], ["a", "b", "c", "d"], True), ("^^ ( a? ( !b ) !c? ( d ) )", ["a", "d"], ["a", "b", "c", "d"], False), ("|| ( ^^ ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"], False), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["a"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["b"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["c"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["a", "b"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["a", "c"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["b", "c"], ["a", "b", "c"], True), ("|| ( ^^ ( a b ) ^^ ( b c ) )", ["a", "b", "c"], ["a", "b", "c"], False), ("^^ ( || ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"], False), ("^^ ( || ( a b ) ^^ ( b c ) )", ["a"], ["a", "b", "c"], True), ("^^ ( || ( a b ) ^^ ( b c ) )", ["b"], ["a", "b", "c"], False), ("^^ ( || ( a b ) ^^ ( b c ) )", ["c"], ["a", "b", "c"], True), ("^^ ( || ( a b ) ^^ ( b c ) )", ["a", "b"], ["a", "b", "c"], False), ("^^ ( || ( a b ) ^^ ( b c ) )", ["a", "c"], ["a", "b", "c"], False), ("^^ ( || ( a b ) ^^ ( b c ) )", ["b", "c"], ["a", "b", "c"], True), ("^^ ( || ( a b ) ^^ ( b c ) )", ["a", "b", "c"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", ["a", "b", "c"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", ["b", "c"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", ["a", "c"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", ["a", "b"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", ["a"], ["a", "b", "c"], False), ("|| ( ( a b ) c )", ["b"], ["a", "b", "c"], False), ("|| ( ( a b ) c )", ["c"], ["a", "b", "c"], True), ("|| ( ( a b ) c )", [], ["a", "b", "c"], False), ("^^ ( ( a b ) c )", ["a", "b", "c"], ["a", "b", "c"], False), ("^^ ( ( a b ) c )", ["b", "c"], ["a", "b", "c"], True), ("^^ ( ( a b ) c )", ["a", "c"], ["a", "b", "c"], True), ("^^ ( ( a b ) c )", ["a", "b"], ["a", "b", "c"], True), ("^^ ( ( a b ) c )", ["a"], ["a", "b", "c"], False), ("^^ ( ( a b ) c )", ["b"], ["a", "b", "c"], False), ("^^ ( ( a b ) c )", ["c"], ["a", "b", "c"], True), ("^^ ( ( a b ) c )", [], ["a", "b", "c"], False), ) test_cases_xfail = ( ("^^ ( || ( a b ) ^^ ( b c ) )", [], ["a", "b"]), ("^^ ( || ( a b ) ^^ ( b c )", [], ["a", "b", "c"]), ("^^( || ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"]), ("^^ || ( a b ) ^^ ( b c )", [], ["a", "b", "c"]), ("^^ ( ( || ) ( a b ) ^^ ( b c ) )", [], ["a", "b", "c"]), ("^^ ( || ( a b ) ) ^^ ( b c ) )", [], ["a", "b", "c"]), ) test_cases_xfail_eapi = ( ("?? ( a b )", [], ["a", "b"], "4"), ) for required_use, use, iuse, expected in test_cases: self.assertEqual(bool(check_required_use(required_use, use, iuse.__contains__)), \ expected, required_use + ", USE = " + " ".join(use)) for required_use, use, iuse in test_cases_xfail: self.assertRaisesMsg(required_use + ", USE = " + " ".join(use), \ InvalidDependString, check_required_use, required_use, use, iuse.__contains__) for required_use, use, iuse, eapi in test_cases_xfail_eapi: self.assertRaisesMsg(required_use + ", USE = " + " ".join(use), \ InvalidDependString, check_required_use, required_use, use, iuse.__contains__, eapi=eapi)
def _find_suggestions(self): if not self.shortest_cycle: return None, None suggestions = [] final_solutions = {} for pos, pkg in enumerate(self.shortest_cycle): parent = self.shortest_cycle[pos - 1] priorities = self.graph.nodes[parent][0][pkg] parent_atoms = self.all_parent_atoms.get(pkg) if priorities[-1].buildtime: dep = parent.metadata["DEPEND"] elif priorities[-1].runtime: dep = parent.metadata["RDEPEND"] for ppkg, atom in parent_atoms: if ppkg == parent: changed_parent = ppkg parent_atom = atom.unevaluated_atom break affecting_use = extract_affecting_use(dep, parent_atom) # Make sure we don't want to change a flag that is # a) in use.mask or use.force # b) changed by autounmask usemask, useforce = self._get_use_mask_and_force(parent) autounmask_changes = self._get_autounmask_changes(parent) untouchable_flags = frozenset( chain(usemask, useforce, autounmask_changes)) affecting_use.difference_update(untouchable_flags) #If any of the flags we're going to touch is in REQUIRED_USE, add all #other flags in REQUIRED_USE to affecting_use, to not lose any solution. required_use_flags = get_required_use_flags( parent.metadata["REQUIRED_USE"]) if affecting_use.intersection(required_use_flags): affecting_use.update(required_use_flags) affecting_use.difference_update(untouchable_flags) affecting_use = tuple(affecting_use) if not affecting_use: continue #We iterate over all possible settings of these use flags and gather #a set of possible changes #TODO: Use the information encoded in REQUIRED_USE use_state = [] for flag in affecting_use: use_state.append("disabled") def _next_use_state(state, id=None): if id is None: id = len(state) - 1 if id == 0 and state[0] == "enabled": return False if state[id] == "disabled": state[id] = "enabled" for i in range(id + 1, len(state)): state[i] = "disabled" return True else: return _next_use_state(state, id - 1) solutions = set() while (True): current_use = set(self.depgraph._pkg_use_enabled(parent)) for flag, state in zip(affecting_use, use_state): if state == "enabled": current_use.add(flag) else: current_use.discard(flag) reduced_dep = use_reduce(dep, uselist=current_use, flat=True) if parent_atom not in reduced_dep: #We found an assignment that removes the atom from 'dep'. #Make sure it doesn't conflict with REQUIRED_USE. required_use = parent.metadata["REQUIRED_USE"] if check_required_use(required_use, current_use, parent.iuse.is_valid_flag): use = self.depgraph._pkg_use_enabled(parent) solution = set() for flag, state in zip(affecting_use, use_state): if state == "enabled" and \ flag not in use: solution.add((flag, True)) elif state == "disabled" and \ flag in use: solution.add((flag, False)) solutions.add(frozenset(solution)) if not _next_use_state(use_state): break for solution in solutions: ignore_solution = False for other_solution in solutions: if solution is other_solution: continue if solution.issuperset(other_solution): ignore_solution = True if ignore_solution: continue #Check if a USE change conflicts with use requirements of the parents. #If a requiremnet is hard, ignore the suggestion. #If the requirment is conditional, warn the user that other changes might be needed. followup_change = False parent_parent_atoms = self.depgraph._dynamic_config._parent_atoms.get( changed_parent) for ppkg, atom in parent_parent_atoms: atom = atom.unevaluated_atom if not atom.use: continue for flag, state in solution: if flag in atom.use.enabled or flag in atom.use.disabled: ignore_solution = True break elif atom.use.conditional: for flags in atom.use.conditional.values(): if flag in flags: followup_change = True break if ignore_solution: break if ignore_solution: continue changes = [] for flag, state in solution: if state: changes.append(colorize("red", "+" + flag)) else: changes.append(colorize("blue", "-" + flag)) msg = "- %s (Change USE: %s)\n" \ % (parent.cpv, " ".join(changes)) if followup_change: msg += " (This change might require USE changes on parent packages.)" suggestions.append(msg) final_solutions.setdefault(pkg, set()).add(solution) return final_solutions, suggestions