Merged from trunk -r12565:12583
authorFabian Groffen <grobian@gentoo.org>
Sat, 14 Feb 2009 10:46:01 +0000 (10:46 -0000)
committerFabian Groffen <grobian@gentoo.org>
Sat, 14 Feb 2009 10:46:01 +0000 (10:46 -0000)
   | 12566   | Always ignore hidden files when generating Manifests. Thanks |
   | zmedico | to Christian Ruppert for reporting.                          |

   | 12567   | Implement Manifest2Entry.__ne__() so that comparisons inside |
   | zmedico | Manifest.write() work properly (to avoid rewriting an        |
   |         | identical manifest when possible).                           |

   | 12568   | Fix logic inside depgraph._serialize_tasks() to avoid the    |
   | zmedico | circular runtime deps path in some cases when it's not       |
   |         | appropriate. This solves a case that was reported, in which  |
   |         | the perl was merged before libperl due do perl and lots of   |
   |         | it's deps being selected all at once. In this case, so many  |
   |         | packages were selected at once that the cmp_circular_bias()  |
   |         | sort did not order them very well (though it normally works  |
   |         | fine with a smaller number of packages). Thanks to Daniel    |
   |         | Robbins for reporting this issue and helping me reproduce    |
   |         | it.                                                          |

   | 12569   | Bug #256616 - Since dependencies on system packages are      |
   | zmedico | frequently unspecified, merge them only when no builds are   |
   |         | executing. When a system package finishes building, it's     |
   |         | added to a wait queue that is only processed when the number |
   |         | of running builds drops to zero. All pending merges are then |
   |         | processed before any new builds are allowed to start.        |

   | 12570   | In depgraph._serialize_tasks(), when separating uninstall    |
   | zmedico | nodes from leaf nodes, do it earlier so that it covers more  |
   |         | code paths.                                                  |

   | 12571   | Add a cmp_sort_key class which makes it easier to port code  |
   | zmedico | for python-3.0 compatibility. It works by generating key     |
   |         | objects which use the given cmp function to implement their  |
   |         | __lt__ method.                                               |

   | 12572   | Use portage.util.cmp_sort_key for python-3.0 compatibility.  |
   | zmedico |                                                              |

   | 12573   | Use portage.util.cmp_sort_key for python-3.0 compatibility.  |
   | zmedico |                                                              |

   | 12574   | Use portage.util.cmp_sort_key for python-3.0 compatibility.  |
   | zmedico |                                                              |

   | 12575   | Make emerge --regen return non-zero if that are any          |
   | zmedico | failures. Thanks to Daniel Robbins for reporting.            |

   | 12576   | Add a DepPriority.optional attribute for optional build time |
   | zmedico | deps that are pulled in by --with-bdeps=y.                   |

   | 12577   | For optional deps pulled in by --with-bdeps=y, set           |
   | zmedico | DepPriority.buildtime to False.                              |

   | 12578   | Bug #256616 - Also consider deep runtime dependencies of     |
   | zmedico | system packages when adding packages to merge_wait_queue.    |

   | 12579   | Add UnmergeDepPriority.optional attribute, to avoid          |
   | zmedico | traceback.                                                   |

   | 12580   | Add support in digraph for multiple priorities per edge and  |
   | zmedico | support for callable ignore_priority arguments that can be   |
   |         | used for finer grained filtering.                            |

   | 12581   | Fix Scheduler._find_system_deps() so that it only traverses  |
   | zmedico | runtime deps.                                                |

   | 12582   | Fix the priorities display in digraph.debug_print() so it    |
   | zmedico | shows the highest priority (since multiple priorities are    |
   |         | now supported).                                              |

   | 12583   | Warn about WANT_AUTO(CONF|MAKE)=latest settings since they   |
   | zmedico | are redundant. Thanks to Petteri Räty <betelgeuse@g.o> for  |
   |         | the initial patch.                                           |

svn path=/main/branches/prefix/; revision=12606

bin/repoman
pym/_emerge/__init__.py
pym/portage/__init__.py
pym/portage/dbapi/__init__.py
pym/portage/manifest.py
pym/portage/util.py
pym/repoman/checks.py

