| 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 624 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 635 # If this package is installed, or will get installed, add it to | 635 # If this package is installed, or will get installed, add it to |
| 636 # final_pkgs | 636 # final_pkgs |
| 637 for pkg in deps_map: | 637 for pkg in deps_map: |
| 638 for match in final_db.match_pkgs(pkg): | 638 for match in final_db.match_pkgs(pkg): |
| 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 A dict mapping cyclic packages to a dict of the deps that cause |
| 646 to a list of the cycles the package is involved in. | 646 cycles. For each dep that causes cycles, it returns an example |
| 647 traversal of the graph that shows the cycle. |
| 647 """ | 648 """ |
| 648 | 649 |
| 649 def FindCyclesAtNode(pkg, cycles, unresolved): | 650 def FindCyclesAtNode(pkg, cycles, unresolved, resolved): |
| 650 """Find cycles in cyclic dependencies starting at specified package. | 651 """Find cycles in cyclic dependencies starting at specified package. |
| 651 | 652 |
| 652 Args: | 653 Args: |
| 653 pkg: Package identifier. | 654 pkg: Package identifier. |
| 654 cycles: Set of cycles so far. | 655 cycles: A dict mapping cyclic packages to a dict of the deps that |
| 656 cause cycles. For each dep that causes cycles, it returns an |
| 657 example traversal of the graph that shows the cycle. |
| 655 unresolved: Nodes that have been visited but are not fully processed. | 658 unresolved: Nodes that have been visited but are not fully processed. |
| 659 resolved: Nodes that have been visited and are fully processed. |
| 656 """ | 660 """ |
| 661 pkg_cycles = cycles.get(pkg) |
| 662 if pkg in resolved and not pkg_cycles: |
| 663 # If we already looked at this package, and found no cyclic |
| 664 # dependencies, we can stop now. |
| 665 return |
| 657 unresolved.append(pkg) | 666 unresolved.append(pkg) |
| 658 mycycles = cycles.get(pkg) | |
| 659 if mycycles: | |
| 660 mycycles = mycycles.get("pkgs") | |
| 661 for dep in deps_map[pkg]["needs"]: | 667 for dep in deps_map[pkg]["needs"]: |
| 662 if mycycles and dep in mycycles: | 668 if dep in unresolved: |
| 663 continue | |
| 664 elif dep in unresolved: | |
| 665 idx = unresolved.index(dep) | 669 idx = unresolved.index(dep) |
| 666 mycycle = unresolved[idx:] + [dep] | 670 mycycle = unresolved[idx:] + [dep] |
| 667 for cycle_pkg in mycycle: | 671 for i in range(len(mycycle) - 1): |
| 668 info = cycles.setdefault(cycle_pkg, {}) | 672 pkg1, pkg2 = mycycle[i], mycycle[i+1] |
| 669 info.setdefault("pkgs", set()).update(mycycle) | 673 cycles.setdefault(pkg1, {}).setdefault(pkg2, mycycle) |
| 670 info.setdefault("cycles", []).append(mycycle) | 674 elif not pkg_cycles or dep not in pkg_cycles: |
| 671 else: | 675 # Looks like we haven't seen this edge before. |
| 672 FindCyclesAtNode(dep, cycles, unresolved) | 676 FindCyclesAtNode(dep, cycles, unresolved, resolved) |
| 673 unresolved.pop() | 677 unresolved.pop() |
| 678 resolved.add(pkg) |
| 674 | 679 |
| 675 cycles, unresolved = {}, [] | 680 cycles, unresolved, resolved = {}, [], set() |
| 676 for pkg in deps_map: | 681 for pkg in deps_map: |
| 677 FindCyclesAtNode(pkg, cycles, unresolved) | 682 FindCyclesAtNode(pkg, cycles, unresolved, resolved) |
| 678 return cycles | 683 return cycles |
| 679 | 684 |
| 680 def RemoveInstalledPackages(): | 685 def RemoveInstalledPackages(): |
| 681 """Remove installed packages, propagating dependencies.""" | 686 """Remove installed packages, propagating dependencies.""" |
| 682 | 687 |
| 683 # If we're in non-selective mode, the packages specified on the command | 688 # If we're in non-selective mode, the packages specified on the command |
| 684 # line are generally mandatory. | 689 # line are generally mandatory. |
| 685 # | 690 # |
| 686 # There are a few exceptions to this rule: | 691 # There are a few exceptions to this rule: |
| 687 # 1. If the package isn't getting installed because it's in | 692 # 1. If the package isn't getting installed because it's in |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 729 dep_provides.update(provides) | 734 dep_provides.update(provides) |
| 730 dep_provides.discard(pkg) | 735 dep_provides.discard(pkg) |
| 731 dep_provides.discard(dep) | 736 dep_provides.discard(dep) |
| 732 for target in provides: | 737 for target in provides: |
| 733 target_needs = deps_map[target]["needs"] | 738 target_needs = deps_map[target]["needs"] |
| 734 target_needs.update(needs) | 739 target_needs.update(needs) |
| 735 target_needs.pop(pkg, None) | 740 target_needs.pop(pkg, None) |
| 736 target_needs.pop(target, None) | 741 target_needs.pop(target, None) |
| 737 del deps_map[pkg] | 742 del deps_map[pkg] |
| 738 | 743 |
| 744 def PrintCycleBreak(basedep, dep, mycycle): |
| 745 """Print details about a cycle that we are planning on breaking. |
| 746 |
| 747 We are breaking a cycle where dep needs basedep. mycycle is an |
| 748 example cycle which contains dep -> basedep.""" |
| 749 |
| 750 # If it's an optional dependency, there's no need to spam the user with |
| 751 # warning messages. |
| 752 needs = deps_map[dep]["needs"] |
| 753 depinfo = needs.get(basedep, "deleted") |
| 754 if depinfo == "optional": |
| 755 return |
| 756 |
| 757 # Notify the user that we're breaking a cycle. |
| 758 print "Breaking %s -> %s (%s)" % (dep, basedep, depinfo) |
| 759 |
| 760 # Show cycle. |
| 761 for i in range(len(mycycle) - 1): |
| 762 pkg1, pkg2 = mycycle[i], mycycle[i+1] |
| 763 needs = deps_map[pkg1]["needs"] |
| 764 depinfo = needs.get(pkg2, "deleted") |
| 765 if pkg1 == dep and pkg2 == basedep: |
| 766 depinfo = depinfo + ", deleting" |
| 767 print " %s -> %s (%s)" % (pkg1, pkg2, depinfo) |
| 768 |
| 739 def SanitizeTree(cycles): | 769 def SanitizeTree(cycles): |
| 740 """Remove circular dependencies. | 770 """Remove circular dependencies. |
| 741 | 771 |
| 742 We prune all dependencies involved in cycles that go against the emerge | 772 We prune all dependencies involved in cycles that go against the emerge |
| 743 ordering. This has a nice property: we're guaranteed to merge | 773 ordering. This has a nice property: we're guaranteed to merge |
| 744 dependencies in the same order that portage does. | 774 dependencies in the same order that portage does. |
| 745 | 775 |
| 746 Because we don't treat any dependencies as "soft" unless they're killed | 776 Because we don't treat any dependencies as "soft" unless they're killed |
| 747 by a cycle, we pay attention to a larger number of dependencies when | 777 by a cycle, we pay attention to a larger number of dependencies when |
| 748 merging. This hurts performance a bit, but helps reliability. | 778 merging. This hurts performance a bit, but helps reliability. |
| 749 | 779 |
| 750 Args: | 780 Args: |
| 751 cycles: Dict of packages involved in cyclic dependencies, mapping each | 781 cycles: Dict of packages involved in cyclic dependencies, mapping each |
| 752 package to a list of the cycles the package is involved in. Produced | 782 package to a list of the cycles the package is involved in. Produced |
| 753 by FindCycles(). | 783 by FindCycles(). |
| 754 """ | 784 """ |
| 755 for basedep, cycle_info in cycles.iteritems(): | 785 for dep, mycycles in cycles.iteritems(): |
| 756 for mycycle in cycle_info["cycles"]: | 786 for basedep, mycycle in mycycles.iteritems(): |
| 757 info = [] | 787 if deps_info[basedep]["idx"] >= deps_info[dep]["idx"]: |
| 758 broken = False | 788 PrintCycleBreak(basedep, dep, mycycle) |
| 759 for i in range(len(mycycle) - 1): | 789 del deps_map[dep]["needs"][basedep] |
| 760 pkg1, pkg2 = mycycle[i], mycycle[i+1] | 790 deps_map[basedep]["provides"].remove(dep) |
| 761 needs = deps_map[pkg1]["needs"] | |
| 762 depinfo = needs.get(pkg2, "deleted") | |
| 763 bad = False | |
| 764 if (deps_info[pkg1]["idx"] >= deps_info[pkg2]["idx"] and | |
| 765 depinfo != "deleted"): | |
| 766 depinfo = depinfo + ", deleting" | |
| 767 broken = True | |
| 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) | |
| 774 | 791 |
| 775 def AddSecretDeps(): | 792 def AddSecretDeps(): |
| 776 """Find these tagged packages and add extra dependencies. | 793 """Find these tagged packages and add extra dependencies. |
| 777 | 794 |
| 778 For debugging dependency problems. | 795 For debugging dependency problems. |
| 779 """ | 796 """ |
| 780 for bad in secret_deps: | 797 for bad in secret_deps: |
| 781 needed = secret_deps[bad] | 798 needed = secret_deps[bad] |
| 782 bad_pkg = None | 799 bad_pkg = None |
| 783 needed_pkg = None | 800 needed_pkg = None |
| (...skipping 782 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1566 world_set.update(new_world_pkgs) | 1583 world_set.update(new_world_pkgs) |
| 1567 | 1584 |
| 1568 # Update environment (library cache, symlinks, etc.) | 1585 # Update environment (library cache, symlinks, etc.) |
| 1569 if deps.board and "--pretend" not in emerge.opts: | 1586 if deps.board and "--pretend" not in emerge.opts: |
| 1570 portage.env_update() | 1587 portage.env_update() |
| 1571 | 1588 |
| 1572 print "Done" | 1589 print "Done" |
| 1573 | 1590 |
| 1574 if __name__ == "__main__": | 1591 if __name__ == "__main__": |
| 1575 main() | 1592 main() |
| OLD | NEW |