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 [--force-remote-binary=PKGS] [emerge args] package | 10 [--force-remote-binary=PKGS] [emerge args] package |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
69 os.environ["PORTAGE_USERNAME"] = os.path.basename(homedir) | 69 os.environ["PORTAGE_USERNAME"] = os.path.basename(homedir) |
70 | 70 |
71 # Portage doesn't expose dependency trees in its public API, so we have to | 71 # Portage doesn't expose dependency trees in its public API, so we have to |
72 # make use of some private APIs here. These modules are found under | 72 # make use of some private APIs here. These modules are found under |
73 # /usr/lib/portage/pym/. | 73 # /usr/lib/portage/pym/. |
74 # | 74 # |
75 # TODO(davidjames): Update Portage to expose public APIs for these features. | 75 # TODO(davidjames): Update Portage to expose public APIs for these features. |
76 from _emerge.actions import adjust_configs | 76 from _emerge.actions import adjust_configs |
77 from _emerge.actions import load_emerge_config | 77 from _emerge.actions import load_emerge_config |
78 from _emerge.create_depgraph_params import create_depgraph_params | 78 from _emerge.create_depgraph_params import create_depgraph_params |
79 from _emerge.depgraph import backtrack_depgraph | 79 from _emerge.depgraph import depgraph as emerge_depgraph |
| 80 from _emerge.depgraph import _frozen_depgraph_config |
80 from _emerge.main import emerge_main | 81 from _emerge.main import emerge_main |
81 from _emerge.main import parse_opts | 82 from _emerge.main import parse_opts |
82 from _emerge.Package import Package | 83 from _emerge.Package import Package |
83 from _emerge.Scheduler import Scheduler | 84 from _emerge.Scheduler import Scheduler |
84 from _emerge.stdout_spinner import stdout_spinner | 85 from _emerge.stdout_spinner import stdout_spinner |
85 import portage | 86 import portage |
86 import portage.debug | 87 import portage.debug |
87 import portage.versions | 88 import portage.versions |
88 | 89 |
89 | 90 |
(...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
472 # make.defaults, package.use.{mask,force}, etc.). | 473 # make.defaults, package.use.{mask,force}, etc.). |
473 # | 474 # |
474 # This is used by portage in the _reinstall_for_flags function below. | 475 # This is used by portage in the _reinstall_for_flags function below. |
475 forced_flags = set(pkgsettings.useforce).union(pkgsettings.usemask) | 476 forced_flags = set(pkgsettings.useforce).union(pkgsettings.usemask) |
476 | 477 |
477 depgraph = self.emerge.depgraph | 478 depgraph = self.emerge.depgraph |
478 flags = depgraph._reinstall_for_flags(forced_flags, cur_use, | 479 flags = depgraph._reinstall_for_flags(forced_flags, cur_use, |
479 cur_iuse, now_use, now_iuse) | 480 cur_iuse, now_use, now_iuse) |
480 return not flags | 481 return not flags |
481 | 482 |
482 def GenDependencyTree(self, remote_pkgs): | 483 def CreateDepgraph(self, emerge, packages): |
483 """Get dependency tree info from emerge. | 484 """Create an emerge depgraph object.""" |
484 | |
485 TODO(): Update cros_extract_deps to also use this code. | |
486 Returns: | |
487 Dependency tree | |
488 """ | |
489 start = time.time() | |
490 | |
491 # Setup emerge options. | 485 # Setup emerge options. |
492 # | |
493 # We treat dependency info a bit differently than emerge itself. Unless | |
494 # you're using --usepkgonly, we disable --getbinpkg and --usepkg here so | |
495 # that emerge will look at the dependencies of the source ebuilds rather | |
496 # than the binary dependencies. This helps ensure that we have the option | |
497 # of merging a package from source, if we want to switch to it with | |
498 # --workon and the dependencies have changed. | |
499 emerge = self.emerge | |
500 emerge_opts = emerge.opts.copy() | 486 emerge_opts = emerge.opts.copy() |
501 | 487 |
502 # Enable --emptytree so that we get the full tree, which we need for | 488 # Enable --emptytree so that we get the full tree, which we need for |
503 # dependency analysis. By default, with this option, emerge optimizes | 489 # dependency analysis. By default, with this option, emerge optimizes |
504 # the graph by removing uninstall instructions from the graph. By | 490 # the graph by removing uninstall instructions from the graph. By |
505 # specifying --tree as well, we tell emerge that it's not safe to remove | 491 # specifying --tree as well, we tell emerge that it's not safe to remove |
506 # uninstall instructions because we're planning on analyzing the output. | 492 # uninstall instructions because we're planning on analyzing the output. |
507 emerge_opts["--tree"] = True | 493 emerge_opts["--tree"] = True |
508 emerge_opts["--emptytree"] = True | 494 emerge_opts["--emptytree"] = True |
509 | 495 |
510 # Tell emerge not to worry about use flags yet. We handle those inside | 496 # Set up parameters. |
511 # parallel_emerge itself. Further, when we use the --force-remote-binary | 497 params = create_depgraph_params(emerge_opts, emerge.action) |
512 # flag, we don't emerge to reject a package just because it has different | 498 frozen_config = _frozen_depgraph_config(emerge.settings, emerge.trees, |
513 # use flags. | 499 emerge_opts, emerge.spinner) |
514 emerge_opts.pop("--newuse", None) | 500 backtrack_max = emerge_opts.get('--backtrack', 5) |
515 emerge_opts.pop("--reinstall", None) | 501 runtime_pkg_mask = None |
| 502 allow_backtracking = backtrack_max > 0 |
| 503 |
| 504 # Try up to backtrack_max times to create a working depgraph. Each time we |
| 505 # run into a conflict, mask the offending package and try again. |
| 506 # TODO(davidjames): When Portage supports --force-remote-binary directly, |
| 507 # switch back to using the backtrack_depgraph function. |
| 508 for i in range(backtrack_max + 1): |
| 509 if i == backtrack_max: |
| 510 # Looks like we hit the backtracking limit. Run the dependency |
| 511 # calculation one more time (from scratch) to show the original error |
| 512 # message. |
| 513 runtime_pkg_mask = None |
| 514 allow_backtracking = False |
| 515 |
| 516 # Create a depgraph object. |
| 517 depgraph = emerge_depgraph(emerge.settings, emerge.trees, emerge_opts, |
| 518 params, emerge.spinner, frozen_config=frozen_config, |
| 519 allow_backtracking=allow_backtracking, |
| 520 runtime_pkg_mask=runtime_pkg_mask) |
| 521 |
| 522 if i == 0: |
| 523 for cpv in self.forced_remote_binary_packages: |
| 524 # If --force-remote-binary was specified, we want to use this package |
| 525 # regardless of its use flags. Unfortunately, Portage doesn't support |
| 526 # ignoring use flags for just one package. To convince Portage to |
| 527 # install the package, we trick Portage into thinking the package has |
| 528 # the right use flags. |
| 529 # TODO(davidjames): Update Portage to support --force-remote-binary |
| 530 # directly, so that this hack isn't necessary. |
| 531 pkg = depgraph._pkg(cpv, "binary", emerge.root_config) |
| 532 pkgsettings = frozen_config.pkgsettings[pkg.root] |
| 533 pkgsettings.setcpv(pkg) |
| 534 pkg.use.enabled = pkgsettings["PORTAGE_USE"].split() |
| 535 |
| 536 # Select the packages we want. |
| 537 success, favorites = depgraph.select_files(packages) |
| 538 if success: |
| 539 break |
| 540 elif depgraph.need_restart(): |
| 541 # Looks like we found some packages that can't be installed due to |
| 542 # conflicts. Try again, masking out the conflicting packages. |
| 543 runtime_pkg_mask = depgraph.get_runtime_pkg_mask() |
| 544 elif allow_backtracking and i > 0: |
| 545 # Looks like we tried all the possible combinations, and we still can't |
| 546 # solve the graph. Stop backtracking, so that we can report an error |
| 547 # message. |
| 548 runtime_pkg_mask = None |
| 549 allow_backtracking = False |
| 550 else: |
| 551 break |
| 552 |
| 553 # Delete the --tree option, because we don't really want to display a |
| 554 # tree. We just wanted to get emerge to leave uninstall instructions on |
| 555 # the graph. Later, when we display the graph, we'll want standard-looking |
| 556 # output, so removing the --tree option is important. |
| 557 frozen_config.myopts.pop("--tree", None) |
| 558 |
| 559 emerge.depgraph = depgraph |
| 560 |
| 561 # Is it impossible to honor the user's request? Bail! |
| 562 if not success: |
| 563 depgraph.display_problems() |
| 564 sys.exit(1) |
| 565 |
| 566 def GenDependencyTree(self, remote_pkgs): |
| 567 """Get dependency tree info from emerge. |
| 568 |
| 569 TODO(): Update cros_extract_deps to also use this code. |
| 570 Returns: |
| 571 Dependency tree |
| 572 """ |
| 573 start = time.time() |
| 574 |
| 575 emerge = self.emerge |
516 | 576 |
517 # Create a list of packages to merge | 577 # Create a list of packages to merge |
518 packages = set(emerge.cmdline_packages[:]) | 578 packages = set(emerge.cmdline_packages[:]) |
519 if self.mandatory_source: | 579 if self.mandatory_source: |
520 packages.update(self.mandatory_source) | 580 packages.update(self.mandatory_source) |
521 if self.force_remote_binary: | 581 if self.force_remote_binary: |
522 forced_pkgs = {} | 582 forced_pkgs = {} |
523 for pkg in remote_pkgs: | 583 for pkg in remote_pkgs: |
524 category, pkgname, _, _ = portage.catpkgsplit(pkg) | 584 category, pkgname, _, _ = portage.catpkgsplit(pkg) |
525 full_pkgname = "%s/%s" % (category, pkgname) | 585 full_pkgname = "%s/%s" % (category, pkgname) |
526 if (pkgname in self.force_remote_binary or | 586 if (pkgname in self.force_remote_binary or |
527 full_pkgname in self.force_remote_binary): | 587 full_pkgname in self.force_remote_binary): |
528 forced_pkgs.setdefault(full_pkgname, []).append(pkg) | 588 forced_pkgs.setdefault(full_pkgname, []).append(pkg) |
529 | 589 |
| 590 # Add forced binary packages to the dependency list. This is necessary |
| 591 # to ensure that the install plan contains the right package. |
| 592 # |
| 593 # Putting the forced binary package at the beginning of the list is an |
| 594 # optimization that helps avoid unnecessary backtracking (e.g., if |
| 595 # Portage first selects the wrong version, and then backtracks later, it |
| 596 # takes a bit longer and uses up an unnecessary backtrack iteration.) |
| 597 packages = list(packages) |
530 for pkgs in forced_pkgs.values(): | 598 for pkgs in forced_pkgs.values(): |
531 forced_package = portage.versions.best(pkgs) | 599 forced_package = portage.versions.best(pkgs) |
532 packages.add("=%s" % forced_package) | 600 packages.insert(0, "=%s" % forced_package) |
533 self.forced_remote_binary_packages.add(forced_package) | 601 self.forced_remote_binary_packages.add(forced_package) |
534 | 602 |
535 # Tell emerge to be quiet. We print plenty of info ourselves so we don't | 603 # Tell emerge to be quiet. We print plenty of info ourselves so we don't |
536 # need any extra output from portage. | 604 # need any extra output from portage. |
537 portage.util.noiselimit = -1 | 605 portage.util.noiselimit = -1 |
538 | 606 |
539 # My favorite feature: The silent spinner. It doesn't spin. Ever. | 607 # My favorite feature: The silent spinner. It doesn't spin. Ever. |
540 # I'd disable the colors by default too, but they look kind of cool. | 608 # I'd disable the colors by default too, but they look kind of cool. |
541 emerge.spinner = stdout_spinner() | 609 emerge.spinner = stdout_spinner() |
542 emerge.spinner.update = emerge.spinner.update_quiet | 610 emerge.spinner.update = emerge.spinner.update_quiet |
543 | 611 |
544 if "--quiet" not in emerge.opts: | 612 if "--quiet" not in emerge.opts: |
545 print "Calculating deps..." | 613 print "Calculating deps..." |
546 | 614 |
547 # Ask portage to build a dependency graph. with the options we specified | 615 self.CreateDepgraph(emerge, packages) |
548 # above. | 616 depgraph = emerge.depgraph |
549 params = create_depgraph_params(emerge_opts, emerge.action) | |
550 success, depgraph, _ = backtrack_depgraph( | |
551 emerge.settings, emerge.trees, emerge_opts, params, emerge.action, | |
552 packages, emerge.spinner) | |
553 emerge.depgraph = depgraph | |
554 | |
555 # Is it impossible to honor the user's request? Bail! | |
556 if not success: | |
557 depgraph.display_problems() | |
558 sys.exit(1) | |
559 | 617 |
560 # Build our own tree from the emerge digraph. | 618 # Build our own tree from the emerge digraph. |
561 deps_tree = {} | 619 deps_tree = {} |
562 digraph = depgraph._dynamic_config.digraph | 620 digraph = depgraph._dynamic_config.digraph |
563 for node, node_deps in digraph.nodes.items(): | 621 for node, node_deps in digraph.nodes.items(): |
564 # Calculate dependency packages that need to be installed first. Each | 622 # Calculate dependency packages that need to be installed first. Each |
565 # child on the digraph is a dependency. The "operation" field specifies | 623 # child on the digraph is a dependency. The "operation" field specifies |
566 # what we're doing (e.g. merge, uninstall, etc.). The "priorities" array | 624 # what we're doing (e.g. merge, uninstall, etc.). The "priorities" array |
567 # contains the type of dependency (e.g. build, runtime, runtime_post, | 625 # contains the type of dependency (e.g. build, runtime, runtime_post, |
568 # etc.) | 626 # etc.) |
(...skipping 28 matching lines...) Expand all Loading... |
597 # Ask portage for its install plan, so that we can only throw out | 655 # Ask portage for its install plan, so that we can only throw out |
598 # dependencies that portage throws out. Also, keep track of the old | 656 # dependencies that portage throws out. Also, keep track of the old |
599 # versions of packages that we're either upgrading or replacing. | 657 # versions of packages that we're either upgrading or replacing. |
600 # | 658 # |
601 # The "vardb" is the database of installed packages. | 659 # The "vardb" is the database of installed packages. |
602 root = emerge.settings["ROOT"] | 660 root = emerge.settings["ROOT"] |
603 frozen_config = depgraph._frozen_config | 661 frozen_config = depgraph._frozen_config |
604 vardb = frozen_config.trees[root]["vartree"].dbapi | 662 vardb = frozen_config.trees[root]["vartree"].dbapi |
605 pkgsettings = frozen_config.pkgsettings[root] | 663 pkgsettings = frozen_config.pkgsettings[root] |
606 | 664 |
607 # It's time to start worrying about use flags, if necessary. | |
608 for flag in ("--newuse", "--reinstall"): | |
609 if flag in emerge.opts: | |
610 emerge_opts[flag] = emerge.opts[flag] | |
611 | |
612 deps_info = {} | 665 deps_info = {} |
613 for pkg in depgraph.altlist(): | 666 for pkg in depgraph.altlist(): |
614 if isinstance(pkg, Package): | 667 if isinstance(pkg, Package): |
615 # If we're not using --force-remote-binary, check what flags are being | 668 # If we're not using --force-remote-binary, check what flags are being |
616 # used by the real package. | 669 # used by the real package. |
617 if "--usepkgonly" not in emerge.opts: | 670 if "--usepkgonly" not in emerge.opts: |
618 try: | 671 try: |
619 pkg = emerge.depgraph._pkg(pkg.cpv, "ebuild", emerge.root_config) | 672 pkg = emerge.depgraph._pkg(pkg.cpv, "ebuild", emerge.root_config) |
620 except portage.exception.PackageNotFound: | 673 except portage.exception.PackageNotFound: |
621 # This is a --force-remote-binary package. | 674 # This is a --force-remote-binary package. |
622 pass | 675 pass |
623 self.package_db[pkg.cpv] = pkg | 676 self.package_db[pkg.cpv] = pkg |
624 | 677 |
625 # If we're not in emptytree mode, and we're going to replace a package | 678 # If we're not in emptytree mode, and we're going to replace a package |
626 # that is already installed, then this operation is possibly optional. | 679 # that is already installed, then this operation is possibly optional. |
627 # ("--selective" mode is handled later, in RemoveInstalledPackages()) | 680 # ("--selective" mode is handled later, in RemoveInstalledPackages()) |
628 optional = False | 681 optional = False |
629 if not emptytree: | 682 if not emptytree: |
630 for vardb_pkg in vardb.match_pkgs(pkg.cpv): | 683 for vardb_pkg in vardb.match_pkgs(pkg.cpv): |
631 if self.CheckUseFlags(pkgsettings, vardb_pkg, pkg): | 684 if self.CheckUseFlags(pkgsettings, vardb_pkg, pkg): |
632 optional = True | 685 optional = True |
633 break | 686 break |
634 | 687 |
635 # Save off info about the package | 688 # Save off info about the package |
636 deps_info[str(pkg.cpv)] = {"idx": len(deps_info), | 689 deps_info[str(pkg.cpv)] = {"idx": len(deps_info), |
637 "optional": optional} | 690 "optional": optional} |
638 | 691 |
639 # Delete the --tree option, because we don't really want to display a | |
640 # tree. We just wanted to get emerge to leave uninstall instructions on | |
641 # the graph. Later, when we display the graph, we'll want standard-looking | |
642 # output, so removing the --tree option is important. | |
643 frozen_config.myopts.pop("--tree", None) | |
644 | |
645 seconds = time.time() - start | 692 seconds = time.time() - start |
646 if "--quiet" not in emerge.opts: | 693 if "--quiet" not in emerge.opts: |
647 print "Deps calculated in %dm%.1fs" % (seconds / 60, seconds % 60) | 694 print "Deps calculated in %dm%.1fs" % (seconds / 60, seconds % 60) |
648 | 695 |
649 return deps_tree, deps_info | 696 return deps_tree, deps_info |
650 | 697 |
651 def PrintTree(self, deps, depth=""): | 698 def PrintTree(self, deps, depth=""): |
652 """Print the deps we have seen in the emerge output. | 699 """Print the deps we have seen in the emerge output. |
653 | 700 |
654 Args: | 701 Args: |
(...skipping 1192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1847 # need to upgrade the rest of the packages. So we'll go ahead and do that. | 1894 # need to upgrade the rest of the packages. So we'll go ahead and do that. |
1848 if portage_upgrade: | 1895 if portage_upgrade: |
1849 args = sys.argv[1:] + ["--nomerge=sys-apps/portage"] | 1896 args = sys.argv[1:] + ["--nomerge=sys-apps/portage"] |
1850 os.execvp(os.path.realpath(sys.argv[0]), args) | 1897 os.execvp(os.path.realpath(sys.argv[0]), args) |
1851 | 1898 |
1852 print "Done" | 1899 print "Done" |
1853 sys.exit(0) | 1900 sys.exit(0) |
1854 | 1901 |
1855 if __name__ == "__main__": | 1902 if __name__ == "__main__": |
1856 main() | 1903 main() |
OLD | NEW |