index ba7b3c2d35dbfb98e994108c737c75ad43563145..03fc80c13357c9247dcd740c5ef01d015ff23b8b 100755 (executable)
@@ -64,7 +64,7 @@ from portage.process import find_binary, spawn
 from portage.output import bold, create_color_func, darkgreen, \
        green, nocolor, red, turquoise, yellow
 from portage.output import ConsoleStyleFile, StyleWriter
-from portage.util import writemsg_level
+from portage.util import cmp_sort_key, writemsg_level
 
 util.initialize_logger()
 
@@ -846,7 +846,7 @@ for x in scanlist:
                ebuild_split = portage.pkgsplit(ebuildlist[i])
                pkgsplits[ebuild_split] = ebuildlist[i]
                ebuildlist[i] = ebuild_split
-       ebuildlist.sort(portage.pkgcmp)
+       ebuildlist.sort(key=cmp_sort_key(portage.pkgcmp))
        for i in xrange(len(ebuildlist)):
                ebuildlist[i] = pkgsplits[ebuildlist[i]]
        del pkgsplits
index 398fcf776493287f19c270d276cd7cca85482f9b..cf34dcb006f6abda7b8052bd84194cbd11f5c42c 100644 (file)
@@ -66,7 +66,7 @@ from portage.const import EPREFIX, BPREFIX, EPREFIX_LSTRIP, EAPIPREFIX
 from portage.data import secpass
 from portage.elog.messages import eerror
 from portage.util import normalize_path as normpath
-from portage.util import writemsg, writemsg_level
+from portage.util import cmp_sort_key, writemsg, writemsg_level
 from portage.sets import load_default_config, SETPREFIX
 from portage.sets.base import InternalPackageSet
 
@@ -962,7 +962,8 @@ class DepPriority(AbstractDepPriority):
                satisfied and buildtime               -4       SOFT
                satisfied and runtime                 -5       SOFT
                satisfied and runtime_post            -6       SOFT
-               (none of the above)                   -6       SOFT
+               optional                              -7       SOFT
+               (none of the above)                   -7       SOFT
 
                Several integer constants are defined for categorization of priority
                levels:
@@ -972,13 +973,15 @@ class DepPriority(AbstractDepPriority):
                SOFT     The upper boundary for soft dependencies.
                MIN      The lower boundary for soft dependencies.
        """
-       __slots__ = ("satisfied", "rebuild")
+       __slots__ = ("satisfied", "optional", "rebuild")
        MEDIUM = -1
        MEDIUM_SOFT = -2
        SOFT   = -3
-       MIN    = -6
+       MIN    = -7
 
        def __int__(self):
+               if self.optional:
+                       return -7
                if not self.satisfied:
                        if self.buildtime:
                                return 0
@@ -994,7 +997,7 @@ class DepPriority(AbstractDepPriority):
                        return -5
                if self.runtime_post:
                        return -6
-               return -6
+               return -7
 
        def __str__(self):
                myvalue = self.__int__()
@@ -1014,7 +1017,7 @@ class BlockerDepPriority(DepPriority):
 BlockerDepPriority.instance = BlockerDepPriority()
 
 class UnmergeDepPriority(AbstractDepPriority):
-       __slots__ = ("satisfied",)
+       __slots__ = ("optional", "satisfied",)
        """
        Combination of properties           Priority  Category
 
@@ -4855,7 +4858,7 @@ class depgraph(object):
                dep_pkg, existing_node = self._select_package(dep.root, dep.atom,
                        onlydeps=dep.onlydeps)
                if not dep_pkg:
-                       if dep.priority.satisfied:
+                       if dep.priority.optional:
                                # This could be an unecessary build-time dep
                                # pulled in by --with-bdeps=y.
                                return 1
@@ -5098,18 +5101,18 @@ class depgraph(object):
                        "empty" not in self.myparams:
                        edepend["RDEPEND"] = ""
                        edepend["PDEPEND"] = ""
-               bdeps_satisfied = False
-               
+               bdeps_optional = False
+
                if pkg.built and not removal_action:
                        if self.myopts.get("--with-bdeps", "n") == "y":
                                # Pull in build time deps as requested, but marked them as
