diff options
author | Jukka Jylänki <jujjyl@gmail.com> | 2012-11-14 08:46:37 +0200 |
---|---|---|
committer | Jukka Jylänki <jujjyl@gmail.com> | 2012-11-14 13:00:36 +0200 |
commit | 5ec613c59de33b4949d17397aff81d54e49c21c6 (patch) | |
tree | b4cbe46c57b018770c8493299db0efbacbde4144 /tools/shared.py | |
parent | e20dbc1074a36da62080e936f61f0c2719638f2e (diff) |
Refactor Windows replacement for Popen to allow passing stdin to Popen process, and poll() and kill(). Fixes tests other.test_fix_closure and other.test_warn_undefined on Windows.
Diffstat (limited to 'tools/shared.py')
-rw-r--r-- | tools/shared.py | 98 |
1 files changed, 50 insertions, 48 deletions
diff --git a/tools/shared.py b/tools/shared.py index 9f170a58..19ba71bb 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -2,61 +2,63 @@ import shutil, time, os, sys, json, tempfile, copy, shlex, atexit, subprocess from subprocess import Popen, PIPE, STDOUT from tempfile import mkstemp -# A helper class that is used on Windows to represent the subprocess object that is the return value of Popen. -class MockPopen: - def __init__(self, stdout, stderr, returncode): - self.returncode = returncode - self.output = (stdout, stderr) - def communicate(self): - return self.output - def poll(self): - return self.returncode - def kill(self): - return # We've already communicate()d the process (waited for its completion), so can't kill() anymore. Therefore this is a no-op. - # On Windows python suffers from a particularly nasty bug if python is spawning new processes while python itself is spawned from some other non-console process. # Use a custom replacement for Popen on Windows to avoid the "WindowsError: [Error 6] The handle is invalid" errors when emcc is driven through cmake or mingw32-make. # See http://bugs.python.org/issue3905 -def call_process(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, - shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0): - # (stdin, stdout, stderr) store what the caller originally wanted to be done with the streams. - # (stdin_, stdout_, stderr_) will store the fixed set of streams that workaround the bug. - stdin_ = stdin - stdout_ = stdout - stderr_ = stderr - - # If the caller wants one of these PIPEd, we must PIPE them all to avoid the 'handle is invalid' bug. - if stdin_ == PIPE or stdout_ == PIPE or stderr_ == PIPE: - if stdin_ == None: - stdin_ = PIPE - if stdout_ == None: - stdout_ = PIPE - if stderr_ == None: - stderr_ = PIPE - - # Call the process with fixed streams. - process = subprocess.Popen(args, bufsize, executable, stdin_, stdout_, stderr_, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags) - output = process.communicate() - - # If caller never wanted to PIPE stdout or stderr, route the output back to screen to avoid swallowing output. - if stdout == None and stdout_ == PIPE and len(output[0].strip()) > 0: - print >> sys.stdout, output[0] - if stderr == None and stderr_ == PIPE and len(output[1].strip()) > 0: - print >> sys.stderr, output[1] +class WindowsPopen: + def __init__(self, args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, + shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0): + self.stdin = stdin + self.stdout = stdout + self.stderr = stderr + + # (stdin, stdout, stderr) store what the caller originally wanted to be done with the streams. + # (stdin_, stdout_, stderr_) will store the fixed set of streams that workaround the bug. + self.stdin_ = stdin + self.stdout_ = stdout + self.stderr_ = stderr + + # If the caller wants one of these PIPEd, we must PIPE them all to avoid the 'handle is invalid' bug. + if self.stdin_ == PIPE or self.stdout_ == PIPE or self.stderr_ == PIPE: + if self.stdin_ == None: + self.stdin_ = PIPE + if self.stdout_ == None: + self.stdout_ = PIPE + if self.stderr_ == None: + self.stderr_ = PIPE - # Return a mock object to the caller. This works as long as all emscripten code immediately .communicate()s the result, and doesn't - # leave the process object around for longer/more exotic uses. - if stdout == None and stderr == None: - return MockPopen(None, None, process.returncode) - if stdout == None: - return MockPopen(None, output[1], process.returncode) - if stderr == None: - return MockPopen(output[0], None, process.returncode) - return MockPopen(output[0], output[1], process.returncode) + # Call the process with fixed streams. + self.process = subprocess.Popen(args, bufsize, executable, self.stdin_, self.stdout_, self.stderr_, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags) + + def communicate(self, input=None): + output = self.process.communicate(input) + self.returncode = self.process.returncode + + # If caller never wanted to PIPE stdout or stderr, route the output back to screen to avoid swallowing output. + if self.stdout == None and self.stdout_ == PIPE and len(output[0].strip()) > 0: + print >> sys.stdout, output[0] + if self.stderr == None and self.stderr_ == PIPE and len(output[1].strip()) > 0: + print >> sys.stderr, output[1] + + # Return a mock object to the caller. This works as long as all emscripten code immediately .communicate()s the result, and doesn't + # leave the process object around for longer/more exotic uses. + if self.stdout == None and self.stderr == None: + return (None, None) + if self.stdout == None: + return (None, output[1]) + if self.stderr == None: + return (output[0], None) + return (output[0], output[1]) + + def poll(self): + return self.process.returncode + + def kill(self): + return self.process.kill # Install our replacement Popen handler if we are running on Windows to avoid python spawn process function. if os.name == 'nt': - Popen = call_process + Popen = WindowsPopen import js_optimizer |