| 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 [emerge args] package" |
| (...skipping 22 matching lines...) Expand all Loading... |
| 33 * Some ebuild packages have incorrectly specified deps, and running | 33 * Some ebuild packages have incorrectly specified deps, and running |
| 34 them in parallel is more likely to bring out these failures. | 34 them in parallel is more likely to bring out these failures. |
| 35 * Some ebuilds (especially the build part) have complex dependencies | 35 * Some ebuilds (especially the build part) have complex dependencies |
| 36 that are not captured well by this script (it may be necessary to | 36 that are not captured well by this script (it may be necessary to |
| 37 install an old package to build, but then install a newer version | 37 install an old package to build, but then install a newer version |
| 38 of the same package for a runtime dep). | 38 of the same package for a runtime dep). |
| 39 """ | 39 """ |
| 40 | 40 |
| 41 import codecs | 41 import codecs |
| 42 import copy | 42 import copy |
| 43 import errno |
| 43 import multiprocessing | 44 import multiprocessing |
| 44 import os | 45 import os |
| 45 import Queue | 46 import Queue |
| 46 import shlex | 47 import shlex |
| 47 import signal | 48 import signal |
| 48 import sys | 49 import sys |
| 49 import tempfile | 50 import tempfile |
| 50 import time | 51 import time |
| 51 import traceback | 52 import traceback |
| 52 import urllib2 | 53 import urllib2 |
| (...skipping 1248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1301 """ | 1302 """ |
| 1302 | 1303 |
| 1303 SetupWorkerSignals() | 1304 SetupWorkerSignals() |
| 1304 settings, trees, mtimedb = emerge.settings, emerge.trees, emerge.mtimedb | 1305 settings, trees, mtimedb = emerge.settings, emerge.trees, emerge.mtimedb |
| 1305 opts, spinner = emerge.opts, emerge.spinner | 1306 opts, spinner = emerge.opts, emerge.spinner |
| 1306 opts["--nodeps"] = True | 1307 opts["--nodeps"] = True |
| 1307 while True: | 1308 while True: |
| 1308 # Wait for a new item to show up on the queue. This is a blocking wait, | 1309 # Wait for a new item to show up on the queue. This is a blocking wait, |
| 1309 # so if there's nothing to do, we just sit here. | 1310 # so if there's nothing to do, we just sit here. |
| 1310 target = task_queue.get() | 1311 target = task_queue.get() |
| 1312 if not target: |
| 1313 # If target is None, this means that the main thread wants us to quit. |
| 1314 # The other workers need to exit too, so we'll push the message back on |
| 1315 # to the queue so they'll get it too. |
| 1316 task_queue.put(target) |
| 1317 return |
| 1311 db_pkg = package_db[target] | 1318 db_pkg = package_db[target] |
| 1312 db_pkg.root_config = emerge.root_config | 1319 db_pkg.root_config = emerge.root_config |
| 1313 install_list = [db_pkg] | 1320 install_list = [db_pkg] |
| 1314 pkgname = db_pkg.pf | 1321 pkgname = db_pkg.pf |
| 1315 output = tempfile.NamedTemporaryFile(prefix=pkgname + "-", delete=False) | 1322 output = tempfile.NamedTemporaryFile(prefix=pkgname + "-", delete=False) |
| 1316 start_timestamp = time.time() | 1323 start_timestamp = time.time() |
| 1317 job = EmergeJobState(target, pkgname, False, output.name, start_timestamp) | 1324 job = EmergeJobState(target, pkgname, False, output.name, start_timestamp) |
| 1318 job_queue.put(job) | 1325 job_queue.put(job) |
| 1319 if "--pretend" in opts: | 1326 if "--pretend" in opts: |
| 1320 retcode = 0 | 1327 retcode = 0 |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1405 print "=== Complete: %s ===" % info | 1412 print "=== Complete: %s ===" % info |
| 1406 else: | 1413 else: |
| 1407 print "=== Still running: %s ===" % info | 1414 print "=== Still running: %s ===" % info |
| 1408 | 1415 |
| 1409 if self.unlink: | 1416 if self.unlink: |
| 1410 os.unlink(job.filename) | 1417 os.unlink(job.filename) |
| 1411 | 1418 |
| 1412 | 1419 |
| 1413 def PrintWorker(queue): | 1420 def PrintWorker(queue): |
| 1414 """A worker that prints stuff to the screen as requested.""" | 1421 """A worker that prints stuff to the screen as requested.""" |
| 1415 SetupWorkerSignals() | 1422 |
| 1423 def ExitHandler(signum, frame): |
| 1424 # Switch to default signal handlers so that we'll die after two signals. |
| 1425 signal.signal(signal.SIGINT, signal.SIG_DFL) |
| 1426 signal.signal(signal.SIGTERM, signal.SIG_DFL) |
| 1427 |
| 1428 # Don't exit on the first SIGINT / SIGTERM, because the parent worker will |
| 1429 # handle it and tell us when we need to exit. |
| 1430 signal.signal(signal.SIGINT, ExitHandler) |
| 1431 signal.signal(signal.SIGTERM, ExitHandler) |
| 1432 |
| 1433 # seek_locations is a map indicating the position we are at in each file. |
| 1434 # It starts off empty, but is set by the various Print jobs as we go along |
| 1435 # to indicate where we left off in each file. |
| 1416 seek_locations = {} | 1436 seek_locations = {} |
| 1417 while True: | 1437 while True: |
| 1418 job = queue.get() | 1438 try: |
| 1419 if job: | 1439 job = queue.get() |
| 1420 job.Print(seek_locations) | 1440 if job: |
| 1421 else: | 1441 job.Print(seek_locations) |
| 1422 break | 1442 else: |
| 1443 break |
| 1444 except IOError as ex: |
| 1445 if ex.errno == errno.EINTR: |
| 1446 # Looks like we received a signal. Keep printing. |
| 1447 continue |
| 1448 raise |
| 1423 | 1449 |
| 1424 | 1450 |
| 1425 class EmergeQueue(object): | 1451 class EmergeQueue(object): |
| 1426 """Class to schedule emerge jobs according to a dependency graph.""" | 1452 """Class to schedule emerge jobs according to a dependency graph.""" |
| 1427 | 1453 |
| 1428 def __init__(self, deps_map, emerge, package_db, show_output): | 1454 def __init__(self, deps_map, emerge, package_db, show_output): |
| 1429 # Store the dependency graph. | 1455 # Store the dependency graph. |
| 1430 self._deps_map = deps_map | 1456 self._deps_map = deps_map |
| 1431 # Initialize the running queue to empty | 1457 # Initialize the running queue to empty |
| 1432 self._jobs = {} | 1458 self._jobs = {} |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1483 signal.signal(signal.SIGTERM, signal.SIG_DFL) | 1509 signal.signal(signal.SIGTERM, signal.SIG_DFL) |
| 1484 | 1510 |
| 1485 # Print our current job status | 1511 # Print our current job status |
| 1486 for target, job in self._jobs.iteritems(): | 1512 for target, job in self._jobs.iteritems(): |
| 1487 if job: | 1513 if job: |
| 1488 self._print_queue.put(JobPrinter(job, unlink=True)) | 1514 self._print_queue.put(JobPrinter(job, unlink=True)) |
| 1489 | 1515 |
| 1490 # Notify the user that we are exiting | 1516 # Notify the user that we are exiting |
| 1491 self._Print("Exiting on signal %s" % signum) | 1517 self._Print("Exiting on signal %s" % signum) |
| 1492 | 1518 |
| 1493 # Exit when print worker is done. | 1519 # Kill child threads, then exit. |
| 1494 self._print_queue.put(None) | 1520 self._Exit() |
| 1495 self._print_worker.join() | |
| 1496 sys.exit(1) | 1521 sys.exit(1) |
| 1497 | 1522 |
| 1498 # Print out job status when we are killed | 1523 # Print out job status when we are killed |
| 1499 signal.signal(signal.SIGINT, ExitHandler) | 1524 signal.signal(signal.SIGINT, ExitHandler) |
| 1500 signal.signal(signal.SIGTERM, ExitHandler) | 1525 signal.signal(signal.SIGTERM, ExitHandler) |
| 1501 | 1526 |
| 1502 def _Schedule(self, target): | 1527 def _Schedule(self, target): |
| 1503 # We maintain a tree of all deps, if this doesn't need | 1528 # We maintain a tree of all deps, if this doesn't need |
| 1504 # to be installed just free up it's children and continue. | 1529 # to be installed just free up it's children and continue. |
| 1505 # It is possible to reinstall deps of deps, without reinstalling | 1530 # It is possible to reinstall deps of deps, without reinstalling |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1551 if not self._deps_map[dep]["needs"]: | 1576 if not self._deps_map[dep]["needs"]: |
| 1552 self._Schedule(dep) | 1577 self._Schedule(dep) |
| 1553 self._deps_map.pop(target) | 1578 self._deps_map.pop(target) |
| 1554 | 1579 |
| 1555 def _Retry(self): | 1580 def _Retry(self): |
| 1556 if self._retry_queue: | 1581 if self._retry_queue: |
| 1557 target = self._retry_queue.pop(0) | 1582 target = self._retry_queue.pop(0) |
| 1558 self._Schedule(target) | 1583 self._Schedule(target) |
| 1559 self._Print("Retrying emerge of %s." % target) | 1584 self._Print("Retrying emerge of %s." % target) |
| 1560 | 1585 |
| 1586 def _Exit(self): |
| 1587 # Tell emerge workers to exit. They all exit when 'None' is pushed |
| 1588 # to the queue. |
| 1589 self._emerge_queue.put(None) |
| 1590 self._pool.close() |
| 1591 self._pool.join() |
| 1592 |
| 1593 # Now that our workers are finished, we can kill the print queue. |
| 1594 self._print_queue.put(None) |
| 1595 self._print_worker.join() |
| 1596 |
| 1561 def Run(self): | 1597 def Run(self): |
| 1562 """Run through the scheduled ebuilds. | 1598 """Run through the scheduled ebuilds. |
| 1563 | 1599 |
| 1564 Keep running so long as we have uninstalled packages in the | 1600 Keep running so long as we have uninstalled packages in the |
| 1565 dependency graph to merge. | 1601 dependency graph to merge. |
| 1566 """ | 1602 """ |
| 1567 while self._deps_map: | 1603 while self._deps_map: |
| 1568 # Check here that we are actually waiting for something. | 1604 # Check here that we are actually waiting for something. |
| 1569 if (self._emerge_queue.empty() and | 1605 if (self._emerge_queue.empty() and |
| 1570 self._job_queue.empty() and | 1606 self._job_queue.empty() and |
| 1571 not self._jobs and | 1607 not self._jobs and |
| 1572 self._deps_map): | 1608 self._deps_map): |
| 1573 # If we have failed on a package, retry it now. | 1609 # If we have failed on a package, retry it now. |
| 1574 if self._retry_queue: | 1610 if self._retry_queue: |
| 1575 self._Retry() | 1611 self._Retry() |
| 1576 else: | 1612 else: |
| 1577 # Tell the print worker we're done, and wait for it to exit. | 1613 # Tell child threads to exit. |
| 1578 self._print_queue.put(None) | 1614 self._Exit() |
| 1579 self._print_worker.join() | |
| 1580 | 1615 |
| 1581 # The dependency map is helpful for debugging failures. | 1616 # The dependency map is helpful for debugging failures. |
| 1582 PrintDepsMap(self._deps_map) | 1617 PrintDepsMap(self._deps_map) |
| 1583 | 1618 |
| 1584 # Tell the user why we're exiting. | 1619 # Tell the user why we're exiting. |
| 1585 if self._failed: | 1620 if self._failed: |
| 1586 print "Packages failed: %s" % " ,".join(self._failed) | 1621 print "Packages failed: %s" % " ,".join(self._failed) |
| 1587 else: | 1622 else: |
| 1588 print "Deadlock! Circular dependencies!" | 1623 print "Deadlock! Circular dependencies!" |
| 1589 sys.exit(1) | 1624 sys.exit(1) |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1630 # one retrying package actively running at a time. | 1665 # one retrying package actively running at a time. |
| 1631 self._Retry() | 1666 self._Retry() |
| 1632 | 1667 |
| 1633 self._Print("Completed %s" % details) | 1668 self._Print("Completed %s" % details) |
| 1634 # Mark as completed and unblock waiting ebuilds. | 1669 # Mark as completed and unblock waiting ebuilds. |
| 1635 self._Finish(target) | 1670 self._Finish(target) |
| 1636 | 1671 |
| 1637 # Print an update. | 1672 # Print an update. |
| 1638 self._Status() | 1673 self._Status() |
| 1639 | 1674 |
| 1640 # Tell the print worker we're done, and wait for it to exit. | 1675 # Tell child threads to exit. |
| 1641 self._print_queue.put(None) | 1676 self._Print("Merge complete") |
| 1642 self._print_worker.join() | 1677 self._Exit() |
| 1643 | 1678 |
| 1644 | 1679 |
| 1645 def main(): | 1680 def main(): |
| 1646 | 1681 |
| 1647 deps = DepGraphGenerator() | 1682 deps = DepGraphGenerator() |
| 1648 deps.Initialize(sys.argv[1:]) | 1683 deps.Initialize(sys.argv[1:]) |
| 1649 emerge = deps.emerge | 1684 emerge = deps.emerge |
| 1650 | 1685 |
| 1651 if emerge.action is not None: | 1686 if emerge.action is not None: |
| 1652 sys.argv = deps.ParseParallelEmergeArgs(sys.argv) | 1687 sys.argv = deps.ParseParallelEmergeArgs(sys.argv) |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1730 # need to upgrade the rest of the packages. So we'll go ahead and do that. | 1765 # need to upgrade the rest of the packages. So we'll go ahead and do that. |
| 1731 if portage_upgrade: | 1766 if portage_upgrade: |
| 1732 args = sys.argv[1:] + ["--nomerge=sys-apps/portage"] | 1767 args = sys.argv[1:] + ["--nomerge=sys-apps/portage"] |
| 1733 os.execvp(os.path.realpath(sys.argv[0]), args) | 1768 os.execvp(os.path.realpath(sys.argv[0]), args) |
| 1734 | 1769 |
| 1735 print "Done" | 1770 print "Done" |
| 1736 sys.exit(0) | 1771 sys.exit(0) |
| 1737 | 1772 |
| 1738 if __name__ == "__main__": | 1773 if __name__ == "__main__": |
| 1739 main() | 1774 main() |
| OLD | NEW |