-                               # "satisfied" since they are not strictly required. This allows
+                               # "optional" since they are not strictly required. This allows
                                # more freedom in the merge order calculation for solving
                                # circular dependencies. Don't convert to PDEPEND since that
                                # could make --with-bdeps=y less effective if it is used to
                                # adjust merge order to prevent built_with_use() calls from
                                # failing.
-                               bdeps_satisfied = True
+                               bdeps_optional = True
                        else:
                                # built packages do not have build time dependencies.
                                edepend["DEPEND"] = ""
@@ -5119,7 +5122,8 @@ class depgraph(object):
 
                deps = (
                        ("/", edepend["DEPEND"],
-                               self._priority(buildtime=True, satisfied=bdeps_satisfied)),
+                               self._priority(buildtime=(not bdeps_optional),
+                               optional=bdeps_optional)),
                        (myroot, edepend["RDEPEND"], self._priority(runtime=True)),
                        (myroot, edepend["PDEPEND"], self._priority(runtime_post=True))
                )
@@ -6710,7 +6714,7 @@ class depgraph(object):
                        node_info[node] = len(mygraph.parent_nodes(node))
                def cmp_merge_preference(node1, node2):
                        return node_info[node2] - node_info[node1]
-               mygraph.order.sort(cmp_merge_preference)
+               mygraph.order.sort(key=cmp_sort_key(cmp_merge_preference))
 
        def altlist(self, reversed=False):
 
@@ -6929,37 +6933,42 @@ class depgraph(object):
                                for ignore_priority in ignore_priority_soft_range:
                                        nodes = get_nodes(ignore_priority=ignore_priority)
                                        if nodes:
-                                               break
-                               if nodes:
-                                       if ignore_priority is None and not tree_mode:
-                                               # Greedily pop all of these nodes since no relationship
-                                               # has been ignored.  This optimization destroys --tree
-                                               # output, so it's disabled in reversed mode. If there
-                                               # is a mix of merge and uninstall nodes, save the
-                                               # uninstall nodes from later since sometimes a merge
-                                               # node will render an install node unnecessary, and
-                                               # we want to avoid doing a separate uninstall task in
-                                               # that case.
-                                               merge_nodes = [node for node in nodes \
-                                                       if node.operation == "merge"]
-                                               if merge_nodes:
-                                                       selected_nodes = merge_nodes
-                                               else:
+                                               # If there is a mix of uninstall nodes with other
+                                               # types, save the uninstall nodes for later since
+                                               # sometimes a merge node will render an uninstall
+                                               # node unnecessary (due to occupying the same slot),
+                                               # and we want to avoid executing a separate uninstall
+                                               # task in that case.
+                                               if len(nodes) > 1:
+                                                       non_uninstalls = [node for node in nodes \
+                                                               if node.operation != "uninstall"]
+                                                       if non_uninstalls:
+                                                               nodes = non_uninstalls
+                                                       else:
+                                                               nodes = nodes
+
+                                               if ignore_priority is None and not tree_mode:
+                                                       # Greedily pop all of these nodes since no
+                                                       # relationship has been ignored. This optimization
+                                                       # destroys --tree output, so it's disabled in tree
+                                                       # mode.
                                                        selected_nodes = nodes
-                                       else:
-                                               # For optimal merge order:
-                                               #  * Only pop one node.
-                                               #  * Removing a root node (node without a parent)
-                                               #    will not produce a leaf node, so avoid it.
-                                               for node in nodes:
-                                                       if mygraph.parent_nodes(node):
-                                                               # found a non-root node
-                                                               selected_nodes = [node]
-                                                               break
-                                               if not selected_nodes and \
-                                                       (accept_root_node or ignore_priority is None):
-                                                       # settle for a root node
-                                                       selected_nodes = [nodes[0]]
+                                               else:
+                                                       # For optimal merge order:
+                                                       #  * Only pop one node.
+                                                       #  * Removing a root node (node without a parent)
+                                                       #    will not produce a leaf node, so avoid it.
+                                                       for node in nodes:
+                                                               if mygraph.parent_nodes(node):
+                                                                       # found a non-root node
+                                                                       selected_nodes = [node]
+                                                                       break
+                                                       if not selected_nodes and \
+                                                               (accept_root_node or ignore_priority is None):
+                                                               # settle for a root node
+                                                               selected_nodes = [nodes[0]]
+                                               if selected_nodes:
+                                                       break
 
                        if not selected_nodes:
                                nodes = get_nodes(ignore_priority=DepPriority.MEDIUM)
