OLD | NEW |
1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Generic utils.""" | 5 """Generic utils.""" |
6 | 6 |
7 import errno | 7 import errno |
8 import logging | 8 import logging |
9 import os | 9 import os |
10 import Queue | 10 import Queue |
11 import re | 11 import re |
12 import stat | 12 import stat |
13 import subprocess | 13 import subprocess |
14 import sys | 14 import sys |
15 import threading | 15 import threading |
16 import time | 16 import time |
17 import xml.dom.minidom | 17 import xml.dom.minidom |
18 import xml.parsers.expat | 18 import xml.parsers.expat |
19 | 19 |
| 20 import subprocess2 |
20 | 21 |
21 def hack_subprocess(): | 22 # Keep an alias for now. |
22 """subprocess functions may throw exceptions when used in multiple threads. | 23 Popen = subprocess2.Popen |
23 | |
24 See http://bugs.python.org/issue1731717 for more information. | |
25 """ | |
26 subprocess._cleanup = lambda: None | |
27 | 24 |
28 | 25 |
29 class Error(Exception): | 26 class Error(Exception): |
30 """gclient exception class.""" | 27 """gclient exception class.""" |
31 pass | 28 pass |
32 | 29 |
33 | 30 |
34 class CheckCallError(OSError, Error): | 31 class CheckCallError(OSError, Error): |
35 """CheckCall() returned non-0.""" | 32 """CheckCall() returned non-0.""" |
36 def __init__(self, command, cwd, returncode, stdout, stderr=None): | 33 def __init__(self, command, cwd, returncode, stdout, stderr=None): |
(...skipping 11 matching lines...) Expand all Loading... |
48 out += ' in ' + self.cwd | 45 out += ' in ' + self.cwd |
49 if self.returncode is not None: | 46 if self.returncode is not None: |
50 out += ' returned %d' % self.returncode | 47 out += ' returned %d' % self.returncode |
51 if self.stdout is not None: | 48 if self.stdout is not None: |
52 out += '\nstdout: %s\n' % self.stdout | 49 out += '\nstdout: %s\n' % self.stdout |
53 if self.stderr is not None: | 50 if self.stderr is not None: |
54 out += '\nstderr: %s\n' % self.stderr | 51 out += '\nstderr: %s\n' % self.stderr |
55 return out | 52 return out |
56 | 53 |
57 | 54 |
58 def Popen(args, **kwargs): | |
59 """Calls subprocess.Popen() with hacks to work around certain behaviors. | |
60 | |
61 Ensure English outpout for svn and make it work reliably on Windows. | |
62 """ | |
63 logging.debug(u'%s, cwd=%s' % (u' '.join(args), kwargs.get('cwd', ''))) | |
64 if not 'env' in kwargs: | |
65 # It's easier to parse the stdout if it is always in English. | |
66 kwargs['env'] = os.environ.copy() | |
67 kwargs['env']['LANGUAGE'] = 'en' | |
68 if not 'shell' in kwargs: | |
69 # *Sigh*: Windows needs shell=True, or else it won't search %PATH% for the | |
70 # executable, but shell=True makes subprocess on Linux fail when it's called | |
71 # with a list because it only tries to execute the first item in the list. | |
72 kwargs['shell'] = (sys.platform=='win32') | |
73 try: | |
74 return subprocess.Popen(args, **kwargs) | |
75 except OSError, e: | |
76 if e.errno == errno.EAGAIN and sys.platform == 'cygwin': | |
77 raise Error( | |
78 'Visit ' | |
79 'http://code.google.com/p/chromium/wiki/CygwinDllRemappingFailure to ' | |
80 'learn how to fix this error; you need to rebase your cygwin dlls') | |
81 raise | |
82 | |
83 | |
84 def CheckCall(command, print_error=True, **kwargs): | 55 def CheckCall(command, print_error=True, **kwargs): |
85 """Similar subprocess.check_call() but redirects stdout and | 56 """Similar subprocess.check_call() but redirects stdout and |
86 returns (stdout, stderr). | 57 returns (stdout, stderr). |
87 | 58 |
88 Works on python 2.4 | 59 Works on python 2.4 |
89 """ | 60 """ |
90 try: | 61 try: |
91 stderr = None | 62 stderr = None |
92 if not print_error: | 63 if not print_error: |
93 stderr = subprocess.PIPE | 64 stderr = subprocess.PIPE |
(...skipping 465 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
559 | 530 |
560 In gclient's case, Dependencies sometime needs to be run out of order due to | 531 In gclient's case, Dependencies sometime needs to be run out of order due to |
561 From() keyword. This class manages that all the required dependencies are run | 532 From() keyword. This class manages that all the required dependencies are run |
562 before running each one. | 533 before running each one. |
563 | 534 |
564 Methods of this class are thread safe. | 535 Methods of this class are thread safe. |
565 """ | 536 """ |
566 def __init__(self, jobs, progress): | 537 def __init__(self, jobs, progress): |
567 """jobs specifies the number of concurrent tasks to allow. progress is a | 538 """jobs specifies the number of concurrent tasks to allow. progress is a |
568 Progress instance.""" | 539 Progress instance.""" |
569 hack_subprocess() | |
570 # Set when a thread is done or a new item is enqueued. | 540 # Set when a thread is done or a new item is enqueued. |
571 self.ready_cond = threading.Condition() | 541 self.ready_cond = threading.Condition() |
572 # Maximum number of concurrent tasks. | 542 # Maximum number of concurrent tasks. |
573 self.jobs = jobs | 543 self.jobs = jobs |
574 # List of WorkItem, for gclient, these are Dependency instances. | 544 # List of WorkItem, for gclient, these are Dependency instances. |
575 self.queued = [] | 545 self.queued = [] |
576 # List of strings representing each Dependency.name that was run. | 546 # List of strings representing each Dependency.name that was run. |
577 self.ran = [] | 547 self.ran = [] |
578 # List of items currently running. | 548 # List of items currently running. |
579 self.running = [] | 549 self.running = [] |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
703 logging.info('Caught exception in thread %s' % self.item.name) | 673 logging.info('Caught exception in thread %s' % self.item.name) |
704 logging.info(str(sys.exc_info())) | 674 logging.info(str(sys.exc_info())) |
705 work_queue.exceptions.put(sys.exc_info()) | 675 work_queue.exceptions.put(sys.exc_info()) |
706 logging.info('Task %s done' % self.item.name) | 676 logging.info('Task %s done' % self.item.name) |
707 | 677 |
708 work_queue.ready_cond.acquire() | 678 work_queue.ready_cond.acquire() |
709 try: | 679 try: |
710 work_queue.ready_cond.notifyAll() | 680 work_queue.ready_cond.notifyAll() |
711 finally: | 681 finally: |
712 work_queue.ready_cond.release() | 682 work_queue.ready_cond.release() |
OLD | NEW |