forked from emscripten-core/emscripten
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjsrun.py
More file actions
171 lines (152 loc) · 6.6 KB
/
jsrun.py
File metadata and controls
171 lines (152 loc) · 6.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# Copyright 2013 The Emscripten Authors. All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.
import logging
import os
import sys
import time
from subprocess import Popen, PIPE, CalledProcessError
TRACK_PROCESS_SPAWNS = int(os.getenv('EM_BUILD_VERBOSE', '0')) >= 3
WORKING_ENGINES = {} # Holds all configured engines and whether they work: maps path -> True/False
def timeout_run(proc, timeout=None, note='unnamed process', full_output=False, note_args=[], throw_on_failure=True):
start = time.time()
if timeout is not None:
while time.time() - start < timeout and proc.poll() is None:
time.sleep(0.1)
if proc.poll() is None:
proc.kill() # XXX bug: killing emscripten.py does not kill it's child process!
raise Exception("Timed out: " + note)
stdout, stderr = proc.communicate()
out = ['' if o is None else o for o in (stdout, stderr)]
if throw_on_failure and proc.returncode != 0:
raise CalledProcessError(proc.returncode, ' '.join(note_args), stdout, stderr)
if TRACK_PROCESS_SPAWNS:
logging.info('Process ' + str(proc.pid) + ' finished after ' + str(time.time() - start) + ' seconds. Exit code: ' + str(proc.returncode))
return '\n'.join(out) if full_output else out[0]
def make_command(filename, engine=None, args=[]):
if type(engine) is not list:
engine = [engine]
# Emscripten supports multiple javascript runtimes. The default is nodejs but
# it can also use d8 (the v8 engine shell) or jsc (JavaScript Core aka
# Safari). Both d8 and jsc require a '--' to delimit arguments to be passed
# to the executed script from d8/jsc options. Node does not require a
# delimeter--arguments after the filename are passed to the script.
#
# Check only the last part of the engine path to ensure we don't accidentally
# label a path to nodejs containing a 'd8' as spidermonkey instead.
jsengine = os.path.basename(engine[0])
# Use "'d8' in" because the name can vary, e.g. d8_g, d8, etc.
is_d8 = 'd8' in jsengine or 'v8' in jsengine
is_jsc = 'jsc' in jsengine
is_wasmer = 'wasmer' in jsengine
is_wasmtime = 'wasmtime' in jsengine
# Disable true async compilation (async apis will in fact be synchronous) for now
# due to https://bugs.chromium.org/p/v8/issues/detail?id=6263
shell_option_flags = ['--no-wasm-async-compilation'] if is_d8 else []
command_flags = []
if is_wasmer:
command_flags += ['run']
if is_wasmer or is_wasmtime:
# in a wasm runtime, run the wasm, not the js
filename = shared.unsuffixed(filename) + '.wasm'
# Separates engine flags from script flags
flag_separator = ['--'] if is_d8 or is_jsc else []
return engine + command_flags + [filename] + shell_option_flags + flag_separator + args
def check_engine(engine):
if type(engine) is list:
engine_path = engine[0]
else:
engine_path = engine
global WORKING_ENGINES
if engine_path in WORKING_ENGINES:
return WORKING_ENGINES[engine_path]
try:
logging.debug('Checking JS engine %s' % engine)
output = run_js(shared.path_from_root('src', 'hello_world.js'), engine,
skip_check=True)
if 'hello, world!' in output:
WORKING_ENGINES[engine_path] = True
except Exception as e:
logging.info('Checking JS engine %s failed. Check your config file. Details: %s' % (str(engine), str(e)))
WORKING_ENGINES[engine_path] = False
return WORKING_ENGINES[engine_path]
def require_engine(engine):
engine_path = engine[0]
if engine_path not in WORKING_ENGINES:
check_engine(engine)
if not WORKING_ENGINES[engine_path]:
logging.critical('The JavaScript shell (%s) does not seem to work, check the paths in the config file' % engine)
sys.exit(1)
def run_js_tool(filename, engine, jsargs=[], *args, **kw):
"""Execute a javascript tool.
This is used by emcc to run parts of the build process that are written
implemented in javascript.
"""
command = make_command(filename, engine, jsargs)
return shared.check_call(command, *args, **kw).stdout
# TODO(sbc): Move to into test directory
def run_js(filename, engine=None, args=[], check_timeout=False, stdin=None, stdout=PIPE, stderr=None, cwd=None,
full_output=False, assert_returncode=0, skip_check=False):
"""Execute javascript code generated by tests, with possible timeout."""
if not os.path.exists(filename):
raise Exception('JavaScript file not found: ' + filename)
# # code to serialize out the test suite files
# # XXX make sure to disable memory init files, and clear out the base_dir. you may also need to manually grab e.g. paper.pdf.js from a run of test_poppler
# import shutil, json
# base_dir = '/tmp/emscripten_suite'
# if not os.path.exists(base_dir):
# os.makedirs(base_dir)
# commands_file = os.path.join(base_dir, 'commands.txt')
# commands = ''
# if os.path.exists(commands_file):
# commands = open(commands_file).read()
# i = 0
# while True:
# curr = os.path.join(base_dir, str(i) + '.js')
# if not os.path.exists(curr): break
# i += 1
# shutil.copyfile(filename, curr)
# commands += os.path.basename(curr) + ',' + json.dumps(args) + '\n'
# open(commands_file, 'w').write(commands)
command = make_command(filename, engine, args)
try:
proc = Popen(
command,
stdin=stdin,
stdout=stdout,
stderr=stderr,
cwd=cwd,
universal_newlines=True)
except Exception:
# the failure may be because the engine is not present. show the proper
# error in that case
if not skip_check:
require_engine(engine)
# if we got here, then require_engine succeeded, so we can raise the original error
raise
timeout = 15 * 60 if check_timeout else None
if TRACK_PROCESS_SPAWNS:
logging.info('Blocking on process ' + str(proc.pid) + ': ' + str(command) + (' for ' + str(timeout) + ' seconds' if timeout else ' until it finishes.'))
try:
ret = timeout_run(
proc,
timeout,
'Execution',
full_output=full_output,
throw_on_failure=False)
except Exception:
# the failure may be because the engine does not work. show the proper
# error in that case
if not skip_check:
require_engine(engine)
# if we got here, then require_engine succeeded, so we can raise the original error
raise
if assert_returncode is not None and proc.returncode is not assert_returncode:
raise CalledProcessError(proc.returncode, ' '.join(command), str(ret))
return ret
try:
from . import shared
except ImportError:
# Python 2 circular import compatibility
import shared