@@ -7046,7 +7055,7 @@ class depgraph(object):
                        if selected_nodes and len(selected_nodes) > 1:
                                if not isinstance(selected_nodes, list):
                                        selected_nodes = list(selected_nodes)
-                               selected_nodes.sort(cmp_circular_bias)
+                               selected_nodes.sort(key=cmp_sort_key(cmp_circular_bias))
 
                        if not selected_nodes and not myblocker_uninstalls.is_empty():
                                # An Uninstall task needs to be executed in order to
@@ -9811,6 +9820,22 @@ class Scheduler(PollScheduler):
                for k in self._task_queues.allowed_keys:
                        setattr(self._task_queues, k,
                                SequentialTaskQueue())
+
+               # Holds merges that will wait to be executed when no builds are
+               # executing. This is useful for system packages since dependencies
+               # on system packages are frequently unspecified.
+               self._merge_wait_queue = []
+               # Holds merges that have been transfered from the merge_wait_queue to
+               # the actual merge queue. They are removed from this list upon
+               # completion. Other packages can start building only when this list is
+               # empty.
+               self._merge_wait_scheduled = []
+
+               # Holds system packages and their deep runtime dependencies. Before
+               # being merged, these packages go to merge_wait_queue, to be merged
+               # when no other packages are building.
+               self._deep_system_deps = set()
+
                self._status_display = JobStatusDisplay()
                self._max_load = myopts.get("--load-average")
                max_jobs = myopts.get("--jobs")
@@ -9985,9 +10010,51 @@ class Scheduler(PollScheduler):
                        return
 
                self._digraph = digraph
+               self._find_system_deps()
                self._prune_digraph()
                self._prevent_builddir_collisions()
 
+       def _find_system_deps(self):
+               """
+               Find system packages and their deep runtime dependencies. Before being
+               merged, these packages go to merge_wait_queue, to be merged when no
+               other packages are building.
+               """
+               graph = self._digraph
+               deep_system_deps = self._deep_system_deps
+               deep_system_deps.clear()
+               node_stack = []
+               for node in graph.order:
+                       if not isinstance(node, Package) or \
+                               node.operation == "uninstall":
+                               continue
+                       system_set = node.root_config.sets["system"]
+                       if system_set.findAtomForPackage(node):
+                               node_stack.append(node)
+
+               def ignore_priority(priority):
+                       """
+                       Ignore non-runtime priorities.
+                       """
+                       if isinstance(priority, DepPriority) and \
+                               (priority.runtime or priority.runtime_post):
+                               return False
+                       return True
+
+               while node_stack:
+                       node = node_stack.pop()
+                       if node in deep_system_deps:
+                               continue
+                       deep_system_deps.add(node)
+                       for child in graph.child_nodes(node, ignore_priority=ignore_priority):
+                               if not isinstance(child, Package) or \
+                                       child.operation == "uninstall":
+                                       continue
+                               node_stack.append(child)
+
+               deep_system_deps.difference_update([pkg for pkg in \
+                       deep_system_deps if pkg.operation != "merge"])
+
        def _prune_digraph(self):
                """
                Prune any root nodes that are irrelevant.
@@ -10538,6 +10605,10 @@ class Scheduler(PollScheduler):
                        elif isinstance(pkg, Blocker):
                                pass
 
+       def _merge_wait_exit_handler(self, task):
+               self._merge_wait_scheduled.remove(task)
+               self._merge_exit(task)
+
        def _merge_exit(self, merge):
                self._do_merge_exit(merge)
                self._deallocate_config(merge.merge.settings)
@@ -10590,9 +10661,15 @@ class Scheduler(PollScheduler):
                if build.returncode == os.EX_OK:
                        self.curval += 1
                        merge = PackageMerge(merge=build)
-                       merge.addExitListener(self._merge_exit)
-                       self._task_queues.merge.add(merge)
-                       self._status_display.merges = len(self._task_queues.merge)
+                       if not build.build_opts.buildpkgonly and \
+                               build.pkg in self._deep_system_deps:
+                               # Since dependencies on system packages are frequently
+                               # unspecified, merge them only when no builds are executing.
+                               self._merge_wait_queue.append(merge)
+                       else:
+                               merge.addExitListener(self._merge_exit)
+                               self._task_queues.merge.add(merge)
+                               self._status_display.merges = len(self._task_queues.merge)
                else:
                        settings = build.settings
                        build_dir = settings.get("PORTAGE_BUILDDIR")
@@ -10641,6 +10718,7 @@ class Scheduler(PollScheduler):
        def _main_loop_cleanup(self):
                del self._pkg_queue[:]
                self._completed_tasks.clear()
+               self._deep_system_deps.clear()
                self._choose_pkg_return_early = False
                self._status_display.reset()
                self._digraph = None
@@ -10770,6 +10848,16 @@ class Scheduler(PollScheduler):
                        not (self._failed_pkgs and not self._build_opts.fetchonly))
 
        def _schedule_tasks(self):
+
+               # When the number of jobs drops to zero, process all waiting merges.
+               if not self._jobs and self._merge_wait_queue:
+                       for task in self._merge_wait_queue:
+                               task.addExitListener(self._merge_wait_exit_handler)
+                               self._task_queues.merge.add(task)
+                       self._status_display.merges = len(self._task_queues.merge)
+                       self._merge_wait_scheduled.extend(self._merge_wait_queue)
+                       del self._merge_wait_queue[:]
+
                self._schedule_tasks_imp()
                self._status_display.display()
 
@@ -10824,6 +10912,7 @@ class Scheduler(PollScheduler):
                                return bool(state_change)
 
                        if self._choose_pkg_return_early or \
+                               self._merge_wait_scheduled or \
                                not self._can_add_job() or \
                                self._job_delay():
                                return bool(state_change)
@@ -11150,6 +11239,8 @@ class MetadataRegen(PollScheduler):
 
                self._valid_pkgs = set()
                self._process_iter = self._iter_metadata_processes()
+               self.returncode = os.EX_OK
+               self._error_count = 0
 
        def _iter_metadata_processes(self):
                portdb = self._portdb
@@ -11227,6 +11318,8 @@ class MetadataRegen(PollScheduler):
        def _metadata_exit(self, metadata_process):
                self._jobs -= 1
                if metadata_process.returncode != os.EX_OK:
+                       self.returncode = 1
+                       self._error_count += 1
                        self._valid_pkgs.discard(metadata_process.cpv)
                        portage.writemsg("Error processing %s, continuing...\n" % \
                                (metadata_process.cpv,))
@@ -11675,7 +11768,7 @@ def unmerge(root_config, myopts, unmerge_action,
                                writemsg_level((mytype + ": ").rjust(14), noiselevel=-1)
                        if pkgmap[x][mytype]:
                                sorted_pkgs = [portage.catpkgsplit(mypkg)[1:] for mypkg in pkgmap[x][mytype]]
-                               sorted_pkgs.sort(portage.pkgcmp)
+                               sorted_pkgs.sort(key=cmp_sort_key(portage.pkgcmp))
                                for pn, ver, rev in sorted_pkgs:
                                        if rev == "r0":
                                                myversion = ver
@@ -12925,6 +13018,7 @@ def action_regen(settings, portdb, max_jobs, max_load):
        regen.run()
 
        portage.writemsg_stdout("done!\n")
+       return regen.returncode
 
 def action_config(settings, trees, myopts, myfiles):
        if len(myfiles) != 1:
@@ -13035,7 +13129,7 @@ def action_info(settings, trees, myopts, myfiles):
                if portage.isvalidatom(x):
                        pkg_matches = trees["/"]["vartree"].dbapi.match(x)
                        pkg_matches = [portage.catpkgsplit(cpv)[1:] for cpv in pkg_matches]
-                       pkg_matches.sort(portage.pkgcmp)
+                       pkg_matches.sort(key=cmp_sort_key(portage.pkgcmp))
                        pkgs = []
                        for pn, ver, rev in pkg_matches:
                                if rev != "r0":
@@ -13763,7 +13857,7 @@ def action_depclean(settings, trees, ldpath_mtimes,
                                node_refcounts[node] = len(graph.parent_nodes(node))
                        def cmp_reference_count(node1, node2):
                                return node_refcounts[node1] - node_refcounts[node2]
-                       graph.order.sort(cmp_reference_count)
+                       graph.order.sort(key=cmp_sort_key(cmp_reference_count))
        
                        ignore_priority_range = [None]
                        ignore_priority_range.extend(
@@ -15249,7 +15343,7 @@ def emerge_main():
                action_metadata(settings, portdb, myopts)
        elif myaction=="regen":
                validate_ebuild_environment(trees)
-               action_regen(settings, portdb, myopts.get("--jobs"),
+               return action_regen(settings, portdb, myopts.get("--jobs"),
                        myopts.get("--load-average"))
        # HELP action
        elif "config"==myaction:
index c9782f2944930cb07a0a1659720a261ab285e1c9..4ea43517169da464c161df97ac9fac192e0b63c0 100644 (file)
@@ -369,18 +369,14 @@ class digraph(object):
                if parent not in self.nodes:
                        self.nodes[parent] = ({}, {}, parent)
                        self.order.append(parent)
-               
-               if parent in self.nodes[node][1]:
-                       if priority > self.nodes[node][1][parent]:
-                               self.nodes[node][1][parent] = priority
-               else:
-                       self.nodes[node][1][parent] = priority
-               
-               if node in self.nodes[parent][0]:
-                       if priority > self.nodes[parent][0][node]:
-                               self.nodes[parent][0][node] = priority
-               else:
-                       self.nodes[parent][0][node] = priority
+
+               priorities = self.nodes[node][1].setdefault(parent, [])
+               priorities.append(priority)
+               priorities.sort()
+
+               priorities = self.nodes[parent][0].setdefault(node, [])
+               priorities.append(priority)
+               priorities.sort()
 
        def remove(self, node):
                """Removes the specified node from the digraph, also removing
