Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(479)

Side by Side Diff: site_scons/site_tools/command_output.py

Issue 8117: Pulling in latest software construction toolkit. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 12 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « site_scons/site_tools/collada_dom.py ('k') | site_scons/site_tools/component_bits.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python2.4 1 #!/usr/bin/python2.4
2 # Copyright 2008, Google Inc. 2 # Copyright 2008, Google Inc.
3 # All rights reserved. 3 # All rights reserved.
4 # 4 #
5 # Redistribution and use in source and binary forms, with or without 5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions are 6 # modification, are permitted provided that the following conditions are
7 # met: 7 # met:
8 # 8 #
9 # * Redistributions of source code must retain the above copyright 9 # * Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer. 10 # notice, this list of conditions and the following disclaimer.
(...skipping 14 matching lines...) Expand all
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 30
31 """Command output builder for SCons.""" 31 """Command output builder for SCons."""
32 32
33 33
34 import os 34 import os
35 import signal
36 import subprocess
37 import sys
38 import threading
39 import time
35 import SCons.Script 40 import SCons.Script
36 import subprocess
37 41
38 42
39 def RunCommand(cmdargs, cwdir=None, env=None, echo_output=True): 43 # TODO(rspangler): Move KillProcessTree() and RunCommand() into their own
44 # module.
45
46
47 def KillProcessTree(pid):
48 """Kills the process and all of its child processes.
49
50 Args:
51 pid: process to kill.
52
53 Raises:
54 OSError: Unsupported OS.
55 """
56
57 if sys.platform in ('win32', 'cygwin'):
58 # Use Windows' taskkill utility
59 killproc_path = '%s;%s\\system32;%s\\system32\\wbem' % (
60 (os.environ['SYSTEMROOT'],) * 3)
61 killproc_cmd = 'taskkill /F /T /PID %d' % pid
62 killproc_task = subprocess.Popen(killproc_cmd, shell=True,
63 stdout=subprocess.PIPE,
64 env={'PATH':killproc_path})
65 killproc_task.communicate()
66
67 elif sys.platform in ('linux', 'linux2', 'darwin'):
68 # Use ps to get a list of processes
69 ps_task = subprocess.Popen(['/bin/ps', 'x', '-o', 'pid,ppid'], stdout=subpro cess.PIPE)
70 ps_out = ps_task.communicate()[0]
71
72 # Parse out a dict of pid->ppid
73 ppid = {}
74 for ps_line in ps_out.split('\n'):
75 w = ps_line.strip().split()
76 if len(w) < 2:
77 continue # Not enough words in this line to be a process list
78 try:
79 ppid[int(w[0])] = int(w[1])
80 except ValueError:
81 pass # Header or footer
82
83 # For each process, kill it if it or any of its parents is our child
84 for p in ppid:
85 p2 = p
86 while p2:
87 if p2 == pid:
88 os.kill(p, signal.SIGKILL)
89 break
90 p2 = ppid.get(p2)
91
92 else:
93 raise OSError('Unsupported OS for KillProcessTree()')
94
95
96 def RunCommand(cmdargs, cwdir=None, env=None, echo_output=True, timeout=None,
97 timeout_errorlevel=14):
40 """Runs an external command. 98 """Runs an external command.
41 99
42 Args: 100 Args:
43 cmdargs: A command string, or a tuple containing the command and its 101 cmdargs: A command string, or a tuple containing the command and its
44 arguments. 102 arguments.
45 cwdir: Working directory for the command, if not None. 103 cwdir: Working directory for the command, if not None.
46 env: Environment variables dict, if not None. 104 env: Environment variables dict, if not None.
47 echo_output: If True, output will be echoed to stdout. 105 echo_output: If True, output will be echoed to stdout.
106 timeout: If not None, timeout for command in seconds. If command times
107 out, it will be killed and timeout_errorlevel will be returned.
108 timeout_errorlevel: The value to return if the command times out.
48 109
49 Returns: 110 Returns:
50 The integer errorlevel from the command. 111 The integer errorlevel from the command.
51 The combined stdout and stderr as a string. 112 The combined stdout and stderr as a string.
52 """ 113 """
53
54 # Force unicode string in the environment to strings. 114 # Force unicode string in the environment to strings.
55 if env: 115 if env:
56 env = dict([(k, str(v)) for k, v in env.items()]) 116 env = dict([(k, str(v)) for k, v in env.items()])
117 start_time = time.time()
57 child = subprocess.Popen(cmdargs, cwd=cwdir, env=env, shell=True, 118 child = subprocess.Popen(cmdargs, cwd=cwdir, env=env, shell=True,
119 universal_newlines=True,
58 stdin=subprocess.PIPE, 120 stdin=subprocess.PIPE,
59 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) 121 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
60 child_out = [] 122 child_out = []
61 child_retcode = None 123 child_retcode = None
62 124
63 # Need to poll the child process, since the stdout pipe can fill. 125 def _ReadThread():
126 """Thread worker function to read output from child process.
127
128 Necessary since there is no cross-platform way of doing non-blocking
129 reads of the output pipe.
130 """
131 read_run = True
132 while read_run:
133 # Need to have a delay of 1 cycle between child completing and
134 # thread exit, to pick up the final output from the child.
135 if child_retcode is not None:
136 read_run = False
137 new_out = child.stdout.read()
138 if new_out:
139 if echo_output:
140 print new_out,
141 child_out.append(new_out)
142
143 read_thread = threading.Thread(target=_ReadThread)
144 read_thread.start()
145
146 # Wait for child to exit or timeout
64 while child_retcode is None: 147 while child_retcode is None:
148 time.sleep(1) # So we don't poll too frequently
65 child_retcode = child.poll() 149 child_retcode = child.poll()
66 new_out = child.stdout.read() 150 if timeout and child_retcode is None:
67 if echo_output: 151 elapsed = time.time() - start_time
68 print new_out, 152 if elapsed > timeout:
69 child_out.append(new_out) 153 print '*** RunCommand() timeout:', cmdargs
154 KillProcessTree(child.pid)
155 child_retcode = timeout_errorlevel
156
157 # Wait for worker thread to pick up final output and die
158 read_thread.join(5)
159 if read_thread.isAlive():
160 print '*** Error: RunCommand() read thread did not exit.'
161 sys.exit(1)
70 162
71 if echo_output: 163 if echo_output:
72 print # end last line of output 164 print # end last line of output
73 return child_retcode, ''.join(child_out) 165 return child_retcode, ''.join(child_out)
74 166
75 167
76 def CommandOutputBuilder(target, source, env): 168 def CommandOutputBuilder(target, source, env):
77 """Command output builder. 169 """Command output builder.
78 170
79 Args: 171 Args:
(...skipping 13 matching lines...) Expand all
93 env = env.Clone() 185 env = env.Clone()
94 186
95 cmdline = env.subst('$COMMAND_OUTPUT_CMDLINE', target=target, source=source) 187 cmdline = env.subst('$COMMAND_OUTPUT_CMDLINE', target=target, source=source)
96 cwdir = env.subst('$COMMAND_OUTPUT_RUN_DIR', target=target, source=source) 188 cwdir = env.subst('$COMMAND_OUTPUT_RUN_DIR', target=target, source=source)
97 if cwdir: 189 if cwdir:
98 cwdir = os.path.normpath(cwdir) 190 cwdir = os.path.normpath(cwdir)
99 env.AppendENVPath('PATH', cwdir) 191 env.AppendENVPath('PATH', cwdir)
100 env.AppendENVPath('LD_LIBRARY_PATH', cwdir) 192 env.AppendENVPath('LD_LIBRARY_PATH', cwdir)
101 else: 193 else:
102 cwdir = None 194 cwdir = None
195 cmdecho = env.get('COMMAND_OUTPUT_ECHO', True)
196 timeout = env.get('COMMAND_OUTPUT_TIMEOUT')
197 timeout_errorlevel = env.get('COMMAND_OUTPUT_TIMEOUT_ERRORLEVEL')
103 198
104 retcode, output = RunCommand(cmdline, cwdir=cwdir, env=env['ENV']) 199 retcode, output = RunCommand(cmdline, cwdir=cwdir, env=env['ENV'],
200 echo_output=cmdecho, timeout=timeout,
201 timeout_errorlevel=timeout_errorlevel)
105 202
106 # Save command line output 203 # Save command line output
107 output_file = open(str(target[0]), 'w') 204 output_file = open(str(target[0]), 'w')
108 output_file.write(output) 205 output_file.write(output)
109 output_file.close() 206 output_file.close()
110 207
111 return retcode 208 return retcode
112 209
113 210
114 def generate(env): 211 def generate(env):
115 # NOTE: SCons requires the use of this name, which fails gpylint. 212 # NOTE: SCons requires the use of this name, which fails gpylint.
116 """SCons entry point for this tool.""" 213 """SCons entry point for this tool."""
117 214
118 # Add the builder and tell it which build environment variables we use. 215 # Add the builder and tell it which build environment variables we use.
119 action = SCons.Script.Action(CommandOutputBuilder, varlist=[ 216 action = SCons.Script.Action(
120 'COMMAND_OUTPUT_CMDLINE', 217 CommandOutputBuilder,
121 'COMMAND_OUTPUT_RUN_DIR', 218 'Output "$COMMAND_OUTPUT_CMDLINE" to $TARGET',
122 ]) 219 varlist=[
220 'COMMAND_OUTPUT_CMDLINE',
221 'COMMAND_OUTPUT_RUN_DIR',
222 'COMMAND_OUTPUT_TIMEOUT',
223 'COMMAND_OUTPUT_TIMEOUT_ERRORLEVEL',
224 # We use COMMAND_OUTPUT_ECHO also, but that doesn't change the
225 # command being run or its output.
226 ], )
123 builder = SCons.Script.Builder(action = action) 227 builder = SCons.Script.Builder(action = action)
124 env.Append(BUILDERS={'CommandOutput': builder}) 228 env.Append(BUILDERS={'CommandOutput': builder})
125 229
126 # Default command line is to run the first input 230 # Default command line is to run the first input
127 env['COMMAND_OUTPUT_CMDLINE'] = '$SOURCE' 231 env['COMMAND_OUTPUT_CMDLINE'] = '$SOURCE'
128 232
129 # TODO(rspangler): add a pseudo-builder which takes an additional command 233 # TODO(rspangler): add a pseudo-builder which takes an additional command
130 # line as an argument. 234 # line as an argument.
OLDNEW
« no previous file with comments | « site_scons/site_tools/collada_dom.py ('k') | site_scons/site_tools/component_bits.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698