OLD | NEW |
1 #!/usr/bin/python2.6 | 1 #!/usr/bin/python2.6 |
2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Program to run emerge in parallel, for significant speedup. | 6 """Program to run emerge in parallel, for significant speedup. |
7 | 7 |
8 Usage: | 8 Usage: |
9 ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps] | 9 ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps] |
10 [--force-remote-binary=PKGS] [emerge args] package | 10 [--force-remote-binary=PKGS] [emerge args] package |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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 depgraph as emerge_depgraph | 79 from _emerge.depgraph import depgraph as emerge_depgraph |
80 from _emerge.depgraph import _frozen_depgraph_config | 80 from _emerge.depgraph import _frozen_depgraph_config |
81 from _emerge.main import emerge_main | 81 from _emerge.main import emerge_main |
82 from _emerge.main import parse_opts | 82 from _emerge.main import parse_opts |
83 from _emerge.Package import Package | 83 from _emerge.Package import Package |
84 from _emerge.Scheduler import Scheduler | 84 from _emerge.Scheduler import Scheduler |
| 85 from _emerge.SetArg import SetArg |
85 from _emerge.stdout_spinner import stdout_spinner | 86 from _emerge.stdout_spinner import stdout_spinner |
86 import portage | 87 import portage |
87 import portage.debug | 88 import portage.debug |
88 import portage.versions | 89 import portage.versions |
89 | 90 |
| 91 new_portage = not portage.VERSION.startswith("2.1.7.") |
| 92 if new_portage: |
| 93 from portage._global_updates import _global_updates |
| 94 else: |
| 95 from portage import _global_updates |
90 | 96 |
91 def Usage(): | 97 def Usage(): |
92 """Print usage.""" | 98 """Print usage.""" |
93 print "Usage:" | 99 print "Usage:" |
94 print " ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps]" | 100 print " ./parallel_emerge [--board=BOARD] [--workon=PKGS] [--no-workon-deps]" |
95 print " [--rebuild] [emerge args] package" | 101 print " [--rebuild] [emerge args] package" |
96 print | 102 print |
97 print "Packages specified as workon packages are always built from source." | 103 print "Packages specified as workon packages are always built from source." |
98 print "Unless --no-workon-deps is specified, packages that depend on these" | 104 print "Unless --no-workon-deps is specified, packages that depend on these" |
99 print "packages are also built from source." | 105 print "packages are also built from source." |
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
383 settings, trees, mtimedb = load_emerge_config() | 389 settings, trees, mtimedb = load_emerge_config() |
384 | 390 |
385 # Check whether our portage tree is out of date. Typically, this happens | 391 # Check whether our portage tree is out of date. Typically, this happens |
386 # when you're setting up a new portage tree, such as in setup_board and | 392 # when you're setting up a new portage tree, such as in setup_board and |
387 # make_chroot. In that case, portage applies a bunch of global updates | 393 # make_chroot. In that case, portage applies a bunch of global updates |
388 # here. Once the updates are finished, we need to commit any changes | 394 # here. Once the updates are finished, we need to commit any changes |
389 # that the global update made to our mtimedb, and reload the config. | 395 # that the global update made to our mtimedb, and reload the config. |
390 # | 396 # |
391 # Portage normally handles this logic in emerge_main, but again, we can't | 397 # Portage normally handles this logic in emerge_main, but again, we can't |
392 # use that function here. | 398 # use that function here. |
393 if portage._global_updates(trees, mtimedb["updates"]): | 399 if _global_updates(trees, mtimedb["updates"]): |
394 mtimedb.commit() | 400 mtimedb.commit() |
395 settings, trees, mtimedb = load_emerge_config(trees=trees) | 401 settings, trees, mtimedb = load_emerge_config(trees=trees) |
396 | 402 |
397 # Setup implied options. Portage normally handles this logic in | 403 # Setup implied options. Portage normally handles this logic in |
398 # emerge_main. | 404 # emerge_main. |
399 if "--buildpkgonly" in opts or "buildpkg" in settings.features: | 405 if "--buildpkgonly" in opts or "buildpkg" in settings.features: |
400 opts.setdefault("--buildpkg", True) | 406 opts.setdefault("--buildpkg", True) |
401 if "--getbinpkgonly" in opts: | 407 if "--getbinpkgonly" in opts: |
402 opts.setdefault("--usepkgonly", True) | 408 opts.setdefault("--usepkgonly", True) |
403 opts.setdefault("--getbinpkg", True) | 409 opts.setdefault("--getbinpkg", True) |
(...skipping 27 matching lines...) Expand all Loading... |
431 adjust_configs(opts, trees) | 437 adjust_configs(opts, trees) |
432 | 438 |
433 # Save our configuration so far in the emerge object | 439 # Save our configuration so far in the emerge object |
434 emerge = self.emerge | 440 emerge = self.emerge |
435 emerge.action, emerge.opts = action, opts | 441 emerge.action, emerge.opts = action, opts |
436 emerge.settings, emerge.trees, emerge.mtimedb = settings, trees, mtimedb | 442 emerge.settings, emerge.trees, emerge.mtimedb = settings, trees, mtimedb |
437 emerge.cmdline_packages = cmdline_packages | 443 emerge.cmdline_packages = cmdline_packages |
438 root = settings["ROOT"] | 444 root = settings["ROOT"] |
439 emerge.root_config = trees[root]["root_config"] | 445 emerge.root_config = trees[root]["root_config"] |
440 | 446 |
| 447 if new_portage and "--usepkg" in opts: |
| 448 emerge.trees[root]["bintree"].populate("--getbinpkg" in opts) |
| 449 |
441 def CheckUseFlags(self, pkgsettings, cur_pkg, new_pkg): | 450 def CheckUseFlags(self, pkgsettings, cur_pkg, new_pkg): |
442 """Are the use flags in cur_pkg up to date? | 451 """Are the use flags in cur_pkg up to date? |
443 | 452 |
444 Return True if use flags are up to date; return false otherwise.""" | 453 Return True if use flags are up to date; return false otherwise.""" |
445 | 454 |
446 # cur_use: The set of flags that were enabled when the package was | 455 # cur_use: The set of flags that were enabled when the package was |
447 # first installed. | 456 # first installed. |
448 # cur_iuse: The set of flags that affected the specified package | 457 # cur_iuse: The set of flags that affected the specified package |
449 # when it was first installed. | 458 # when it was first installed. |
450 # | 459 # |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
491 # specifying --tree as well, we tell emerge that it's not safe to remove | 500 # specifying --tree as well, we tell emerge that it's not safe to remove |
492 # uninstall instructions because we're planning on analyzing the output. | 501 # uninstall instructions because we're planning on analyzing the output. |
493 emerge_opts["--tree"] = True | 502 emerge_opts["--tree"] = True |
494 emerge_opts["--emptytree"] = True | 503 emerge_opts["--emptytree"] = True |
495 | 504 |
496 # Set up parameters. | 505 # Set up parameters. |
497 params = create_depgraph_params(emerge_opts, emerge.action) | 506 params = create_depgraph_params(emerge_opts, emerge.action) |
498 frozen_config = _frozen_depgraph_config(emerge.settings, emerge.trees, | 507 frozen_config = _frozen_depgraph_config(emerge.settings, emerge.trees, |
499 emerge_opts, emerge.spinner) | 508 emerge_opts, emerge.spinner) |
500 backtrack_max = emerge_opts.get('--backtrack', 5) | 509 backtrack_max = emerge_opts.get('--backtrack', 5) |
501 runtime_pkg_mask = None | 510 backtrack_parameters = {} |
502 allow_backtracking = backtrack_max > 0 | 511 allow_backtracking = backtrack_max > 0 |
503 | 512 |
504 # Try up to backtrack_max times to create a working depgraph. Each time we | 513 # 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. | 514 # run into a conflict, mask the offending package and try again. |
506 # TODO(davidjames): When Portage supports --force-remote-binary directly, | 515 # TODO(davidjames): When Portage supports --force-remote-binary directly, |
507 # switch back to using the backtrack_depgraph function. | 516 # switch back to using the backtrack_depgraph function. |
508 for i in range(backtrack_max + 1): | 517 for i in range(backtrack_max + 2): |
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. | 518 # Create a depgraph object. |
517 depgraph = emerge_depgraph(emerge.settings, emerge.trees, emerge_opts, | 519 depgraph = emerge_depgraph(emerge.settings, emerge.trees, emerge_opts, |
518 params, emerge.spinner, frozen_config=frozen_config, | 520 params, emerge.spinner, frozen_config=frozen_config, |
519 allow_backtracking=allow_backtracking, | 521 allow_backtracking=allow_backtracking, |
520 runtime_pkg_mask=runtime_pkg_mask) | 522 **backtrack_parameters) |
521 | 523 |
522 if i == 0: | 524 if i == 0: |
523 for cpv in self.forced_remote_binary_packages: | 525 for cpv in self.forced_remote_binary_packages: |
524 # If --force-remote-binary was specified, we want to use this package | 526 # If --force-remote-binary was specified, we want to use this package |
525 # regardless of its use flags. Unfortunately, Portage doesn't support | 527 # regardless of its use flags. Unfortunately, Portage doesn't support |
526 # ignoring use flags for just one package. To convince Portage to | 528 # ignoring use flags for just one package. To convince Portage to |
527 # install the package, we trick Portage into thinking the package has | 529 # install the package, we trick Portage into thinking the package has |
528 # the right use flags. | 530 # the right use flags. |
529 # TODO(davidjames): Update Portage to support --force-remote-binary | 531 # TODO(davidjames): Update Portage to support --force-remote-binary |
530 # directly, so that this hack isn't necessary. | 532 # directly, so that this hack isn't necessary. |
531 pkg = depgraph._pkg(cpv, "binary", emerge.root_config) | 533 pkg = depgraph._pkg(cpv, "binary", emerge.root_config) |
532 pkgsettings = frozen_config.pkgsettings[pkg.root] | 534 pkgsettings = frozen_config.pkgsettings[pkg.root] |
533 pkgsettings.setcpv(pkg) | 535 pkgsettings.setcpv(pkg) |
534 pkg.use.enabled = pkgsettings["PORTAGE_USE"].split() | 536 pkg.use.enabled = pkgsettings["PORTAGE_USE"].split() |
535 | 537 |
536 # Select the packages we want. | 538 # Select the packages we want. |
537 success, favorites = depgraph.select_files(packages) | 539 success, favorites = depgraph.select_files(packages) |
538 if success: | 540 if success: |
539 break | 541 break |
540 elif depgraph.need_restart(): | 542 elif depgraph.need_restart() and i < backtrack_max: |
541 # Looks like we found some packages that can't be installed due to | 543 # Looks like we found some packages that can't be installed due to |
542 # conflicts. Try again, masking out the conflicting packages. | 544 # conflicts. Try again, masking out the conflicting packages. |
543 runtime_pkg_mask = depgraph.get_runtime_pkg_mask() | 545 if new_portage: |
| 546 backtrack_parameters = depgraph.get_backtrack_parameters() |
| 547 else: |
| 548 backtrack_parameters = { |
| 549 'runtime_pkg_mask': depgraph.get_runtime_pkg_mask() |
| 550 } |
544 elif allow_backtracking and i > 0: | 551 elif allow_backtracking and i > 0: |
545 # Looks like we tried all the possible combinations, and we still can't | 552 # Looks like we can't solve the graph. Stop backtracking and report an |
546 # solve the graph. Stop backtracking, so that we can report an error | 553 # error message. |
547 # message. | 554 backtrack_parameters.pop('runtime_pkg_mask', None) |
548 runtime_pkg_mask = None | |
549 allow_backtracking = False | 555 allow_backtracking = False |
550 else: | 556 else: |
551 break | 557 break |
552 | 558 |
553 # Delete the --tree option, because we don't really want to display a | 559 # 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 | 560 # 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 | 561 # the graph. Later, when we display the graph, we'll want standard-looking |
556 # output, so removing the --tree option is important. | 562 # output, so removing the --tree option is important. |
557 frozen_config.myopts.pop("--tree", None) | 563 frozen_config.myopts.pop("--tree", None) |
558 | 564 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
634 # | 640 # |
635 # Here's an example CPV: chromeos-base/power_manager-0.0.1-r1 | 641 # Here's an example CPV: chromeos-base/power_manager-0.0.1-r1 |
636 # Split up, this CPV would be: | 642 # Split up, this CPV would be: |
637 # C -- Component: chromeos-base | 643 # C -- Component: chromeos-base |
638 # P -- Path: power_manager | 644 # P -- Path: power_manager |
639 # V -- Version: 0.0.1-r1 | 645 # V -- Version: 0.0.1-r1 |
640 # | 646 # |
641 # We just refer to CPVs as packages here because it's easier. | 647 # We just refer to CPVs as packages here because it's easier. |
642 deps = {} | 648 deps = {} |
643 for child, priorities in node_deps[0].items(): | 649 for child, priorities in node_deps[0].items(): |
| 650 if isinstance(child, SetArg): continue |
644 deps[str(child.cpv)] = dict(action=str(child.operation), | 651 deps[str(child.cpv)] = dict(action=str(child.operation), |
645 deptype=str(priorities[-1]), | 652 deptype=str(priorities[-1]), |
646 deps={}) | 653 deps={}) |
647 | 654 |
648 # We've built our list of deps, so we can add our package to the tree. | 655 # We've built our list of deps, so we can add our package to the tree. |
649 if isinstance(node, Package): | 656 if isinstance(node, Package): |
650 deps_tree[str(node.cpv)] = dict(action=str(node.operation), | 657 deps_tree[str(node.cpv)] = dict(action=str(node.operation), |
651 deps=deps) | 658 deps=deps) |
652 | 659 |
653 emptytree = "--emptytree" in emerge.opts | 660 emptytree = "--emptytree" in emerge.opts |
(...skipping 531 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1185 remote_ready = PrebuiltsReady(pkg, remote_pkgs, remote_ready_cache) | 1192 remote_ready = PrebuiltsReady(pkg, remote_pkgs, remote_ready_cache) |
1186 if remote_ready and (local_mtime <= remote_mtime or pkg in cycles): | 1193 if remote_ready and (local_mtime <= remote_mtime or pkg in cycles): |
1187 MergeChildren(pkg, "mandatory") | 1194 MergeChildren(pkg, "mandatory") |
1188 else: | 1195 else: |
1189 MergeChildren(pkg, "mandatory_source") | 1196 MergeChildren(pkg, "mandatory_source") |
1190 | 1197 |
1191 def UsePrebuiltPackages(remote_pkgs): | 1198 def UsePrebuiltPackages(remote_pkgs): |
1192 """Update packages that can use prebuilts to do so.""" | 1199 """Update packages that can use prebuilts to do so.""" |
1193 start = time.time() | 1200 start = time.time() |
1194 | 1201 |
1195 # Build list of prebuilt packages | 1202 # Build list of prebuilt packages. |
1196 prebuilt_pkgs = {} | 1203 prebuilt_pkgs = {} |
1197 for pkg, info in deps_map.iteritems(): | 1204 for pkg, info in deps_map.iteritems(): |
1198 if info and info["action"] == "merge": | 1205 if info and info["action"] == "merge": |
1199 if (not info["force_remote_binary"] and info["mandatory_source"] or | 1206 if (not info["force_remote_binary"] and info["mandatory_source"] or |
1200 "--usepkgonly" not in emerge.opts and pkg not in remote_pkgs): | 1207 "--usepkgonly" not in emerge.opts and pkg not in remote_pkgs): |
1201 continue | 1208 continue |
1202 | 1209 |
1203 db_pkg = emerge.depgraph._pkg(pkg, "binary", emerge.root_config) | 1210 db_pkg = emerge.depgraph._pkg(pkg, "binary", emerge.root_config) |
1204 if info["force_remote_binary"]: | 1211 if info["force_remote_binary"]: |
1205 # Undo our earlier hacks to the use flags so that the use flags | 1212 # Undo our earlier hacks to the use flags so that the use flags |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1387 It expects package identifiers to be passed to it via task_queue. When | 1394 It expects package identifiers to be passed to it via task_queue. When |
1388 a task is started, it pushes the (target, filename) to the started_queue. | 1395 a task is started, it pushes the (target, filename) to the started_queue. |
1389 The output is stored in filename. When a merge starts or finishes, we push | 1396 The output is stored in filename. When a merge starts or finishes, we push |
1390 EmergeJobState objects to the job_queue. | 1397 EmergeJobState objects to the job_queue. |
1391 """ | 1398 """ |
1392 | 1399 |
1393 SetupWorkerSignals() | 1400 SetupWorkerSignals() |
1394 settings, trees, mtimedb = emerge.settings, emerge.trees, emerge.mtimedb | 1401 settings, trees, mtimedb = emerge.settings, emerge.trees, emerge.mtimedb |
1395 opts, spinner = emerge.opts, emerge.spinner | 1402 opts, spinner = emerge.opts, emerge.spinner |
1396 opts["--nodeps"] = True | 1403 opts["--nodeps"] = True |
| 1404 if new_portage: |
| 1405 # When Portage launches new processes, it goes on a rampage and closes all |
| 1406 # open file descriptors. Ask Portage not to do that, as it breaks us. |
| 1407 portage.process.get_open_fds = lambda: [] |
1397 while True: | 1408 while True: |
1398 # Wait for a new item to show up on the queue. This is a blocking wait, | 1409 # Wait for a new item to show up on the queue. This is a blocking wait, |
1399 # so if there's nothing to do, we just sit here. | 1410 # so if there's nothing to do, we just sit here. |
1400 target = task_queue.get() | 1411 target = task_queue.get() |
1401 if not target: | 1412 if not target: |
1402 # If target is None, this means that the main thread wants us to quit. | 1413 # If target is None, this means that the main thread wants us to quit. |
1403 # The other workers need to exit too, so we'll push the message back on | 1414 # The other workers need to exit too, so we'll push the message back on |
1404 # to the queue so they'll get it too. | 1415 # to the queue so they'll get it too. |
1405 task_queue.put(target) | 1416 task_queue.put(target) |
1406 return | 1417 return |
1407 db_pkg = package_db[target] | 1418 db_pkg = package_db[target] |
1408 db_pkg.root_config = emerge.root_config | 1419 db_pkg.root_config = emerge.root_config |
1409 install_list = [db_pkg] | 1420 install_list = [db_pkg] |
1410 pkgname = db_pkg.pf | 1421 pkgname = db_pkg.pf |
1411 output = tempfile.NamedTemporaryFile(prefix=pkgname + "-", delete=False) | 1422 output = tempfile.NamedTemporaryFile(prefix=pkgname + "-", delete=False) |
1412 start_timestamp = time.time() | 1423 start_timestamp = time.time() |
1413 job = EmergeJobState(target, pkgname, False, output.name, start_timestamp) | 1424 job = EmergeJobState(target, pkgname, False, output.name, start_timestamp) |
1414 job_queue.put(job) | 1425 job_queue.put(job) |
1415 if "--pretend" in opts: | 1426 if "--pretend" in opts: |
1416 retcode = 0 | 1427 retcode = 0 |
1417 else: | 1428 else: |
1418 save_stdout = sys.stdout | 1429 save_stdout = sys.stdout |
1419 save_stderr = sys.stderr | 1430 save_stderr = sys.stderr |
1420 try: | 1431 try: |
1421 sys.stdout = output | 1432 sys.stdout = output |
1422 sys.stderr = output | 1433 sys.stderr = output |
1423 scheduler = Scheduler(settings, trees, mtimedb, opts, spinner, | 1434 if new_portage: |
1424 install_list, [], emerge.scheduler_graph) | 1435 emerge.scheduler_graph.mergelist = install_list |
| 1436 scheduler = Scheduler(settings, trees, mtimedb, opts, spinner, |
| 1437 favorites=[], graph_config=emerge.scheduler_graph) |
| 1438 else: |
| 1439 scheduler = Scheduler(settings, trees, mtimedb, opts, spinner, |
| 1440 install_list, [], emerge.scheduler_graph) |
1425 retcode = scheduler.merge() | 1441 retcode = scheduler.merge() |
1426 except Exception: | 1442 except Exception: |
1427 traceback.print_exc(file=output) | 1443 traceback.print_exc(file=output) |
1428 retcode = 1 | 1444 retcode = 1 |
1429 finally: | 1445 finally: |
1430 sys.stdout = save_stdout | 1446 sys.stdout = save_stdout |
1431 sys.stderr = save_stderr | 1447 sys.stderr = save_stderr |
1432 output.close() | 1448 output.close() |
1433 if retcode is None: | 1449 if retcode is None: |
1434 retcode = 0 | 1450 retcode = 0 |
(...skipping 441 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1876 # need to upgrade the rest of the packages. So we'll go ahead and do that. | 1892 # need to upgrade the rest of the packages. So we'll go ahead and do that. |
1877 if portage_upgrade: | 1893 if portage_upgrade: |
1878 args = sys.argv[1:] + ["--nomerge=sys-apps/portage"] | 1894 args = sys.argv[1:] + ["--nomerge=sys-apps/portage"] |
1879 os.execvp(os.path.realpath(sys.argv[0]), args) | 1895 os.execvp(os.path.realpath(sys.argv[0]), args) |
1880 | 1896 |
1881 print "Done" | 1897 print "Done" |
1882 sys.exit(0) | 1898 sys.exit(0) |
1883 | 1899 |
1884 if __name__ == "__main__": | 1900 if __name__ == "__main__": |
1885 main() | 1901 main() |
OLD | NEW |