From: Fabian Groffen Date: Mon, 2 Jun 2008 20:38:21 +0000 (-0000) Subject: Merged from trunk 10509:10521 X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=15500474901d36593cb6aa6cde40e9c0a6dd32bd;p=portage.git Merged from trunk 10509:10521 | 10511 | Fix PackageSet.findAtomForPackage() to find the most | | zmedico | specific atom since it can affect behavior when deciding | | | whether or not to add a slot atom to the world file. | | 10513 | Optimize the new --skipfirst code so that in only has to | | zmedico | build a new depgraph one time when there are unsatisfied | | | deps. This works by recursively traversing the digraph to | | | remove the parent packages whose deps become unsatisfied | | | when their dependencies are pruned from the mergelist. | | 10514 | Clean up reference to parts of the first graph for | | zmedico | --skipfirst. | | 10516 | * Tweak isvalidatom() to treat "null" category as valid, but | | zmedico | missing category as invalid (previously both where treated | | | as invalid). * Fix a spot inside emerge's unmerge() function | | | that sometimes generates invalid atoms by adding an | | | unnecessary "=" to the front. Even when in cases that | | | resulted in a valid atom here, adding the "=" was redundant | | | because dep_expand() does that automatically now (for | | | backward compat). | | 10518 | Fix TypeError: 'tuple' object does not support item | | zmedico | assignment | | 10519 | Add some isvalidatom() test cases with "null" and missing | | zmedico | category. | | 10521 | Fix create_world_atom() to take a Package instance in it's | | zmedico | arguments. | svn path=/main/branches/prefix/; revision=10544 --- diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py index 7faeda8d1..495a5fa49 100644 --- a/pym/_emerge/__init__.py +++ b/pym/_emerge/__init__.py @@ -751,15 +751,14 @@ class RootConfig(object): self.sets = self.setconfig.getSets() self.visible_pkgs = PackageVirtualDbapi(self.settings) -def create_world_atom(pkg_key, metadata, args_set, root_config): +def create_world_atom(pkg, args_set, root_config): """Create a new atom for the world file if one does not exist. If the argument atom is precise enough to identify a specific slot then a slot atom will be returned. Atoms that are in the system set may also be stored in world since system atoms can only match one slot while world atoms can be greedy with respect to slots. Unslotted system packages will not be stored in world.""" - pkg = Package(cpv=pkg_key, root_config=root_config, metadata=metadata) - metadata = pkg.metadata + arg_atom = args_set.findAtomForPackage(pkg) if not arg_atom: return None @@ -781,7 +780,7 @@ def create_world_atom(pkg_key, metadata, args_set, root_config): if slotted and arg_atom != cp: # If the user gave a specific atom, store it as a # slot atom in the world file. - slot_atom = "%s:%s" % (cp, metadata["SLOT"]) + slot_atom = pkg.slot_atom # For USE=multislot, there are a couple of cases to # handle here: @@ -4964,8 +4963,7 @@ class depgraph(object): myroot == self.target_root and \ favorites_set.findAtomForPackage(pkg): # Maybe it will be added to world now. - if create_world_atom(pkg_key, metadata, - favorites_set, root_config): + if create_world_atom(pkg, favorites_set, root_config): pkg_world = True except portage.exception.InvalidDependString: # This is reported elsewhere if relevant. @@ -5264,10 +5262,9 @@ class depgraph(object): pkg_type, root, pkg_key, pkg_status = x if pkg_status != "nomerge": continue - metadata = x.metadata + try: - myfavkey = create_world_atom(pkg_key, metadata, - args_set, root_config) + myfavkey = create_world_atom(pkg, args_set, root_config) if myfavkey: if myfavkey in added_favorites: continue @@ -6219,8 +6216,7 @@ class MergeTask(object): args_set.findAtomForPackage(pkg): world_set.lock() world_set.load() # maybe it's changed on disk - myfavkey = create_world_atom(pkg_key, metadata, - args_set, root_config) + myfavkey = create_world_atom(pkg, args_set, root_config) if myfavkey: print ">>> Recording",myfavkey,"in \"world\" favorites file..." emergelog(xterm_titles, " === ("+\ @@ -6468,8 +6464,7 @@ def unmerge(root_config, myopts, unmerge_action, sys.exit(1) if not mymatch and x[0] not in "<>=~": - #add a "=" if missing - mymatch=localtree.dep_match("="+x) + mymatch = localtree.dep_match(x) if not mymatch: portage.writemsg("\n--- Couldn't find '%s' to %s.\n" % \ (x, unmerge_action), noiselevel=-1) @@ -8464,19 +8459,49 @@ def action_build(settings, trees, mtimedb, except depgraph.UnsatisfiedResumeDep, e: if "--skipfirst" not in myopts: raise - unsatisfied_parents = set(dep.parent for dep in e.value) - pruned_mergelist = [] - for task in mergelist: - if isinstance(task, list) and \ - tuple(task) in unsatisfied_parents: + + graph = mydepgraph.digraph + unsatisfied_parents = dict((dep.parent, dep.parent) \ + for dep in e.value) + traversed_nodes = set() + unsatisfied_stack = list(unsatisfied_parents) + while unsatisfied_stack: + pkg = unsatisfied_stack.pop() + if pkg in traversed_nodes: continue - pruned_mergelist.append(task) + traversed_nodes.add(pkg) + + # If this package was pulled in by a parent + # package scheduled for merge, removing this + # package may cause the the parent package's + # dependency to become unsatisfied. + for parent_node in graph.parent_nodes(pkg): + if not isinstance(parent_node, Package) \ + or parent_node.operation != "merge": + continue + unsatisfied = \ + graph.child_nodes(parent_node, + ignore_priority=DepPriority.SOFT) + if pkg in unsatisfied: + unsatisfied_parents[parent_node] = parent_node + unsatisfied_stack.append(parent_node) + + pruned_mergelist = [x for x in mergelist \ + if isinstance(x, list) and \ + tuple(x) not in unsatisfied_parents] + + # It shouldn't happen, but if the size of mergelist + # does not decrease for some reason then the loop + # will be infinite. Therefore, if that case ever + # occurs for some reason, raise the exception to + # break out of the loop. if not pruned_mergelist or \ len(pruned_mergelist) == len(mergelist): raise mergelist[:] = pruned_mergelist dropped_tasks.update(unsatisfied_parents) - del e, unsatisfied_parents + del e, graph, traversed_nodes, \ + unsatisfied_parents, unsatisfied_stack continue else: break diff --git a/pym/portage/dep.py b/pym/portage/dep.py index d0e487afc..df6bdfd3e 100644 --- a/pym/portage/dep.py +++ b/pym/portage/dep.py @@ -593,19 +593,31 @@ def isvalidatom(atom, allow_blockers=False): if allow_blockers and atom.startswith("!"): atom = atom[1:] cpv = dep_getcpv(atom) + cpv_catsplit = catsplit(cpv) + mycpv_cps = None if cpv: - if _valid_category.match(catsplit(cpv)[0]) is None: - return 0 - mycpv_cps = catpkgsplit(cpv) - else: - mycpv_cps = None + if len(cpv_catsplit) == 2: + if _valid_category.match(cpv_catsplit[0]) is None: + return 0 + if cpv_catsplit[0] == "null": + # "null" category is valid, missing category is not. + mycpv_cps = catpkgsplit(cpv.replace("null/", "cat/", 1)) + if mycpv_cps: + mycpv_cps = list(mycpv_cps) + mycpv_cps[0] = "null" + if not mycpv_cps: + mycpv_cps = catpkgsplit(cpv) + operator = get_operator(atom) if operator: if operator[0] in "<>" and remove_slot(atom).endswith("*"): return 0 - if mycpv_cps and mycpv_cps[0] != "null": - # >=cat/pkg-1.0 - return 1 + if mycpv_cps: + if len(cpv_catsplit) == 2: + # >=cat/pkg-1.0 + return 1 + else: + return 0 else: # >=cat/pkg or >=pkg-1.0 (no category) return 0 @@ -613,7 +625,7 @@ def isvalidatom(atom, allow_blockers=False): # cat/pkg-1.0 return 0 - if (len(atom.split('/')) == 2): + if len(cpv_catsplit) == 2: # cat/pkg return 1 else: diff --git a/pym/portage/sets/base.py b/pym/portage/sets/base.py index 2ae4723fb..d9f2a8d91 100644 --- a/pym/portage/sets/base.py +++ b/pym/portage/sets/base.py @@ -105,10 +105,19 @@ class PackageSet(object): None if there are no matches. This matches virtual arguments against the PROVIDE metadata. This can raise an InvalidDependString exception if an error occurs while parsing PROVIDE.""" - try: - return self.iterAtomsForPackage(pkg).next() - except StopIteration: - return None + + # Atoms matched via PROVIDE must be temporarily transformed since + # match_from_list() only works correctly when atom.cp == pkg.cp. + rev_transform = {} + for atom in self.iterAtomsForPackage(pkg): + if atom.cp == pkg.cp: + rev_transform[atom] = atom + else: + rev_transform[Atom(atom.replace(atom.cp, pkg.cp, 1))] = atom + best_match = best_match_to_list(pkg, rev_transform.iterkeys()) + if best_match: + return rev_transform[best_match] + return None def iterAtomsForPackage(self, pkg): """ diff --git a/pym/portage/tests/dep/test_isvalidatom.py b/pym/portage/tests/dep/test_isvalidatom.py index 2b99b3dcb..dfcf17898 100644 --- a/pym/portage/tests/dep/test_isvalidatom.py +++ b/pym/portage/tests/dep/test_isvalidatom.py @@ -31,7 +31,17 @@ class IsValidAtom(TestCase): ( "<~category/foo-1.0", False ), ( "###cat/foo-1.0", False ), ( "~sys-apps/portage", False ), - ( "portage", False ) ] + ( "portage", False ), + ( "=portage", False ), + ( ">=portage-2.1", False ), + ( "~portage-2.1", False ), + ( "=portage-2.1*", False ), + ( "null/portage", True ), + ( "=null/portage", False ), + ( "=null/portage*", False ), + ( ">=null/portage-2.1", True ), + ( "~null/portage-2.1", True ), + ( "=null/portage-2.1*", True ),] for test in tests: if test[1]: