| OLD | NEW |
| 1 #!/usr/bin/python2.6 | 1 #!/usr/bin/python2.6 |
| 2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Program to run emerge in parallel, for significant speedup. | 6 """Program to run emerge in parallel, for significant speedup. |
| 7 | 7 |
| 8 Usage: | 8 Usage: |
| 9 ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps] | 9 ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps] |
| 10 [emerge args] package" | 10 [emerge args] package" |
| (...skipping 628 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 639 final_pkgs.add(str(match.cpv)) | 639 final_pkgs.add(str(match.cpv)) |
| 640 | 640 |
| 641 def FindCycles(): | 641 def FindCycles(): |
| 642 """Find cycles in the dependency tree. | 642 """Find cycles in the dependency tree. |
| 643 | 643 |
| 644 Returns: | 644 Returns: |
| 645 Dict of packages involved in cyclic dependencies, mapping each package | 645 Dict of packages involved in cyclic dependencies, mapping each package |
| 646 to a list of the cycles the package is involved in. | 646 to a list of the cycles the package is involved in. |
| 647 """ | 647 """ |
| 648 | 648 |
| 649 def FindCyclesAtNode(pkg, cycles, unresolved, resolved): | 649 def FindCyclesAtNode(pkg, cycles, unresolved): |
| 650 """Find cycles in cyclic dependencies starting at specified package. | 650 """Find cycles in cyclic dependencies starting at specified package. |
| 651 | 651 |
| 652 Args: | 652 Args: |
| 653 pkg: Package identifier. | 653 pkg: Package identifier. |
| 654 cycles: Set of cycles so far. | 654 cycles: Set of cycles so far. |
| 655 unresolved: Nodes that have been visited but are not fully processed. | 655 unresolved: Nodes that have been visited but are not fully processed. |
| 656 resolved: Nodes that have been visited and are fully processed. | |
| 657 Returns: | |
| 658 Whether a cycle was found. | |
| 659 """ | 656 """ |
| 660 if pkg in resolved: | |
| 661 return | |
| 662 unresolved.append(pkg) | 657 unresolved.append(pkg) |
| 658 mycycles = cycles.get(pkg) |
| 659 if mycycles: |
| 660 mycycles = mycycles.get("pkgs") |
| 663 for dep in deps_map[pkg]["needs"]: | 661 for dep in deps_map[pkg]["needs"]: |
| 664 if dep in unresolved: | 662 if mycycles and dep in mycycles: |
| 663 continue |
| 664 elif dep in unresolved: |
| 665 idx = unresolved.index(dep) | 665 idx = unresolved.index(dep) |
| 666 mycycle = unresolved[idx:] + [dep] | 666 mycycle = unresolved[idx:] + [dep] |
| 667 for cycle_pkg in mycycle: | 667 for cycle_pkg in mycycle: |
| 668 info = cycles.setdefault(cycle_pkg, {}) | 668 info = cycles.setdefault(cycle_pkg, {}) |
| 669 info.setdefault("pkgs", set()).update(mycycle) | 669 info.setdefault("pkgs", set()).update(mycycle) |
| 670 info.setdefault("cycles", []).append(mycycle) | 670 info.setdefault("cycles", []).append(mycycle) |
| 671 else: | 671 else: |
| 672 FindCyclesAtNode(dep, cycles, unresolved, resolved) | 672 FindCyclesAtNode(dep, cycles, unresolved) |
| 673 unresolved.pop() | 673 unresolved.pop() |
| 674 resolved.add(pkg) | |
| 675 | 674 |
| 676 cycles, unresolved, resolved = {}, [], set() | 675 cycles, unresolved = {}, [] |
| 677 for pkg in deps_map: | 676 for pkg in deps_map: |
| 678 FindCyclesAtNode(pkg, cycles, unresolved, resolved) | 677 FindCyclesAtNode(pkg, cycles, unresolved) |
| 679 return cycles | 678 return cycles |
| 680 | 679 |
| 681 def RemoveInstalledPackages(): | 680 def RemoveInstalledPackages(): |
| 682 """Remove installed packages, propagating dependencies.""" | 681 """Remove installed packages, propagating dependencies.""" |
| 683 | 682 |
| 684 # If we're in non-selective mode, the packages specified on the command | 683 # If we're in non-selective mode, the packages specified on the command |
| 685 # line are generally mandatory. | 684 # line are generally mandatory. |
| 686 # | 685 # |
| 687 # There are a few exceptions to this rule: | 686 # There are a few exceptions to this rule: |
| 688 # 1. If the package isn't getting installed because it's in | 687 # 1. If the package isn't getting installed because it's in |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 746 | 745 |
| 747 Because we don't treat any dependencies as "soft" unless they're killed | 746 Because we don't treat any dependencies as "soft" unless they're killed |
| 748 by a cycle, we pay attention to a larger number of dependencies when | 747 by a cycle, we pay attention to a larger number of dependencies when |
| 749 merging. This hurts performance a bit, but helps reliability. | 748 merging. This hurts performance a bit, but helps reliability. |
| 750 | 749 |
| 751 Args: | 750 Args: |
| 752 cycles: Dict of packages involved in cyclic dependencies, mapping each | 751 cycles: Dict of packages involved in cyclic dependencies, mapping each |
| 753 package to a list of the cycles the package is involved in. Produced | 752 package to a list of the cycles the package is involved in. Produced |
| 754 by FindCycles(). | 753 by FindCycles(). |
| 755 """ | 754 """ |
| 756 for basedep in set(cycles).intersection(deps_map): | 755 for basedep, cycle_info in cycles.iteritems(): |
| 757 this_pkg = deps_map[basedep] | 756 for mycycle in cycle_info["cycles"]: |
| 758 for dep in this_pkg["provides"].intersection(cycles[basedep]["pkgs"]): | 757 info = [] |
| 759 if deps_info[basedep]["idx"] >= deps_info[dep]["idx"]: | 758 broken = False |
| 760 for mycycle in cycles[basedep]["cycles"]: | 759 for i in range(len(mycycle) - 1): |
| 761 if dep in mycycle: | 760 pkg1, pkg2 = mycycle[i], mycycle[i+1] |
| 762 print "Breaking %s -> %s in cycle:" % (dep, basedep) | 761 needs = deps_map[pkg1]["needs"] |
| 763 for i in range(len(mycycle) - 1): | 762 depinfo = needs.get(pkg2, "deleted") |
| 764 needs = deps_map[mycycle[i]]["needs"] | 763 bad = False |
| 765 deptype = needs.get(mycycle[i+1], "deleted") | 764 if (deps_info[pkg1]["idx"] >= deps_info[pkg2]["idx"] and |
| 766 print " %s -> %s (%s)" % (mycycle[i], mycycle[i+1], deptype) | 765 depinfo != "deleted"): |
| 767 del deps_map[dep]["needs"][basedep] | 766 depinfo = depinfo + ", deleting" |
| 768 this_pkg["provides"].remove(dep) | 767 broken = True |
| 769 break | 768 del deps_map[pkg1]["needs"][pkg2] |
| 769 deps_map[pkg2]["provides"].remove(pkg1) |
| 770 info.append(" %s -> %s (%s)" % (pkg1, pkg2, depinfo)) |
| 771 if broken: |
| 772 print "Breaking cycle:" |
| 773 print "\n".join(info) |
| 770 | 774 |
| 771 def AddSecretDeps(): | 775 def AddSecretDeps(): |
| 772 """Find these tagged packages and add extra dependencies. | 776 """Find these tagged packages and add extra dependencies. |
| 773 | 777 |
| 774 For debugging dependency problems. | 778 For debugging dependency problems. |
| 775 """ | 779 """ |
| 776 for bad in secret_deps: | 780 for bad in secret_deps: |
| 777 needed = secret_deps[bad] | 781 needed = secret_deps[bad] |
| 778 bad_pkg = None | 782 bad_pkg = None |
| 779 needed_pkg = None | 783 needed_pkg = None |
| (...skipping 282 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1062 | 1066 |
| 1063 deps_map = copy.deepcopy(deps_map) | 1067 deps_map = copy.deepcopy(deps_map) |
| 1064 install_plan = [] | 1068 install_plan = [] |
| 1065 plan = set() | 1069 plan = set() |
| 1066 for target, info in deps_map.iteritems(): | 1070 for target, info in deps_map.iteritems(): |
| 1067 if not info["needs"] and target not in plan: | 1071 if not info["needs"] and target not in plan: |
| 1068 for item in InstallPlanAtNode(target, deps_map): | 1072 for item in InstallPlanAtNode(target, deps_map): |
| 1069 plan.add(item) | 1073 plan.add(item) |
| 1070 install_plan.append(self.package_db[item]) | 1074 install_plan.append(self.package_db[item]) |
| 1071 | 1075 |
| 1076 for pkg in plan: |
| 1077 del deps_map[pkg] |
| 1078 |
| 1079 if deps_map: |
| 1080 print "Cyclic dependencies:", " ".join(deps_map) |
| 1081 PrintDepsMap(deps_map) |
| 1082 sys.exit(1) |
| 1083 |
| 1072 self.emerge.depgraph.display(install_plan) | 1084 self.emerge.depgraph.display(install_plan) |
| 1073 | 1085 |
| 1074 | 1086 |
| 1075 def PrintDepsMap(deps_map): | 1087 def PrintDepsMap(deps_map): |
| 1076 """Print dependency graph, for each package list it's prerequisites.""" | 1088 """Print dependency graph, for each package list it's prerequisites.""" |
| 1077 for i in deps_map: | 1089 for i in sorted(deps_map): |
| 1078 print "%s: (%s) needs" % (i, deps_map[i]["action"]) | 1090 print "%s: (%s) needs" % (i, deps_map[i]["action"]) |
| 1079 needs = deps_map[i]["needs"] | 1091 needs = deps_map[i]["needs"] |
| 1080 for j in needs: | 1092 for j in sorted(needs): |
| 1081 print " %s" % (j) | 1093 print " %s" % (j) |
| 1082 if not needs: | 1094 if not needs: |
| 1083 print " no dependencies" | 1095 print " no dependencies" |
| 1084 | 1096 |
| 1085 | 1097 |
| 1086 class EmergeJobState(object): | 1098 class EmergeJobState(object): |
| 1087 __slots__ = ["done", "filename", "last_output_seek", "last_output_timestamp", | 1099 __slots__ = ["done", "filename", "last_output_seek", "last_output_timestamp", |
| 1088 "pkgname", "retcode", "start_timestamp", "target"] | 1100 "pkgname", "retcode", "start_timestamp", "target"] |
| 1089 | 1101 |
| 1090 def __init__(self, target, pkgname, done, filename, start_timestamp, | 1102 def __init__(self, target, pkgname, done, filename, start_timestamp, |
| (...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1554 world_set.update(new_world_pkgs) | 1566 world_set.update(new_world_pkgs) |
| 1555 | 1567 |
| 1556 # Update environment (library cache, symlinks, etc.) | 1568 # Update environment (library cache, symlinks, etc.) |
| 1557 if deps.board and "--pretend" not in emerge.opts: | 1569 if deps.board and "--pretend" not in emerge.opts: |
| 1558 portage.env_update() | 1570 portage.env_update() |
| 1559 | 1571 |
| 1560 print "Done" | 1572 print "Done" |
| 1561 | 1573 |
| 1562 if __name__ == "__main__": | 1574 if __name__ == "__main__": |
| 1563 main() | 1575 main() |
| OLD | NEW |