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 |