@@ -462,9 +458,16 @@ class digraph(object):
                if ignore_priority is None:
                        return list(self.nodes[node][0])
                children = []
-               for child, priority in self.nodes[node][0].iteritems():
-                       if priority > ignore_priority:
-                               children.append(child)
+               if hasattr(ignore_priority, '__call__'):
+                       for child, priorities in self.nodes[node][0].iteritems():
+                               for priority in priorities:
+                                       if not ignore_priority(priority):
+                                               children.append(child)
+                                               break
+               else:
+                       for child, priorities in self.nodes[node][0].iteritems():
+                               if ignore_priority < priorities[-1]:
+                                       children.append(child)
                return children
 
        def parent_nodes(self, node, ignore_priority=None):
@@ -472,9 +475,16 @@ class digraph(object):
                if ignore_priority is None:
                        return list(self.nodes[node][1])
                parents = []
-               for parent, priority in self.nodes[node][1].iteritems():
-                       if priority > ignore_priority:
-                               parents.append(parent)
+               if hasattr(ignore_priority, '__call__'):
+                       for parent, priorities in self.nodes[node][1].iteritems():
+                               for priority in priorities:
+                                       if not ignore_priority(priority):
+                                               parents.append(parent)
+                                               break
+               else:
+                       for parent, priorities in self.nodes[node][1].iteritems():
+                               if ignore_priority < priorities[-1]:
+                                       parents.append(parent)
                return parents
 
        def leaf_nodes(self, ignore_priority=None):
