aboutsummaryrefslogtreecommitdiff
path: root/tools/shared.py
diff options
context:
space:
mode:
authorJukka Jylänki <jujjyl@gmail.com>2012-11-12 22:28:22 +0200
committerJukka Jylänki <jujjyl@gmail.com>2012-11-12 22:28:22 +0200
commit098a94a5785067effa7214c20ef5312d103a35f8 (patch)
tree7b1ac98efe828ffad0082e6f2634ac92478622cf /tools/shared.py
parentfecb3017c213df80866b59e74e9f7cbe1ee24e76 (diff)
On Windows, depending on the version of make, these calls have been observed to fail with a "WindowsError: [Error 6] The handle is invalid", so be loud about it when this occurs. For more information, see http://bugs.python.org/issue3905 . Implement a workaround to Popen to avoid this issue.
Diffstat (limited to 'tools/shared.py')
-rw-r--r--tools/shared.py54
1 files changed, 53 insertions, 1 deletions
diff --git a/tools/shared.py b/tools/shared.py
index 12728114..1c3dbfb4 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -2,6 +2,58 @@ 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
+
+# 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 CallProcess(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:
+ print >> sys.stdout, output[0]
+ if stderr == None and stderr_ == PIPE:
+ 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 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)
+
+# Install our replacement Popen handler if we are running on Windows to avoid python spawn process function.
+if os.name == 'nt':
+ Popen = CallProcess
+
import js_optimizer
__rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
@@ -1110,7 +1162,7 @@ class Compression:
def execute(cmd, *args, **kw):
try:
- return subprocess.Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that)
+ return Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that)
except:
if not isinstance(cmd, str):
cmd = ' '.join(cmd)