blob: 8c49e5b008607a4046e04eee93c6c7ee1d95ba71 [file] [log] [blame] [edit]
# 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 common
from subprocess import Popen, PIPE, CalledProcessError
from tools import shared, utils
WORKING_ENGINES = {} # Holds all configured engines and whether they work: maps path -> True/False
def make_command(filename, engine, args=[]):
# 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.unsuffixed(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
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 not in WORKING_ENGINES:
logging.debug('Checking JS engine %s' % engine)
try:
output = run_js(utils.path_from_root('tests/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=[],
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."""
# We used to support True here but we no longer do. Assert here just in case.
assert(type(assert_returncode) == 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 = Popen(
command,
stdin=stdin,
stdout=stdout,
stderr=stderr,
cwd=cwd,
universal_newlines=True)
stdout, stderr = proc.communicate()
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 (stdout, 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