@@ -484,14 +494,31 @@ class digraph(object):
                children in calculations."""
                
                leaf_nodes = []
-               for node in self.order:
-                       is_leaf_node = True
-                       for child in self.nodes[node][0]:
-                               if self.nodes[node][0][child] > ignore_priority:
-                                       is_leaf_node = False
-                                       break
-                       if is_leaf_node:
-                               leaf_nodes.append(node)
+               if ignore_priority is None:
+                       for node in self.order:
+                               if not self.nodes[node][0]:
+                                       leaf_nodes.append(node)
+               elif hasattr(ignore_priority, '__call__'):
+                       for node in self.order:
+                               is_leaf_node = True
+                               for child, priorities in self.nodes[node][0].iteritems():
+                                       for priority in priorities:
+                                               if not ignore_priority(priority):
+                                                       is_leaf_node = False
+                                                       break
+                                       if not is_leaf_node:
+                                               break
+                               if is_leaf_node:
+                                       leaf_nodes.append(node)
+               else:
+                       for node in self.order:
+                               is_leaf_node = True
+                               for child, priorities in self.nodes[node][0].iteritems():
+                                       if ignore_priority < priorities[-1]:
+                                               is_leaf_node = False
+                                               break
+                               if is_leaf_node:
+                                       leaf_nodes.append(node)
                return leaf_nodes
 
        def root_nodes(self, ignore_priority=None):
@@ -501,14 +528,31 @@ class digraph(object):
                parents in calculations."""
                
                root_nodes = []
-               for node in self.order:
-                       is_root_node = True
-                       for parent in self.nodes[node][1]:
-                               if self.nodes[node][1][parent] > ignore_priority:
-                                       is_root_node = False
-                                       break
-                       if is_root_node:
-                               root_nodes.append(node)
+               if ignore_priority is None:
+                       for node in self.order:
+                               if not self.nodes[node][1]:
+                                       root_nodes.append(node)
+               elif hasattr(ignore_priority, '__call__'):
+                       for node in self.order:
+                               is_root_node = True
+                               for parent, priorities in self.nodes[node][1].iteritems():
+                                       for priority in priorities:
+                                               if not ignore_priority(priority):
+                                                       is_root_node = False
+                                                       break
+                                       if not is_root_node:
+                                               break
+                               if is_root_node:
+                                       root_nodes.append(node)
+               else:
+                       for node in self.order:
+                               is_root_node = True
+                               for parent, priorities in self.nodes[node][1].iteritems():
+                                       if ignore_priority < priorities[-1]:
+                                               is_root_node = False
+                                               break
+                               if is_root_node:
+                                       root_nodes.append(node)
                return root_nodes
 
        def is_empty(self):
@@ -557,9 +601,8 @@ class digraph(object):
                                output("depends on\n")
                        else:
                                output("(no children)\n")
-                       for child in self.nodes[node][0]:
-                               output("  %s (%s)\n" % \
-                                       (child, self.nodes[node][0][child],))
+                       for child, priorities in self.nodes[node][0].iteritems():
+                               output("  %s (%s)\n" % (child, priorities[-1],))
 
 #parse /etc/env.d and generate /etc/profile.env
 
index cdae94573307c5b3abbe2d0bcbf63504ccb80d8e..e5db363d7970f37bb70027d5e9b5f88ae496ca9e 100644 (file)
@@ -9,7 +9,7 @@ import re
 from portage.dep import match_from_list
 from portage.locks import unlockfile
 from portage.output import colorize
-from portage.util import writemsg
+from portage.util import writemsg, cmp_sort_key
 from portage import auxdbkeys, dep_expand
 from portage.versions import catpkgsplit, pkgcmp
 
@@ -62,7 +62,7 @@ class dbapi(object):
                                mysplit = tuple(catpkgsplit(cpv)[1:])
                                str_map[mysplit] = cpv
                                cpv_list[i] = mysplit
-                       cpv_list.sort(pkgcmp)
+                       cpv_list.sort(key=cmp_sort_key(pkgcmp))
                        for i, mysplit in enumerate(cpv_list):
                                cpv_list[i] = str_map[mysplit]
 
index 384da250a13ee9239fce83a3c35b4c0825eb8dbc..9329c889b3f827997b8babf4701c17e4880043f5 100644 (file)
@@ -66,6 +66,17 @@ class Manifest2Entry(ManifestEntry):
                        myline += " " + h + " " + str(self.hashes[h])
                return myline
 
