Fix cycle cracking once and for all.

While testing parallel_emerge, I found more cases where it would fail
to crack cycles. The issue is that parallel_emerge simply cracks the first
cycle it finds that involves an edge, rather than all cycles that involve an
edge.

Finding all cycles that involve an edge without looping forever is a rather
difficult problem, so I've updated the algorithm to simply keep searching
until it runs out of cycles. Testing this, I haven't found any cases where
the performance of the cycle cracking is a problem. I updated the algorithm
to log its performance stats so we can track this.

TEST=emerge -ep portage
BUG=none

Change-Id: I1cb95ffe9d977b9f8d38626d2d6cdbb766c14669

Review URL: http://codereview.chromium.org/3340010
This commit is contained in:
David James 2010-09-07 16:57:56 -07:00
parent ffede3b414
commit 0ac3ac5aa5

View File

@ -829,7 +829,7 @@ class DepGraphGenerator(object):
depinfo = depinfo + ", deleting"
print " %s -> %s (%s)" % (pkg1, pkg2, depinfo)
def SanitizeTree(cycles):
def SanitizeTree():
"""Remove circular dependencies.
We prune all dependencies involved in cycles that go against the emerge
@ -839,18 +839,20 @@ class DepGraphGenerator(object):
Because we don't treat any dependencies as "soft" unless they're killed
by a cycle, we pay attention to a larger number of dependencies when
merging. This hurts performance a bit, but helps reliability.
Args:
cycles: Dict of packages involved in cyclic dependencies, mapping each
package to a list of the cycles the package is involved in. Produced
by FindCycles().
"""
for dep, mycycles in cycles.iteritems():
for basedep, mycycle in mycycles.iteritems():
if deps_info[basedep]["idx"] >= deps_info[dep]["idx"]:
PrintCycleBreak(basedep, dep, mycycle)
del deps_map[dep]["needs"][basedep]
deps_map[basedep]["provides"].remove(dep)
start = time.time()
cycles = FindCycles()
while cycles:
for dep, mycycles in cycles.iteritems():
for basedep, mycycle in mycycles.iteritems():
if deps_info[basedep]["idx"] >= deps_info[dep]["idx"]:
PrintCycleBreak(basedep, dep, mycycle)
del deps_map[dep]["needs"][basedep]
deps_map[basedep]["provides"].remove(dep)
cycles = FindCycles()
seconds = time.time() - start
if "--quiet" not in emerge.opts and seconds >= 0.1:
print "Tree sanitized in %dm%.1fs" % (seconds / 60, seconds % 60)
def AddSecretDeps():
"""Find these tagged packages and add extra dependencies.
@ -1129,8 +1131,7 @@ class DepGraphGenerator(object):
# we've done that, we also need to recalculate our list of cycles so that
# we don't include the installed packages in our cycles.
RemoveInstalledPackages()
cycles = FindCycles()
SanitizeTree(cycles)
SanitizeTree()
if deps_map:
if "--usepkg" in emerge.opts:
UsePrebuiltPackages()