forked from emscripten-core/emscripten
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjsrun.py
127 lines (110 loc) · 4.61 KB
/
jsrun.py
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
# 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 common
import logging
import os
import subprocess
import sys
from subprocess import PIPE, CalledProcessError
from tools import shared, utils
WORKING_ENGINES = {} # Holds all configured engines and whether they work: maps path -> True/False
DEFAULT_TIMEOUT = 5 * 60
def make_command(filename, engine, args=None):
args = args or []
# if no engine is needed, indicated by None, then there is a native executable
# provided which we can just run
if engine[0] is None:
executable = shared.replace_suffix(os.path.abspath(filename), '.exe')
return [executable] + 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 or 'javascriptcore' in jsengine
is_wasmer = 'wasmer' in jsengine
is_wasmtime = 'wasmtime' in jsengine
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.replace_suffix(filename, '.wasm')
# Separates engine flags from script flags
flag_separator = ['--'] if is_d8 or is_jsc else []
return engine + command_flags + [filename] + 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 not in WORKING_ENGINES:
logging.debug('Checking JS engine %s' % engine)
try:
output = run_js(utils.path_from_root('test/hello_world.js'), engine, skip_check=True)
if 'hello, world!' in output:
WORKING_ENGINES[engine_path] = True
else:
WORKING_ENGINES[engine_path] = False
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 None is the "engine", it means we compiled to a native executable;
# there is no engine to check here
if engine_path is None:
return
if engine_path not in WORKING_ENGINES:
check_engine(engine)
if not WORKING_ENGINES[engine_path]:
logging.critical('The engine (%s) does not seem to work, check the paths in the config file' % engine)
sys.exit(1)
def run_js(filename, engine, args=None,
stdin=None, stdout=PIPE, stderr=None, cwd=None,
full_output=False, assert_returncode=0, skip_check=False,
timeout=DEFAULT_TIMEOUT):
"""Execute javascript code generated by tests, with possible timeout."""
# We used to support True here but we no longer do. Assert here just in case.
assert type(assert_returncode) is int
if not os.path.exists(filename):
raise Exception('output file not found: ' + filename)
command = make_command(os.path.abspath(filename), engine, args)
if common.EMTEST_VERBOSE:
print(f"Running: '{shared.shlex_join(command)}'")
try:
proc = subprocess.run(
command,
stdin=stdin,
stdout=stdout,
stderr=stderr,
cwd=cwd,
timeout=timeout,
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
out = ['' if o is None else o for o in (proc.stdout, proc.stderr)]
ret = '\n'.join(out) if full_output else out[0]
if assert_returncode == common.NON_ZERO:
if proc.returncode == 0:
raise CalledProcessError(proc.returncode, ' '.join(command), str(ret))
elif proc.returncode != assert_returncode:
raise CalledProcessError(proc.returncode, ' '.join(command), str(ret))
return ret