Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(821)

Unified Diff: parallel_emerge

Issue 3184011: Cleanup cycle cracking in parallel_emerge. (Closed) Base URL: ssh://git@chromiumos-git/crosutils.git
Patch Set: No changes (hopefully) Created 10 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: parallel_emerge
diff --git a/parallel_emerge b/parallel_emerge
index 7e88516c0180c73f530ecf5002bb50f3c593c912..f9576f6e7568fb11159232fa2ce9add9971e5293 100755
--- a/parallel_emerge
+++ b/parallel_emerge
@@ -642,39 +642,44 @@ class DepGraphGenerator(object):
"""Find cycles in the dependency tree.
Returns:
- Dict of packages involved in cyclic dependencies, mapping each package
- to a list of the cycles the package is involved in.
+ A dict mapping cyclic packages to a dict of the deps that cause
+ cycles. For each dep that causes cycles, it returns an example
+ traversal of the graph that shows the cycle.
"""
- def FindCyclesAtNode(pkg, cycles, unresolved):
+ def FindCyclesAtNode(pkg, cycles, unresolved, resolved):
"""Find cycles in cyclic dependencies starting at specified package.
Args:
pkg: Package identifier.
- cycles: Set of cycles so far.
+ cycles: A dict mapping cyclic packages to a dict of the deps that
+ cause cycles. For each dep that causes cycles, it returns an
+ example traversal of the graph that shows the cycle.
unresolved: Nodes that have been visited but are not fully processed.
+ resolved: Nodes that have been visited and are fully processed.
"""
+ pkg_cycles = cycles.get(pkg)
+ if pkg in resolved and not pkg_cycles:
+ # If we already looked at this package, and found no cyclic
+ # dependencies, we can stop now.
+ return
unresolved.append(pkg)
- mycycles = cycles.get(pkg)
- if mycycles:
- mycycles = mycycles.get("pkgs")
for dep in deps_map[pkg]["needs"]:
- if mycycles and dep in mycycles:
- continue
- elif dep in unresolved:
+ if dep in unresolved:
idx = unresolved.index(dep)
mycycle = unresolved[idx:] + [dep]
- for cycle_pkg in mycycle:
- info = cycles.setdefault(cycle_pkg, {})
- info.setdefault("pkgs", set()).update(mycycle)
- info.setdefault("cycles", []).append(mycycle)
- else:
- FindCyclesAtNode(dep, cycles, unresolved)
+ for i in range(len(mycycle) - 1):
+ pkg1, pkg2 = mycycle[i], mycycle[i+1]
+ cycles.setdefault(pkg1, {}).setdefault(pkg2, mycycle)
+ elif not pkg_cycles or dep not in pkg_cycles:
+ # Looks like we haven't seen this edge before.
+ FindCyclesAtNode(dep, cycles, unresolved, resolved)
unresolved.pop()
+ resolved.add(pkg)
- cycles, unresolved = {}, []
+ cycles, unresolved, resolved = {}, [], set()
for pkg in deps_map:
- FindCyclesAtNode(pkg, cycles, unresolved)
+ FindCyclesAtNode(pkg, cycles, unresolved, resolved)
return cycles
def RemoveInstalledPackages():
@@ -736,6 +741,31 @@ class DepGraphGenerator(object):
target_needs.pop(target, None)
del deps_map[pkg]
+ def PrintCycleBreak(basedep, dep, mycycle):
+ """Print details about a cycle that we are planning on breaking.
+
+ We are breaking a cycle where dep needs basedep. mycycle is an
+ example cycle which contains dep -> basedep."""
+
+ # If it's an optional dependency, there's no need to spam the user with
+ # warning messages.
+ needs = deps_map[dep]["needs"]
+ depinfo = needs.get(basedep, "deleted")
+ if depinfo == "optional":
+ return
+
+ # Notify the user that we're breaking a cycle.
+ print "Breaking %s -> %s (%s)" % (dep, basedep, depinfo)
+
+ # Show cycle.
+ for i in range(len(mycycle) - 1):
+ pkg1, pkg2 = mycycle[i], mycycle[i+1]
+ needs = deps_map[pkg1]["needs"]
+ depinfo = needs.get(pkg2, "deleted")
+ if pkg1 == dep and pkg2 == basedep:
+ depinfo = depinfo + ", deleting"
+ print " %s -> %s (%s)" % (pkg1, pkg2, depinfo)
+
def SanitizeTree(cycles):
"""Remove circular dependencies.
@@ -752,25 +782,12 @@ class DepGraphGenerator(object):
package to a list of the cycles the package is involved in. Produced
by FindCycles().
"""
- for basedep, cycle_info in cycles.iteritems():
- for mycycle in cycle_info["cycles"]:
- info = []
- broken = False
- for i in range(len(mycycle) - 1):
- pkg1, pkg2 = mycycle[i], mycycle[i+1]
- needs = deps_map[pkg1]["needs"]
- depinfo = needs.get(pkg2, "deleted")
- bad = False
- if (deps_info[pkg1]["idx"] >= deps_info[pkg2]["idx"] and
- depinfo != "deleted"):
- depinfo = depinfo + ", deleting"
- broken = True
- del deps_map[pkg1]["needs"][pkg2]
- deps_map[pkg2]["provides"].remove(pkg1)
- info.append(" %s -> %s (%s)" % (pkg1, pkg2, depinfo))
- if broken:
- print "Breaking cycle:"
- print "\n".join(info)
+ 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)
def AddSecretDeps():
"""Find these tagged packages and add extra dependencies.
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698