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

Side by Side 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 unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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()
OLDNEW
« 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