Merged from trunk 10509:10521
authorFabian Groffen <grobian@gentoo.org>
Mon, 2 Jun 2008 20:38:21 +0000 (20:38 -0000)
committerFabian Groffen <grobian@gentoo.org>
Mon, 2 Jun 2008 20:38:21 +0000 (20:38 -0000)
   | 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

pym/_emerge/__init__.py
pym/portage/dep.py
pym/portage/sets/base.py
pym/portage/tests/dep/test_isvalidatom.py

index 7faeda8d1c7b0f198cca01cd7fb041808d45e2a8..495a5fa49e0950e736aa3045f2235a3ff97b5b1c 100644 (file)
@@ -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
index d0e487afc135646f33bc606d42043ae44e53be80..df6bdfd3e2d39b9bd2f3963e7f0ad41216398362 100644 (file)
@@ -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:
index 2ae4723fb30c5f0c0942f821e885bed2f865e87f..d9f2a8d91b034a95c553063998f083407ccbb535 100644 (file)
@@ -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):
                """
index 2b99b3dcb0b3f30375b799afa9458cbff2dbe608..dfcf178988517d9e9afa5872938f94a273ad503f 100644 (file)
@@ -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]: