mirror of
https://github.com/flatcar/scripts.git
synced 2025-09-24 23:21:17 +02:00
Cleanup cycle cracking in parallel_emerge.
I've cleaned up the cycle cracking in parallel_emerge to hopefully be significantly easier to understand. This also fixes some bugs. Most significantly, this patch restores the dependency cracking so that it actually cracks cycles that are against the emerge ordering, instead of cracking everything but those dependencies. This was only broken temporarily due to my fix for Bug 5795. Oops. Fortunately, this bug doesn't affect many people, because make_chroot --fast is deprecated anyway. TEST=make_chroot --fast, parallel_emerge -pe hard-host-depends BUG=chromium-os:5795 Review URL: http://codereview.chromium.org/3184011
This commit is contained in:
parent
64541ede55
commit
ddc372597f
@ -642,39 +642,44 @@ class DepGraphGenerator(object):
|
|||||||
"""Find cycles in the dependency tree.
|
"""Find cycles in the dependency tree.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict of packages involved in cyclic dependencies, mapping each package
|
A dict mapping cyclic packages to a dict of the deps that cause
|
||||||
to a list of the cycles the package is involved in.
|
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.
|
"""Find cycles in cyclic dependencies starting at specified package.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
pkg: Package identifier.
|
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.
|
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)
|
unresolved.append(pkg)
|
||||||
mycycles = cycles.get(pkg)
|
|
||||||
if mycycles:
|
|
||||||
mycycles = mycycles.get("pkgs")
|
|
||||||
for dep in deps_map[pkg]["needs"]:
|
for dep in deps_map[pkg]["needs"]:
|
||||||
if mycycles and dep in mycycles:
|
if dep in unresolved:
|
||||||
continue
|
|
||||||
elif dep in unresolved:
|
|
||||||
idx = unresolved.index(dep)
|
idx = unresolved.index(dep)
|
||||||
mycycle = unresolved[idx:] + [dep]
|
mycycle = unresolved[idx:] + [dep]
|
||||||
for cycle_pkg in mycycle:
|
for i in range(len(mycycle) - 1):
|
||||||
info = cycles.setdefault(cycle_pkg, {})
|
pkg1, pkg2 = mycycle[i], mycycle[i+1]
|
||||||
info.setdefault("pkgs", set()).update(mycycle)
|
cycles.setdefault(pkg1, {}).setdefault(pkg2, mycycle)
|
||||||
info.setdefault("cycles", []).append(mycycle)
|
elif not pkg_cycles or dep not in pkg_cycles:
|
||||||
else:
|
# Looks like we haven't seen this edge before.
|
||||||
FindCyclesAtNode(dep, cycles, unresolved)
|
FindCyclesAtNode(dep, cycles, unresolved, resolved)
|
||||||
unresolved.pop()
|
unresolved.pop()
|
||||||
|
resolved.add(pkg)
|
||||||
|
|
||||||
cycles, unresolved = {}, []
|
cycles, unresolved, resolved = {}, [], set()
|
||||||
for pkg in deps_map:
|
for pkg in deps_map:
|
||||||
FindCyclesAtNode(pkg, cycles, unresolved)
|
FindCyclesAtNode(pkg, cycles, unresolved, resolved)
|
||||||
return cycles
|
return cycles
|
||||||
|
|
||||||
def RemoveInstalledPackages():
|
def RemoveInstalledPackages():
|
||||||
@ -736,6 +741,31 @@ class DepGraphGenerator(object):
|
|||||||
target_needs.pop(target, None)
|
target_needs.pop(target, None)
|
||||||
del deps_map[pkg]
|
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):
|
def SanitizeTree(cycles):
|
||||||
"""Remove circular dependencies.
|
"""Remove circular dependencies.
|
||||||
|
|
||||||
@ -752,25 +782,12 @@ class DepGraphGenerator(object):
|
|||||||
package to a list of the cycles the package is involved in. Produced
|
package to a list of the cycles the package is involved in. Produced
|
||||||
by FindCycles().
|
by FindCycles().
|
||||||
"""
|
"""
|
||||||
for basedep, cycle_info in cycles.iteritems():
|
for dep, mycycles in cycles.iteritems():
|
||||||
for mycycle in cycle_info["cycles"]:
|
for basedep, mycycle in mycycles.iteritems():
|
||||||
info = []
|
if deps_info[basedep]["idx"] >= deps_info[dep]["idx"]:
|
||||||
broken = False
|
PrintCycleBreak(basedep, dep, mycycle)
|
||||||
for i in range(len(mycycle) - 1):
|
del deps_map[dep]["needs"][basedep]
|
||||||
pkg1, pkg2 = mycycle[i], mycycle[i+1]
|
deps_map[basedep]["provides"].remove(dep)
|
||||||
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)
|
|
||||||
|
|
||||||
def AddSecretDeps():
|
def AddSecretDeps():
|
||||||
"""Find these tagged packages and add extra dependencies.
|
"""Find these tagged packages and add extra dependencies.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user