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

Side by Side Diff: parallel_emerge

Issue 6033001: parallel_emerge now uninstalls packages correctly when use flags change. (Closed) Base URL: http://git.chromium.org/git/crosutils.git@master
Patch Set: Remove extra line. Created 10 years 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 | Annotate | Revision Log
« 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 [--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
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
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
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
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()
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