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 |