| 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 [--force-remote-binary=PKGS] [emerge args] package |
| 11 | 11 |
| 12 Basic operation: | 12 Basic operation: |
| 13 Runs 'emerge -p --debug' to display dependencies, and stores a | 13 Runs 'emerge -p --debug' to display dependencies, and stores a |
| 14 dependency graph. All non-blocked packages are launched in parallel, | 14 dependency graph. All non-blocked packages are launched in parallel, |
| 15 as 'emerge --nodeps package' with any blocked packages being emerged | 15 as 'emerge --nodeps package' with any blocked packages being emerged |
| 16 immediately upon deps being met. | 16 immediately upon deps being met. |
| 17 | 17 |
| 18 For this to work effectively, /usr/lib/portage/pym/portage/locks.py | 18 For this to work effectively, /usr/lib/portage/pym/portage/locks.py |
| 19 must be stubbed out, preventing portage from slowing itself with | 19 must be stubbed out, preventing portage from slowing itself with |
| 20 unneccesary locking, as this script ensures that emerge is run in such | 20 unneccesary locking, as this script ensures that emerge is run in such |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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 backtrack_depgraph |
| 80 from _emerge.main import emerge_main | 80 from _emerge.main import emerge_main |
| 81 from _emerge.main import parse_opts | 81 from _emerge.main import parse_opts |
| 82 from _emerge.Package import Package | 82 from _emerge.Package import Package |
| 83 from _emerge.Scheduler import Scheduler | 83 from _emerge.Scheduler import Scheduler |
| 84 from _emerge.stdout_spinner import stdout_spinner | 84 from _emerge.stdout_spinner import stdout_spinner |
| 85 import portage | 85 import portage |
| 86 import portage.debug | 86 import portage.debug |
| 87 import portage.versions |
| 87 | 88 |
| 88 | 89 |
| 89 def Usage(): | 90 def Usage(): |
| 90 """Print usage.""" | 91 """Print usage.""" |
| 91 print "Usage:" | 92 print "Usage:" |
| 92 print " ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps]" | 93 print " ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps]" |
| 93 print " [--rebuild] [emerge args] package" | 94 print " [--rebuild] [emerge args] package" |
| 94 print | 95 print |
| 95 print "Packages specified as workon packages are always built from source." | 96 print "Packages specified as workon packages are always built from source." |
| 96 print "Unless --no-workon-deps is specified, packages that depend on these" | 97 print "Unless --no-workon-deps is specified, packages that depend on these" |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 211 Typical usage: | 212 Typical usage: |
| 212 deps = DepGraphGenerator() | 213 deps = DepGraphGenerator() |
| 213 deps.Initialize(sys.argv[1:]) | 214 deps.Initialize(sys.argv[1:]) |
| 214 deps_tree, deps_info = deps.GenDependencyTree() | 215 deps_tree, deps_info = deps.GenDependencyTree() |
| 215 deps_graph = deps.GenDependencyGraph(deps_tree, deps_info) | 216 deps_graph = deps.GenDependencyGraph(deps_tree, deps_info) |
| 216 deps.PrintTree(deps_tree) | 217 deps.PrintTree(deps_tree) |
| 217 PrintDepsMap(deps_graph) | 218 PrintDepsMap(deps_graph) |
| 218 """ | 219 """ |
| 219 | 220 |
| 220 __slots__ = ["board", "emerge", "mandatory_source", "no_workon_deps", | 221 __slots__ = ["board", "emerge", "mandatory_source", "no_workon_deps", |
| 221 "nomerge", "package_db", "rebuild", "show_output"] | 222 "nomerge", "package_db", "rebuild", "show_output", |
| 223 "force_remote_binary", "forced_remote_binary_packages"] |
| 222 | 224 |
| 223 def __init__(self): | 225 def __init__(self): |
| 224 self.board = None | 226 self.board = None |
| 225 self.emerge = EmergeData() | 227 self.emerge = EmergeData() |
| 226 self.mandatory_source = set() | 228 self.mandatory_source = set() |
| 227 self.no_workon_deps = False | 229 self.no_workon_deps = False |
| 228 self.nomerge = set() | 230 self.nomerge = set() |
| 229 self.package_db = {} | 231 self.package_db = {} |
| 230 self.rebuild = False | 232 self.rebuild = False |
| 231 self.show_output = False | 233 self.show_output = False |
| 234 self.force_remote_binary = set() |
| 235 self.forced_remote_binary_packages = set() |
| 232 | 236 |
| 233 def ParseParallelEmergeArgs(self, argv): | 237 def ParseParallelEmergeArgs(self, argv): |
| 234 """Read the parallel emerge arguments from the command-line. | 238 """Read the parallel emerge arguments from the command-line. |
| 235 | 239 |
| 236 We need to be compatible with emerge arg format. We scrape arguments that | 240 We need to be compatible with emerge arg format. We scrape arguments that |
| 237 are specific to parallel_emerge, and pass through the rest directly to | 241 are specific to parallel_emerge, and pass through the rest directly to |
| 238 emerge. | 242 emerge. |
| 239 Args: | 243 Args: |
| 240 argv: arguments list | 244 argv: arguments list |
| 241 Returns: | 245 Returns: |
| 242 Arguments that don't belong to parallel_emerge | 246 Arguments that don't belong to parallel_emerge |
| 243 """ | 247 """ |
| 244 emerge_args = [] | 248 emerge_args = [] |
| 245 for arg in argv: | 249 for arg in argv: |
| 246 # Specifically match arguments that are specific to parallel_emerge, and | 250 # Specifically match arguments that are specific to parallel_emerge, and |
| 247 # pass through the rest. | 251 # pass through the rest. |
| 248 if arg.startswith("--board="): | 252 if arg.startswith("--board="): |
| 249 self.board = arg.replace("--board=", "") | 253 self.board = arg.replace("--board=", "") |
| 250 elif arg.startswith("--workon="): | 254 elif arg.startswith("--workon="): |
| 251 workon_str = arg.replace("--workon=", "") | 255 workon_str = arg.replace("--workon=", "") |
| 252 package_list = shlex.split(" ".join(shlex.split(workon_str))) | 256 package_list = shlex.split(" ".join(shlex.split(workon_str))) |
| 253 self.mandatory_source.update(package_list) | 257 self.mandatory_source.update(package_list) |
| 258 elif arg.startswith("--force-remote-binary="): |
| 259 force_remote_binary = arg.replace("--force-remote-binary=", "") |
| 260 force_remote_binary = \ |
| 261 shlex.split(" ".join(shlex.split(force_remote_binary))) |
| 262 self.force_remote_binary.update(force_remote_binary) |
| 254 elif arg.startswith("--nomerge="): | 263 elif arg.startswith("--nomerge="): |
| 255 nomerge_str = arg.replace("--nomerge=", "") | 264 nomerge_str = arg.replace("--nomerge=", "") |
| 256 package_list = shlex.split(" ".join(shlex.split(nomerge_str))) | 265 package_list = shlex.split(" ".join(shlex.split(nomerge_str))) |
| 257 self.nomerge.update(package_list) | 266 self.nomerge.update(package_list) |
| 258 elif arg == "--no-workon-deps": | 267 elif arg == "--no-workon-deps": |
| 259 self.no_workon_deps = True | 268 self.no_workon_deps = True |
| 260 elif arg == "--rebuild": | 269 elif arg == "--rebuild": |
| 261 self.rebuild = True | 270 self.rebuild = True |
| 262 elif arg == "--show-output": | 271 elif arg == "--show-output": |
| 263 self.show_output = True | 272 self.show_output = True |
| (...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 453 # | 462 # |
| 454 # This is used by portage in the _reinstall_for_flags function below. | 463 # This is used by portage in the _reinstall_for_flags function below. |
| 455 forced_flags = set(pkgsettings.useforce).union(pkgsettings.usemask) | 464 forced_flags = set(pkgsettings.useforce).union(pkgsettings.usemask) |
| 456 | 465 |
| 457 depgraph = self.emerge.depgraph | 466 depgraph = self.emerge.depgraph |
| 458 | 467 |
| 459 flags = depgraph._reinstall_for_flags(forced_flags, cur_use, | 468 flags = depgraph._reinstall_for_flags(forced_flags, cur_use, |
| 460 cur_iuse, now_use, now_iuse) | 469 cur_iuse, now_use, now_iuse) |
| 461 return not flags | 470 return not flags |
| 462 | 471 |
| 463 def GenDependencyTree(self): | 472 def GenDependencyTree(self, remote_pkgs): |
| 464 """Get dependency tree info from emerge. | 473 """Get dependency tree info from emerge. |
| 465 | 474 |
| 466 TODO(): Update cros_extract_deps to also use this code. | 475 TODO(): Update cros_extract_deps to also use this code. |
| 467 Returns: | 476 Returns: |
| 468 Dependency tree | 477 Dependency tree |
| 469 """ | 478 """ |
| 470 start = time.time() | 479 start = time.time() |
| 471 | 480 |
| 472 # Setup emerge options. | 481 # Setup emerge options. |
| 473 # | 482 # |
| 474 # We treat dependency info a bit differently than emerge itself. Unless | 483 # We treat dependency info a bit differently than emerge itself. Unless |
| 475 # you're using --usepkgonly, we disable --getbinpkg and --usepkg here so | 484 # you're using --usepkgonly, we disable --getbinpkg and --usepkg here so |
| 476 # that emerge will look at the dependencies of the source ebuilds rather | 485 # that emerge will look at the dependencies of the source ebuilds rather |
| 477 # than the binary dependencies. This helps ensure that we have the option | 486 # than the binary dependencies. This helps ensure that we have the option |
| 478 # of merging a package from source, if we want to switch to it with | 487 # of merging a package from source, if we want to switch to it with |
| 479 # --workon and the dependencies have changed. | 488 # --workon and the dependencies have changed. |
| 480 emerge = self.emerge | 489 emerge = self.emerge |
| 481 emerge_opts = emerge.opts.copy() | 490 emerge_opts = emerge.opts.copy() |
| 482 emerge_opts.pop("--getbinpkg", None) | 491 if self.mandatory_source or self.rebuild or self.force_remote_binary: |
| 483 if "--usepkgonly" not in emerge_opts: | |
| 484 emerge_opts.pop("--usepkg", None) | |
| 485 if self.mandatory_source or self.rebuild: | |
| 486 # Enable --emptytree so that we get the full tree, which we need for | 492 # Enable --emptytree so that we get the full tree, which we need for |
| 487 # dependency analysis. By default, with this option, emerge optimizes | 493 # dependency analysis. By default, with this option, emerge optimizes |
| 488 # the graph by removing uninstall instructions from the graph. By | 494 # the graph by removing uninstall instructions from the graph. By |
| 489 # specifying --tree as well, we tell emerge that it's not safe to remove | 495 # specifying --tree as well, we tell emerge that it's not safe to remove |
| 490 # uninstall instructions because we're planning on analyzing the output. | 496 # uninstall instructions because we're planning on analyzing the output. |
| 491 emerge_opts["--tree"] = True | 497 emerge_opts["--tree"] = True |
| 492 emerge_opts["--emptytree"] = True | 498 emerge_opts["--emptytree"] = True |
| 493 | 499 |
| 500 # Tell emerge not to worry about use flags yet. We handle those inside |
| 501 # parallel_emerge itself. Further, when we use the --force-remote-binary |
| 502 # flag, we don't emerge to reject a package just because it has different |
| 503 # use flags. |
| 504 emerge_opts.pop("--newuse", None) |
| 505 emerge_opts.pop("--reinstall", None) |
| 506 |
| 494 # Create a list of packages to merge | 507 # Create a list of packages to merge |
| 495 packages = set(emerge.cmdline_packages[:]) | 508 packages = set(emerge.cmdline_packages[:]) |
| 496 if self.mandatory_source: | 509 if self.mandatory_source: |
| 497 packages.update(self.mandatory_source) | 510 packages.update(self.mandatory_source) |
| 511 if self.force_remote_binary: |
| 512 forced_pkgs = {} |
| 513 for pkg in remote_pkgs: |
| 514 category, pkgname, _, _ = portage.catpkgsplit(pkg) |
| 515 full_pkgname = "%s/%s" % (category, pkgname) |
| 516 if (pkgname in self.force_remote_binary or |
| 517 full_pkgname in self.force_remote_binary): |
| 518 forced_pkgs.setdefault(full_pkgname, []).append(pkg) |
| 519 |
| 520 for pkgs in forced_pkgs.values(): |
| 521 forced_package = portage.versions.best(pkgs) |
| 522 packages.add("=%s" % forced_package) |
| 523 self.forced_remote_binary_packages.add(forced_package) |
| 498 | 524 |
| 499 # Tell emerge to be quiet. We print plenty of info ourselves so we don't | 525 # Tell emerge to be quiet. We print plenty of info ourselves so we don't |
| 500 # need any extra output from portage. | 526 # need any extra output from portage. |
| 501 portage.util.noiselimit = -1 | 527 portage.util.noiselimit = -1 |
| 502 | 528 |
| 503 # My favorite feature: The silent spinner. It doesn't spin. Ever. | 529 # My favorite feature: The silent spinner. It doesn't spin. Ever. |
| 504 # I'd disable the colors by default too, but they look kind of cool. | 530 # I'd disable the colors by default too, but they look kind of cool. |
| 505 emerge.spinner = stdout_spinner() | 531 emerge.spinner = stdout_spinner() |
| 506 emerge.spinner.update = emerge.spinner.update_quiet | 532 emerge.spinner.update = emerge.spinner.update_quiet |
| 507 | 533 |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 573 # If we're not in emptytree mode, and we're going to replace a package | 599 # If we're not in emptytree mode, and we're going to replace a package |
| 574 # that is already installed, then this operation is possibly optional. | 600 # that is already installed, then this operation is possibly optional. |
| 575 # ("--selective" mode is handled later, in RemoveInstalledPackages()) | 601 # ("--selective" mode is handled later, in RemoveInstalledPackages()) |
| 576 optional = False | 602 optional = False |
| 577 if not emptytree: | 603 if not emptytree: |
| 578 for vardb_pkg in vardb.match_pkgs(pkg.cpv): | 604 for vardb_pkg in vardb.match_pkgs(pkg.cpv): |
| 579 if self.CheckUseFlags(pkgsettings, vardb_pkg, pkg): | 605 if self.CheckUseFlags(pkgsettings, vardb_pkg, pkg): |
| 580 optional = True | 606 optional = True |
| 581 break | 607 break |
| 582 | 608 |
| 583 # Add the package to our database. | |
| 584 self.package_db[str(pkg.cpv)] = pkg | |
| 585 | |
| 586 # Save off info about the package | 609 # Save off info about the package |
| 587 deps_info[str(pkg.cpv)] = {"idx": len(deps_info), | 610 deps_info[str(pkg.cpv)] = {"idx": len(deps_info), |
| 588 "optional": optional} | 611 "optional": optional} |
| 589 | 612 |
| 590 # Delete the --tree option, because we don't really want to display a | 613 # Delete the --tree option, because we don't really want to display a |
| 591 # tree. We just wanted to get emerge to leave uninstall instructions on | 614 # tree. We just wanted to get emerge to leave uninstall instructions on |
| 592 # the graph. Later, when we display the graph, we'll want standard-looking | 615 # the graph. Later, when we display the graph, we'll want standard-looking |
| 593 # output, so removing the --tree option is important. | 616 # output, so removing the --tree option is important. |
| 594 frozen_config.myopts.pop("--tree", None) | 617 frozen_config.myopts.pop("--tree", None) |
| 595 | 618 |
| 596 seconds = time.time() - start | 619 seconds = time.time() - start |
| 597 if "--quiet" not in emerge.opts: | 620 if "--quiet" not in emerge.opts: |
| 598 print "Deps calculated in %dm%.1fs" % (seconds / 60, seconds % 60) | 621 print "Deps calculated in %dm%.1fs" % (seconds / 60, seconds % 60) |
| 599 | 622 |
| 600 return deps_tree, deps_info | 623 return deps_tree, deps_info |
| 601 | 624 |
| 602 def PrintTree(self, deps, depth=""): | 625 def PrintTree(self, deps, depth=""): |
| 603 """Print the deps we have seen in the emerge output. | 626 """Print the deps we have seen in the emerge output. |
| 604 | 627 |
| 605 Args: | 628 Args: |
| 606 deps: Dependency tree structure. | 629 deps: Dependency tree structure. |
| 607 depth: Allows printing the tree recursively, with indentation. | 630 depth: Allows printing the tree recursively, with indentation. |
| 608 """ | 631 """ |
| 609 for entry in sorted(deps): | 632 for entry in sorted(deps): |
| 610 action = deps[entry]["action"] | 633 action = deps[entry]["action"] |
| 611 print "%s %s (%s)" % (depth, entry, action) | 634 print "%s %s (%s)" % (depth, entry, action) |
| 612 self.PrintTree(deps[entry]["deps"], depth=depth + " ") | 635 self.PrintTree(deps[entry]["deps"], depth=depth + " ") |
| 613 | 636 |
| 614 def GenDependencyGraph(self, deps_tree, deps_info): | 637 def RemotePackageDatabase(self, binhost_url): |
| 638 """Grab the latest binary package database from the prebuilt server. |
| 639 |
| 640 We need to know the modification times of the prebuilt packages so that we |
| 641 know when it is OK to use these packages and when we should rebuild them |
| 642 instead. |
| 643 |
| 644 Args: |
| 645 binhost_url: Base URL of remote packages (PORTAGE_BINHOST). |
| 646 |
| 647 Returns: |
| 648 A dict mapping package identifiers to modification times. |
| 649 """ |
| 650 |
| 651 if not binhost_url: |
| 652 return {} |
| 653 |
| 654 def retry_urlopen(url, tries=3): |
| 655 """Open the specified url, retrying if we run into network errors. |
| 656 |
| 657 We do not retry for HTTP errors. |
| 658 |
| 659 Args: |
| 660 url: The specified url. |
| 661 tries: The number of times to try. |
| 662 |
| 663 Returns: |
| 664 The result of urllib2.urlopen(url). |
| 665 """ |
| 666 for i in range(tries): |
| 667 try: |
| 668 return urllib2.urlopen(url) |
| 669 except urllib2.HTTPError as e: |
| 670 raise |
| 671 except urllib2.URLError as e: |
| 672 if i + 1 == tries: |
| 673 raise |
| 674 else: |
| 675 print "Cannot GET %s: %s" % (url, e) |
| 676 |
| 677 url = os.path.join(binhost_url, "Packages") |
| 678 try: |
| 679 f = retry_urlopen(url) |
| 680 except urllib2.HTTPError as e: |
| 681 if e.code == 404: |
| 682 return {} |
| 683 else: |
| 684 raise |
| 685 prebuilt_pkgs = {} |
| 686 for line in f: |
| 687 if line.startswith("CPV: "): |
| 688 pkg = line.replace("CPV: ", "").rstrip() |
| 689 elif line.startswith("MTIME: "): |
| 690 prebuilt_pkgs[pkg] = int(line[:-1].replace("MTIME: ", "")) |
| 691 f.close() |
| 692 |
| 693 return prebuilt_pkgs |
| 694 |
| 695 def GenDependencyGraph(self, deps_tree, deps_info, remote_pkgs): |
| 615 """Generate a doubly linked dependency graph. | 696 """Generate a doubly linked dependency graph. |
| 616 | 697 |
| 617 Args: | 698 Args: |
| 618 deps_tree: Dependency tree structure. | 699 deps_tree: Dependency tree structure. |
| 619 deps_info: More details on the dependencies. | 700 deps_info: More details on the dependencies. |
| 620 Returns: | 701 Returns: |
| 621 Deps graph in the form of a dict of packages, with each package | 702 Deps graph in the form of a dict of packages, with each package |
| 622 specifying a "needs" list and "provides" list. | 703 specifying a "needs" list and "provides" list. |
| 623 """ | 704 """ |
| 624 emerge = self.emerge | 705 emerge = self.emerge |
| (...skipping 28 matching lines...) Expand all Loading... |
| 653 # - action: What we're planning on doing with this package. Generally, | 734 # - action: What we're planning on doing with this package. Generally, |
| 654 # "merge", "nomerge", or "uninstall" | 735 # "merge", "nomerge", or "uninstall" |
| 655 # - mandatory_source: | 736 # - mandatory_source: |
| 656 # If true, indicates that this package must be compiled from source. | 737 # If true, indicates that this package must be compiled from source. |
| 657 # We set this for "workon" packages, and for packages where the | 738 # We set this for "workon" packages, and for packages where the |
| 658 # binaries are known to be out of date. | 739 # binaries are known to be out of date. |
| 659 # - mandatory: | 740 # - mandatory: |
| 660 # If true, indicates that this package must be installed. We don't care | 741 # If true, indicates that this package must be installed. We don't care |
| 661 # whether it's binary or source, unless the mandatory_source flag is | 742 # whether it's binary or source, unless the mandatory_source flag is |
| 662 # also set. | 743 # also set. |
| 744 # - force_remote_binary: |
| 745 # If true, indicates that we want to update to the latest remote prebuilt |
| 746 # of this package. Packages that depend on this package should be built |
| 747 # from source. |
| 663 # | 748 # |
| 664 deps_map = {} | 749 deps_map = {} |
| 665 | 750 |
| 666 def ReverseTree(packages): | 751 def ReverseTree(packages): |
| 667 """Convert tree to digraph. | 752 """Convert tree to digraph. |
| 668 | 753 |
| 669 Take the tree of package -> requirements and reverse it to a digraph of | 754 Take the tree of package -> requirements and reverse it to a digraph of |
| 670 buildable packages -> packages they unblock. | 755 buildable packages -> packages they unblock. |
| 671 Args: | 756 Args: |
| 672 packages: Tree(s) of dependencies. | 757 packages: Tree(s) of dependencies. |
| 673 Returns: | 758 Returns: |
| 674 Unsanitized digraph. | 759 Unsanitized digraph. |
| 675 """ | 760 """ |
| 676 for pkg in packages: | 761 for pkg in packages: |
| 677 | 762 |
| 678 # Create an entry for the package | 763 # Create an entry for the package |
| 679 action = packages[pkg]["action"] | 764 action = packages[pkg]["action"] |
| 680 default_pkg = {"needs": {}, "provides": set(), "action": action, | 765 default_pkg = {"needs": {}, "provides": set(), "action": action, |
| 681 "mandatory_source": False, "mandatory": False} | 766 "mandatory_source": False, "mandatory": False, |
| 767 "force_remote_binary": False} |
| 682 this_pkg = deps_map.setdefault(pkg, default_pkg) | 768 this_pkg = deps_map.setdefault(pkg, default_pkg) |
| 683 | 769 |
| 684 # Create entries for dependencies of this package first. | 770 # Create entries for dependencies of this package first. |
| 685 ReverseTree(packages[pkg]["deps"]) | 771 ReverseTree(packages[pkg]["deps"]) |
| 686 | 772 |
| 687 # Add dependencies to this package. | 773 # Add dependencies to this package. |
| 688 for dep, dep_item in packages[pkg]["deps"].iteritems(): | 774 for dep, dep_item in packages[pkg]["deps"].iteritems(): |
| 689 dep_pkg = deps_map[dep] | 775 dep_pkg = deps_map[dep] |
| 690 dep_type = dep_item["deptype"] | 776 dep_type = dep_item["deptype"] |
| 691 if dep_type != "runtime_post": | 777 if dep_type != "runtime_post": |
| (...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 902 | 988 |
| 903 # Mark this package as non-optional | 989 # Mark this package as non-optional |
| 904 deps_info[pkg]["optional"] = False | 990 deps_info[pkg]["optional"] = False |
| 905 this_pkg[merge_type] = True | 991 this_pkg[merge_type] = True |
| 906 for w in this_pkg["provides"].difference(rebuild_blacklist): | 992 for w in this_pkg["provides"].difference(rebuild_blacklist): |
| 907 MergeChildren(w, merge_type) | 993 MergeChildren(w, merge_type) |
| 908 | 994 |
| 909 if this_pkg["action"] == "nomerge": | 995 if this_pkg["action"] == "nomerge": |
| 910 this_pkg["action"] = "merge" | 996 this_pkg["action"] = "merge" |
| 911 | 997 |
| 912 def RemotePackageDatabase(binhost_url): | |
| 913 """Grab the latest binary package database from the prebuilt server. | |
| 914 | |
| 915 We need to know the modification times of the prebuilt packages so that we | |
| 916 know when it is OK to use these packages and when we should rebuild them | |
| 917 instead. | |
| 918 | |
| 919 Args: | |
| 920 binhost_url: Base URL of remote packages (PORTAGE_BINHOST). | |
| 921 | |
| 922 Returns: | |
| 923 A dict mapping package identifiers to modification times. | |
| 924 """ | |
| 925 | |
| 926 if not binhost_url: | |
| 927 return {} | |
| 928 | |
| 929 def retry_urlopen(url, tries=3): | |
| 930 """Open the specified url, retrying if we run into network errors. | |
| 931 | |
| 932 We do not retry for HTTP errors. | |
| 933 | |
| 934 Args: | |
| 935 url: The specified url. | |
| 936 tries: The number of times to try. | |
| 937 | |
| 938 Returns: | |
| 939 The result of urllib2.urlopen(url). | |
| 940 """ | |
| 941 for i in range(tries): | |
| 942 try: | |
| 943 return urllib2.urlopen(url) | |
| 944 except urllib2.HTTPError as e: | |
| 945 raise | |
| 946 except urllib2.URLError as e: | |
| 947 if i + 1 == tries: | |
| 948 raise | |
| 949 else: | |
| 950 print "Cannot GET %s: %s" % (url, e) | |
| 951 | |
| 952 url = binhost_url + "/Packages" | |
| 953 try: | |
| 954 f = retry_urlopen(url) | |
| 955 except urllib2.HTTPError as e: | |
| 956 if e.code == 404: | |
| 957 return {} | |
| 958 else: | |
| 959 raise | |
| 960 prebuilt_pkgs = {} | |
| 961 for line in f: | |
| 962 if line.startswith("CPV: "): | |
| 963 pkg = line.replace("CPV: ", "").rstrip() | |
| 964 elif line.startswith("MTIME: "): | |
| 965 prebuilt_pkgs[pkg] = int(line[:-1].replace("MTIME: ", "")) | |
| 966 f.close() | |
| 967 | |
| 968 return prebuilt_pkgs | |
| 969 | |
| 970 def LocalPackageDatabase(): | 998 def LocalPackageDatabase(): |
| 971 """Get the modification times of the packages in the local database. | 999 """Get the modification times of the packages in the local database. |
| 972 | 1000 |
| 973 We need to know the modification times of the local packages so that we | 1001 We need to know the modification times of the local packages so that we |
| 974 know when they need to be rebuilt. | 1002 know when they need to be rebuilt. |
| 975 | 1003 |
| 976 Returns: | 1004 Returns: |
| 977 A dict mapping package identifiers to modification times. | 1005 A dict mapping package identifiers to modification times. |
| 978 """ | 1006 """ |
| 979 if self.board: | 1007 if self.board: |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1012 Args: | 1040 Args: |
| 1013 pkg: The specified package. | 1041 pkg: The specified package. |
| 1014 pkg_db: The package DB to use. | 1042 pkg_db: The package DB to use. |
| 1015 cache: A dict, where the results are stored. | 1043 cache: A dict, where the results are stored. |
| 1016 | 1044 |
| 1017 Returns: | 1045 Returns: |
| 1018 True iff the prebuilts are ready for pkg and all deps. | 1046 True iff the prebuilts are ready for pkg and all deps. |
| 1019 """ | 1047 """ |
| 1020 if pkg in cache: | 1048 if pkg in cache: |
| 1021 return cache[pkg] | 1049 return cache[pkg] |
| 1022 if pkg not in pkg_db: | 1050 if pkg not in pkg_db and pkg not in self.forced_remote_binary_packages: |
| 1023 cache[pkg] = False | 1051 cache[pkg] = False |
| 1024 else: | 1052 else: |
| 1025 cache[pkg] = True | 1053 cache[pkg] = True |
| 1026 for dep in deps_map[pkg]["needs"]: | 1054 for dep in deps_map[pkg]["needs"]: |
| 1027 if not PrebuiltsReady(dep, pkg_db, cache): | 1055 if not PrebuiltsReady(dep, pkg_db, cache): |
| 1028 cache[pkg] = False | 1056 cache[pkg] = False |
| 1029 break | 1057 break |
| 1030 return cache[pkg] | 1058 return cache[pkg] |
| 1031 | 1059 |
| 1032 def LastModifiedWithDeps(pkg, pkg_db, cache): | 1060 def LastModifiedWithDeps(pkg, pkg_db, cache): |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1074 # least as new as our local packages, we can install them. | 1102 # least as new as our local packages, we can install them. |
| 1075 # Otherwise, we need to build from source. | 1103 # Otherwise, we need to build from source. |
| 1076 remote_mtime = LastModifiedWithDeps(pkg, remote_pkgs, | 1104 remote_mtime = LastModifiedWithDeps(pkg, remote_pkgs, |
| 1077 remote_mtime_cache) | 1105 remote_mtime_cache) |
| 1078 remote_ready = PrebuiltsReady(pkg, remote_pkgs, remote_ready_cache) | 1106 remote_ready = PrebuiltsReady(pkg, remote_pkgs, remote_ready_cache) |
| 1079 if remote_ready and (local_mtime <= remote_mtime or pkg in cycles): | 1107 if remote_ready and (local_mtime <= remote_mtime or pkg in cycles): |
| 1080 MergeChildren(pkg, "mandatory") | 1108 MergeChildren(pkg, "mandatory") |
| 1081 else: | 1109 else: |
| 1082 MergeChildren(pkg, "mandatory_source") | 1110 MergeChildren(pkg, "mandatory_source") |
| 1083 | 1111 |
| 1084 def UsePrebuiltPackages(): | 1112 def UsePrebuiltPackages(remote_pkgs): |
| 1085 """Update packages that can use prebuilts to do so.""" | 1113 """Update packages that can use prebuilts to do so.""" |
| 1086 start = time.time() | 1114 start = time.time() |
| 1087 | 1115 |
| 1088 # The bintree is the database of binary packages. By default, it's | 1116 # The bintree is the database of binary packages. By default, it's |
| 1089 # empty. | 1117 # empty. |
| 1090 bintree = emerge.trees[root]["bintree"] | 1118 bintree = emerge.trees[root]["bintree"] |
| 1091 bindb = bintree.dbapi | 1119 bindb = bintree.dbapi |
| 1092 root_config = emerge.root_config | 1120 root_config = emerge.root_config |
| 1093 pkgsettings = emerge.depgraph._frozen_config.pkgsettings[root] | 1121 pkgsettings = emerge.depgraph._frozen_config.pkgsettings[root] |
| 1094 prebuilt_pkgs = {} | 1122 prebuilt_pkgs = {} |
| 1095 | 1123 |
| 1096 # Populate the DB with packages | 1124 # Populate the DB with packages |
| 1097 bintree.populate("--getbinpkg" in emerge.opts, | 1125 bintree.populate("--getbinpkg" in emerge.opts, |
| 1098 "--getbinpkgonly" in emerge.opts) | 1126 "--getbinpkgonly" in emerge.opts) |
| 1099 | 1127 |
| 1100 # Build list of prebuilt packages | 1128 # Build list of prebuilt packages |
| 1101 for pkg, info in deps_map.iteritems(): | 1129 for pkg, info in deps_map.iteritems(): |
| 1102 if info and not info["mandatory_source"] and info["action"] == "merge": | 1130 if info and info["action"] == "merge": |
| 1131 if (not info["force_remote_binary"] and info["mandatory_source"] or |
| 1132 "--usepkgonly" not in emerge.opts and pkg not in remote_pkgs): |
| 1133 continue |
| 1134 |
| 1103 db_keys = list(bindb._aux_cache_keys) | 1135 db_keys = list(bindb._aux_cache_keys) |
| 1104 try: | 1136 try: |
| 1105 db_vals = bindb.aux_get(pkg, db_keys + ["MTIME"]) | 1137 db_vals = bindb.aux_get(pkg, db_keys + ["MTIME"]) |
| 1106 except KeyError: | 1138 except KeyError: |
| 1107 # No binary package | 1139 # No binary package |
| 1108 continue | 1140 continue |
| 1141 |
| 1109 mtime = int(db_vals.pop() or 0) | 1142 mtime = int(db_vals.pop() or 0) |
| 1110 metadata = zip(db_keys, db_vals) | 1143 metadata = zip(db_keys, db_vals) |
| 1111 db_pkg = Package(built=True, cpv=pkg, installed=False, | 1144 db_pkg = Package(built=True, cpv=pkg, installed=False, |
| 1112 metadata=metadata, onlydeps=False, mtime=mtime, | 1145 metadata=metadata, onlydeps=False, mtime=mtime, |
| 1113 operation="merge", root_config=root_config, | 1146 operation="merge", root_config=root_config, |
| 1114 type_name="binary") | 1147 type_name="binary") |
| 1115 prebuilt_pkgs[pkg] = db_pkg | 1148 prebuilt_pkgs[pkg] = db_pkg |
| 1116 | 1149 |
| 1117 # Calculate what packages need to be rebuilt due to changes in use flags. | 1150 # Calculate what packages need to be rebuilt due to changes in use flags. |
| 1118 for pkg, db_pkg in prebuilt_pkgs.iteritems(): | 1151 for pkg, db_pkg in prebuilt_pkgs.iteritems(): |
| 1119 db_pkg_src = self.package_db[pkg] | 1152 db_pkg_src = self.package_db.get(pkg) |
| 1120 if not self.CheckUseFlags(pkgsettings, db_pkg, db_pkg_src): | 1153 if db_pkg_src and not self.CheckUseFlags(pkgsettings, db_pkg, |
| 1154 db_pkg_src): |
| 1121 MergeChildren(pkg, "mandatory_source") | 1155 MergeChildren(pkg, "mandatory_source") |
| 1122 | 1156 |
| 1123 # Convert eligible packages to binaries. | 1157 # Convert eligible packages to binaries. |
| 1124 for pkg, info in deps_map.iteritems(): | 1158 for pkg, info in deps_map.iteritems(): |
| 1125 if (info and not info["mandatory_source"] and | 1159 if info and info["action"] == "merge" and pkg in prebuilt_pkgs: |
| 1126 info["action"] == "merge" and pkg in prebuilt_pkgs): | 1160 if not info["mandatory_source"] or info["force_remote_binary"]: |
| 1127 self.package_db[pkg] = prebuilt_pkgs[pkg] | 1161 self.package_db[pkg] = prebuilt_pkgs[pkg] |
| 1128 | 1162 |
| 1129 seconds = time.time() - start | 1163 seconds = time.time() - start |
| 1130 if "--quiet" not in emerge.opts: | 1164 if "--quiet" not in emerge.opts: |
| 1131 print "Prebuilt DB populated in %dm%.1fs" % (seconds / 60, seconds % 60) | 1165 print "Prebuilt DB populated in %dm%.1fs" % (seconds / 60, seconds % 60) |
| 1132 | 1166 |
| 1133 return prebuilt_pkgs | 1167 return prebuilt_pkgs |
| 1134 | 1168 |
| 1135 def AddRemainingPackages(): | 1169 def AddRemainingPackages(): |
| 1136 """Fill in packages that don't have entries in the package db. | 1170 """Fill in packages that don't have entries in the package db. |
| 1137 | 1171 |
| 1138 Every package we are installing needs an entry in the package db. | 1172 Every package we are installing needs an entry in the package db. |
| 1139 This function should only be called after we have removed the | 1173 This function should only be called after we have removed the |
| 1140 packages that are not being merged from our deps_map. | 1174 packages that are not being merged from our deps_map. |
| 1141 """ | 1175 """ |
| 1142 for pkg in deps_map: | 1176 for pkg in deps_map: |
| 1143 if pkg not in self.package_db: | 1177 if pkg not in self.package_db: |
| 1144 if deps_map[pkg]["action"] != "merge": | 1178 if deps_map[pkg]["action"] != "merge": |
| 1145 # We should only fill in packages that are being merged. If | 1179 # We should only fill in packages that are being merged. If |
| 1146 # there's any other packages here, something funny is going on. | 1180 # there's any other packages here, something funny is going on. |
| 1147 print "Missing entry for %s in package db" % pkg | 1181 print "Missing entry for %s in package db" % pkg |
| 1148 sys.exit(1) | 1182 sys.exit(1) |
| 1149 | 1183 |
| 1150 db_pkg = emerge.depgraph._pkg(pkg, "ebuild", emerge.root_config) | 1184 db_pkg = emerge.depgraph._pkg(pkg, "ebuild", emerge.root_config) |
| 1151 self.package_db[pkg] = db_pkg | 1185 self.package_db[pkg] = db_pkg |
| 1152 | 1186 |
| 1153 ReverseTree(deps_tree) | 1187 ReverseTree(deps_tree) |
| 1154 BuildFinalPackageSet() | 1188 BuildFinalPackageSet() |
| 1155 AddSecretDeps() | 1189 AddSecretDeps() |
| 1156 | 1190 |
| 1191 # Mark that we want to use remote binaries only for a particular package. |
| 1192 vardb = emerge.depgraph._frozen_config.trees[root]["vartree"].dbapi |
| 1193 for pkg in self.force_remote_binary: |
| 1194 for db_pkg in final_db.match_pkgs(pkg): |
| 1195 match = deps_map.get(str(db_pkg.cpv)) |
| 1196 if match: |
| 1197 match["force_remote_binary"] = True |
| 1198 |
| 1199 rebuild_blacklist.add(str(db_pkg.cpv)) |
| 1200 if not vardb.match_pkgs(db_pkg.cpv): |
| 1201 MergeChildren(str(db_pkg.cpv), "mandatory") |
| 1202 |
| 1157 if self.no_workon_deps: | 1203 if self.no_workon_deps: |
| 1158 for pkg in self.mandatory_source.copy(): | 1204 for pkg in self.mandatory_source.copy(): |
| 1159 for db_pkg in final_db.match_pkgs(pkg): | 1205 for db_pkg in final_db.match_pkgs(pkg): |
| 1160 deps_map[str(db_pkg.cpv)]["mandatory_source"] = True | 1206 deps_map[str(db_pkg.cpv)]["mandatory_source"] = True |
| 1161 else: | 1207 else: |
| 1162 for pkg in self.mandatory_source.copy(): | 1208 for pkg in self.mandatory_source.copy(): |
| 1163 for db_pkg in final_db.match_pkgs(pkg): | 1209 for db_pkg in final_db.match_pkgs(pkg): |
| 1164 MergeChildren(str(db_pkg.cpv), "mandatory_source") | 1210 MergeChildren(str(db_pkg.cpv), "mandatory_source") |
| 1165 | 1211 |
| 1166 cycles = FindCycles() | 1212 cycles = FindCycles() |
| 1167 if self.rebuild: | 1213 if self.rebuild: |
| 1168 local_pkgs = LocalPackageDatabase() | 1214 local_pkgs = LocalPackageDatabase() |
| 1169 remote_pkgs = {} | |
| 1170 if "--getbinpkg" in emerge.opts: | |
| 1171 binhost = emerge.settings["PORTAGE_BINHOST"] | |
| 1172 remote_pkgs = RemotePackageDatabase(binhost) | |
| 1173 AutoRebuildDeps(local_pkgs, remote_pkgs, cycles) | 1215 AutoRebuildDeps(local_pkgs, remote_pkgs, cycles) |
| 1174 | 1216 |
| 1175 # We need to remove installed packages so that we can use the dependency | 1217 # We need to remove installed packages so that we can use the dependency |
| 1176 # ordering of the install process to show us what cycles to crack. Once | 1218 # ordering of the install process to show us what cycles to crack. Once |
| 1177 # we've done that, we also need to recalculate our list of cycles so that | 1219 # we've done that, we also need to recalculate our list of cycles so that |
| 1178 # we don't include the installed packages in our cycles. | 1220 # we don't include the installed packages in our cycles. |
| 1179 RemoveInstalledPackages() | 1221 RemoveInstalledPackages() |
| 1180 SanitizeTree() | 1222 SanitizeTree() |
| 1181 if deps_map: | 1223 if deps_map: |
| 1182 if "--usepkg" in emerge.opts: | 1224 if "--usepkg" in emerge.opts: |
| 1183 UsePrebuiltPackages() | 1225 UsePrebuiltPackages(remote_pkgs) |
| 1184 AddRemainingPackages() | 1226 AddRemainingPackages() |
| 1185 return deps_map | 1227 return deps_map |
| 1186 | 1228 |
| 1187 def PrintInstallPlan(self, deps_map): | 1229 def PrintInstallPlan(self, deps_map): |
| 1188 """Print an emerge-style install plan. | 1230 """Print an emerge-style install plan. |
| 1189 | 1231 |
| 1190 The install plan lists what packages we're installing, in order. | 1232 The install plan lists what packages we're installing, in order. |
| 1191 It's useful for understanding what parallel_emerge is doing. | 1233 It's useful for understanding what parallel_emerge is doing. |
| 1192 | 1234 |
| 1193 Args: | 1235 Args: |
| (...skipping 533 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1727 if "--quiet" not in emerge.opts: | 1769 if "--quiet" not in emerge.opts: |
| 1728 cmdline_packages = " ".join(emerge.cmdline_packages) | 1770 cmdline_packages = " ".join(emerge.cmdline_packages) |
| 1729 nomerge_packages = " ".join(deps.nomerge) | 1771 nomerge_packages = " ".join(deps.nomerge) |
| 1730 print "Starting fast-emerge." | 1772 print "Starting fast-emerge." |
| 1731 print " Building package %s on %s" % (cmdline_packages, | 1773 print " Building package %s on %s" % (cmdline_packages, |
| 1732 deps.board or "root") | 1774 deps.board or "root") |
| 1733 if nomerge_packages: | 1775 if nomerge_packages: |
| 1734 print " Skipping package %s on %s" % (nomerge_packages, | 1776 print " Skipping package %s on %s" % (nomerge_packages, |
| 1735 deps.board or "root") | 1777 deps.board or "root") |
| 1736 | 1778 |
| 1737 deps_tree, deps_info = deps.GenDependencyTree() | 1779 remote_pkgs = {} |
| 1780 if "--getbinpkg" in emerge.opts: |
| 1781 binhost = emerge.settings["PORTAGE_BINHOST"] |
| 1782 remote_pkgs = deps.RemotePackageDatabase(binhost) |
| 1783 |
| 1784 deps_tree, deps_info = deps.GenDependencyTree(remote_pkgs) |
| 1738 | 1785 |
| 1739 # You want me to be verbose? I'll give you two trees! Twice as much value. | 1786 # You want me to be verbose? I'll give you two trees! Twice as much value. |
| 1740 if "--tree" in emerge.opts and "--verbose" in emerge.opts: | 1787 if "--tree" in emerge.opts and "--verbose" in emerge.opts: |
| 1741 deps.PrintTree(deps_tree) | 1788 deps.PrintTree(deps_tree) |
| 1742 | 1789 |
| 1743 deps_graph = deps.GenDependencyGraph(deps_tree, deps_info) | 1790 deps_graph = deps.GenDependencyGraph(deps_tree, deps_info, remote_pkgs) |
| 1744 | 1791 |
| 1745 # OK, time to print out our progress so far. | 1792 # OK, time to print out our progress so far. |
| 1746 deps.PrintInstallPlan(deps_graph) | 1793 deps.PrintInstallPlan(deps_graph) |
| 1747 if "--tree" in emerge.opts: | 1794 if "--tree" in emerge.opts: |
| 1748 PrintDepsMap(deps_graph) | 1795 PrintDepsMap(deps_graph) |
| 1749 | 1796 |
| 1750 # Are we upgrading portage? If so, and there are more packages to merge, | 1797 # Are we upgrading portage? If so, and there are more packages to merge, |
| 1751 # schedule a restart of parallel_emerge to merge the rest. This ensures that | 1798 # schedule a restart of parallel_emerge to merge the rest. This ensures that |
| 1752 # we pick up all updates to portage settings before merging any more | 1799 # we pick up all updates to portage settings before merging any more |
| 1753 # packages. | 1800 # packages. |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1789 # need to upgrade the rest of the packages. So we'll go ahead and do that. | 1836 # need to upgrade the rest of the packages. So we'll go ahead and do that. |
| 1790 if portage_upgrade: | 1837 if portage_upgrade: |
| 1791 args = sys.argv[1:] + ["--nomerge=sys-apps/portage"] | 1838 args = sys.argv[1:] + ["--nomerge=sys-apps/portage"] |
| 1792 os.execvp(os.path.realpath(sys.argv[0]), args) | 1839 os.execvp(os.path.realpath(sys.argv[0]), args) |
| 1793 | 1840 |
| 1794 print "Done" | 1841 print "Done" |
| 1795 sys.exit(0) | 1842 sys.exit(0) |
| 1796 | 1843 |
| 1797 if __name__ == "__main__": | 1844 if __name__ == "__main__": |
| 1798 main() | 1845 main() |
| OLD | NEW |