+       def __eq__(self, other):
+               if not isinstance(other, Manifest2Entry) or \
+                       self.type != other.type or \
+                       self.name != other.name or \
+                       self.hashes != other.hashes:
+                       return False
+               return True
+
+       def __ne__(self, other):
+               return not self.__eq__(other)
+
 class Manifest(object):
        parsers = (parseManifest2,)
        def __init__(self, pkgdir, distdir, fetchlist_dict=None,
@@ -291,7 +302,9 @@ class Manifest(object):
                for pkgdir, pkgdir_dirs, pkgdir_files in os.walk(self.pkgdir):
                        break
                for f in pkgdir_files:
-                       if f.endswith(".ebuild"):
+                       if f[:1] == ".":
+                               continue
+                       elif f[-7:] == ".ebuild":
                                mytype = "EBUILD"
                                pf = f[:-7]
                                ps = portage.versions.pkgsplit(pf)
index eef9cf12fea725c1d345d2cdec1e07200eb26d3b..e2a32b0aa5b293c3369f4bdee6678fce066d300c 100644 (file)
@@ -606,6 +606,42 @@ def dump_traceback(msg, noiselevel=1):
                writemsg(error+"\n", noiselevel=noiselevel)
        writemsg("====================================\n\n", noiselevel=noiselevel)
 
+class cmp_sort_key(object):
+       """
+       In python-3.0 the list.sort() method no longer has a "cmp" keyword
+       argument. This class acts as an adapter which converts a cmp function
+       into one that's suitable for use as the "key" keyword argument to
+       list.sort(), making it easier to port code for python-3.0 compatibility.
+       It works by generating key objects which use the given cmp function to
+       implement their __lt__ method.
+       """
+       __slots__ = ("_cmp_func",)
+
+       def __init__(self, cmp_func):
+               """
+               @type cmp_func: callable which takes 2 positional arguments
+               @param cmp_func: A cmp function.
+               """
+               self._cmp_func = cmp_func
+
+       def __call__(self, lhs):
+               return self._cmp_key(self._cmp_func, lhs)
+
+       class _cmp_key(object):
+               __slots__ = ("_cmp_func", "_obj")
+
+               def __init__(self, cmp_func, obj):
+                       self._cmp_func = cmp_func
+                       self._obj = obj
+
+               def __lt__(self, other):
+                       if not isinstance(other, self.__class__):
+                               raise TypeError("Expected type %s, got %s" % \
+                                       (self.__class__, other.__class__))
+                       if self._cmp_func(self._obj, other._obj) < 0:
+                               return True
+                       return False
+
 def unique_array(s):
        """lifted from python cookbook, credit: Tim Peters
        Return a list of the elements in s in arbitrary order, sans duplicates"""
index c9ef91cfc91d9df7a92259d2367e413bbabf2bfa..e89020980a6d85a262bb59c1b7a38ab18a76b17e 100644 (file)
@@ -328,13 +328,25 @@ class DeprecatedBindnowFlags(LineCheck):
        re = re.compile(r'.*\$\(bindnow-flags\)')
        error = errors.DEPRECATED_BINDNOW_FLAGS
 
+class WantAutoDefaultValue(LineCheck):
+       """Check setting WANT_AUTO* to latest (default value)."""
+       repoman_check_name = 'ebuild.minorsyn'
+       _re = re.compile(r'^WANT_AUTO(CONF|MAKE)=(\'|")?latest')
+
+       def check(self, num, line):
+               m = self._re.match(line)
+               if m is not None:
+                       return 'WANT_AUTO' + m.group(1) + \
+                               ' redundantly set to default value "latest" on line: %d'
+
 _constant_checks = tuple((c() for c in (
        EbuildHeader, EbuildWhitespace, EbuildQuote,
        EbuildAssignment, EbuildUselessDodoc,
        EbuildUselessCdS, EbuildNestedDie,
        EbuildPatches, EbuildQuotedA,
        IUseUndefined, ImplicitRuntimeDeps, InheritAutotools,
-       EMakeParallelDisabled, DeprecatedBindnowFlags)))
+       EMakeParallelDisabled, DeprecatedBindnowFlags,
+       WantAutoDefaultValue)))
 
 def run_checks(contents, pkg):
        checks = _constant_checks