blob: ceb95fad9950f6945ba261335169ab704353460d [file] [log] [blame]
# coding=utf-8
# 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.
# noqa: E241
from __future__ import print_function
import difflib
import filecmp
import glob
import itertools
import json
import os
import pipes
import re
import shlex
import shutil
import struct
import sys
import time
import tempfile
import unittest
import uuid
if __name__ == '__main__':
raise Exception('do not run this file directly; do something like: tests/runner.py other')
from tools.shared import Building, PIPE, run_js, run_process, STDOUT, try_delete, listify
from tools.shared import EMCC, EMXX, EMAR, EMRANLIB, PYTHON, FILE_PACKAGER, WINDOWS, MACOS, LLVM_ROOT, EMCONFIG, EM_BUILD_VERBOSE
from tools.shared import CLANG, CLANG_CC, CLANG_CPP, LLVM_AR
from tools.shared import COMPILER_ENGINE, NODE_JS, SPIDERMONKEY_ENGINE, JS_ENGINES, V8_ENGINE
from tools.shared import WebAssembly
from runner import RunnerCore, path_from_root, get_zlib_library, no_wasm_backend
from runner import needs_dlfcn, env_modify, no_windows, chdir, with_env_modify, create_test_file
from tools import jsrun, shared
import tools.line_endings
import tools.js_optimizer
import tools.tempfiles
import tools.duplicate_function_eliminator
scons_path = Building.which('scons')
class temp_directory(object):
def __init__(self, dirname):
self.dir = dirname
def __enter__(self):
self.directory = tempfile.mkdtemp(prefix='emtest_temp_', dir=self.dir)
self.prev_cwd = os.getcwd()
os.chdir(self.directory)
print('temp_directory: ' + self.directory)
return self.directory
def __exit__(self, type, value, traceback):
os.chdir(self.prev_cwd)
def uses_canonical_tmp(func):
"""Decorator that signals the use of the canonical temp by a test method.
This decorator takes care of cleaning the directory after the
test to satisfy the leak detector.
"""
def decorated(self):
# Before running the test completely remove the canonical_tmp
if os.path.exists(self.canonical_temp_dir):
shutil.rmtree(self.canonical_temp_dir)
try:
func(self)
finally:
# Make sure the test isn't lying about the fact that it uses
# canonical_tmp
self.assertTrue(os.path.exists(self.canonical_temp_dir))
# Remove the temp dir in a try-finally, as otherwise if the
# test fails we would not clean it up, and if leak detection
# is set we will show that error instead of the actual one.
shutil.rmtree(self.canonical_temp_dir)
return decorated
def is_python3_version_supported():
"""Retuns True if the installed python3 version is supported by emscripten.
Note: Emscripten requires python3.5 or above since python3.4 and below do not
support circular dependencies."""
python3 = Building.which('python3')
if not python3:
return False
output = run_process([python3, '--version'], stdout=PIPE).stdout
# strip out 'rc1' etc., we don't care about release candidates
if 'rc' in output:
output = output.split('rc')[0]
version = [int(x) for x in output.split(' ')[1].split('.')]
return version >= [3, 5, 0]
def encode_leb(number):
# TODO(sbc): handle larger numbers
assert(number < 255)
# pack the integer then take only the first (little end) byte
return struct.pack('<i', number)[:1]
def get_fastcomp_src_dir():
"""Locate fastcomp source tree by searching realtive to LLVM_ROOT."""
d = LLVM_ROOT
key_file = 'readme-emscripten-fastcomp.txt'
while d != os.path.dirname(d):
d = os.path.abspath(d)
# when the build directory lives below the source directory
if os.path.exists(os.path.join(d, key_file)):
return d
# when the build directory lives alongside the source directory
elif os.path.exists(os.path.join(d, 'src', key_file)):
return os.path.join(d, 'src')
else:
d = os.path.dirname(d)
return None
class other(RunnerCore):
# Utility to run a simple test in this suite. This receives a directory which
# should contain a test.cpp and test.out files, compiles the cpp, and runs it
# to verify the output, with optional compile and run arguments.
# TODO: use in more places
def do_other_test(self, dirname, emcc_args=[], run_args=[]):
shutil.copyfile(path_from_root('tests', dirname, 'test.cpp'), 'test.cpp')
run_process([PYTHON, EMCC, 'test.cpp'] + emcc_args)
expected = open(path_from_root('tests', dirname, 'test.out')).read()
seen = run_js('a.out.js', args=run_args, stderr=PIPE, full_output=True) + '\n'
self.assertContained(expected, seen)
def test_emcc_v(self):
for compiler in [EMCC, EMXX]:
# -v, without input files
proc = run_process([PYTHON, compiler, '-v'], stdout=PIPE, stderr=PIPE)
self.assertContained('clang version %s' % shared.expected_llvm_version(), proc.stderr)
self.assertContained('GNU', proc.stderr)
self.assertNotContained('this is dangerous', proc.stdout)
self.assertNotContained('this is dangerous', proc.stderr)
def test_emcc_generate_config(self):
for compiler in [EMCC, EMXX]:
config_path = './emscripten_config'
run_process([PYTHON, compiler, '--generate-config', config_path])
assert os.path.exists(config_path), 'A config file should have been created at %s' % config_path
config_contents = open(config_path).read()
self.assertContained('EMSCRIPTEN_ROOT', config_contents)
self.assertContained('LLVM_ROOT', config_contents)
os.remove(config_path)
def test_emcc_output_mjs(self):
run_process([PYTHON, EMCC, '-o', 'hello_world.mjs', path_from_root('tests', 'hello_world.c')])
with open('hello_world.mjs') as f:
output = f.read()
self.assertContained('export default Module;', output)
# TODO(sbc): Test that this is actually runnable. We currently don't have
# any tests for EXPORT_ES6 but once we do this should be enabled.
# self.assertContained('hello, world!', run_js('hello_world.mjs'))
def test_emcc_1(self):
for compiler, suffix in [(EMCC, '.c'), (EMXX, '.cpp')]:
# --version
output = run_process([PYTHON, compiler, '--version'], stdout=PIPE, stderr=PIPE)
output = output.stdout.replace('\r', '')
self.assertContained('emcc (Emscripten gcc/clang-like replacement)', output)
self.assertContained('''Copyright (C) 2014 the Emscripten authors (see AUTHORS.txt)
This is free and open source software under the MIT license.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
''', output)
# --help
output = run_process([PYTHON, compiler, '--help'], stdout=PIPE, stderr=PIPE)
self.assertContained('Display this information', output.stdout)
self.assertContained('Most clang options will work', output.stdout)
# -dumpmachine
output = run_process([PYTHON, compiler, '-dumpmachine'], stdout=PIPE, stderr=PIPE)
self.assertContained(shared.get_llvm_target(), output.stdout)
# -dumpversion
output = run_process([PYTHON, compiler, '-dumpversion'], stdout=PIPE, stderr=PIPE)
self.assertEqual(shared.EMSCRIPTEN_VERSION + os.linesep, output.stdout, 'results should be identical')
# emcc src.cpp ==> writes a.out.js and a.out.wasm
self.clear()
run_process([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix)])
assert os.path.exists('a.out.js')
assert os.path.exists('a.out.wasm')
self.assertContained('hello, world!', run_js('a.out.js'))
# properly report source code errors, and stop there
self.clear()
assert not os.path.exists('a.out.js')
process = run_process([PYTHON, compiler, path_from_root('tests', 'hello_world_error' + suffix)], stdout=PIPE, stderr=PIPE, check=False)
assert not os.path.exists('a.out.js'), 'compilation failed, so no output file is expected'
assert len(process.stdout) == 0, process.stdout
assert process.returncode is not 0, 'Failed compilation must return a nonzero error code!'
self.assertNotContained('IOError', process.stderr) # no python stack
self.assertNotContained('Traceback', process.stderr) # no python stack
self.assertContained('error: invalid preprocessing directive', process.stderr)
self.assertContained(["error: use of undeclared identifier 'cheez", "error: unknown type name 'cheez'"], process.stderr)
self.assertContained('errors generated', process.stderr)
assert 'compiler frontend failed to generate LLVM bitcode, halting' in process.stderr.split('errors generated.')[1]
def test_emcc_2(self):
for compiler in [EMCC, EMXX]:
suffix = '.c' if compiler == EMCC else '.cpp'
# emcc src.cpp -c and emcc src.cpp -o src.[o|bc] ==> should give a .bc file
# regression check: -o js should create "js", with bitcode content
for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'src.so'], ['-o', 'js'], ['-O1', '-c', '-o', '/dev/null'], ['-O1', '-o', '/dev/null']]:
print('-c stuff', args)
if '/dev/null' in args and WINDOWS:
print('skip because windows')
continue
target = args[1] if len(args) == 2 else 'hello_world.o'
self.clear()
run_process([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix)] + args)
if args[-1] == '/dev/null':
print('(no output)')
continue
syms = Building.llvm_nm(target)
assert 'main' in syms.defs
if self.is_wasm_backend():
# wasm backend will also have '__original_main' or such
assert len(syms.defs) == 2
else:
assert len(syms.defs) == 1
if target == 'js': # make sure emcc can recognize the target as a bitcode file
shutil.move(target, target + '.bc')
target += '.bc'
run_process([PYTHON, compiler, target, '-o', target + '.js'])
self.assertContained('hello, world!', run_js(target + '.js'))
def test_emcc_3(self):
for compiler in [EMCC, EMXX]:
suffix = '.c' if compiler == EMCC else '.cpp'
os.chdir(self.get_dir())
self.clear()
# handle singleton archives
run_process([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '-o', 'a.bc'])
run_process([LLVM_AR, 'r', 'a.a', 'a.bc'], stdout=PIPE, stderr=PIPE)
run_process([PYTHON, compiler, 'a.a'])
self.assertContained('hello, world!', run_js('a.out.js'))
if not self.is_wasm_backend():
# emcc src.ll ==> generates .js
self.clear()
run_process([PYTHON, compiler, path_from_root('tests', 'hello_world.ll')])
self.assertContained('hello, world!', run_js('a.out.js'))
# emcc [..] -o [path] ==> should work with absolute paths
for path in [os.path.abspath(os.path.join('..', 'file1.js')), os.path.join('b_dir', 'file2.js')]:
print(path)
os.chdir(self.get_dir())
self.clear()
print(os.listdir(os.getcwd()))
os.makedirs('a_dir')
os.chdir('a_dir')
os.makedirs('b_dir')
# use single file so we don't have more files to clean up
run_process([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '-o', path, '-s', 'SINGLE_FILE=1'])
last = os.getcwd()
os.chdir(os.path.dirname(path))
self.assertContained('hello, world!', run_js(os.path.basename(path)))
os.chdir(last)
try_delete(path)
def test_emcc_4(self):
for compiler in [EMCC, EMXX]:
# Optimization: emcc src.cpp -o something.js [-Ox]. -O0 is the same as not specifying any optimization setting
for params, opt_level, bc_params, closure, has_malloc in [ # bc params are used after compiling to bitcode
(['-o', 'something.js'], 0, None, 0, 1),
(['-o', 'something.js', '-O0'], 0, None, 0, 0),
(['-o', 'something.js', '-O1'], 1, None, 0, 0),
(['-o', 'something.js', '-O1', '-g'], 1, None, 0, 0), # no closure since debug
(['-o', 'something.js', '-O2'], 2, None, 0, 1),
(['-o', 'something.js', '-O2', '-g'], 2, None, 0, 0),
(['-o', 'something.js', '-Os'], 2, None, 0, 1),
(['-o', 'something.js', '-O3'], 3, None, 0, 1),
# and, test compiling to bitcode first
(['-o', 'something.bc'], 0, [], 0, 0),
(['-o', 'something.bc', '-O0'], 0, [], 0, 0),
(['-o', 'something.bc', '-O1'], 1, ['-O1'], 0, 0),
(['-o', 'something.bc', '-O2'], 2, ['-O2'], 0, 0),
(['-o', 'something.bc', '-O3'], 3, ['-O3'], 0, 0),
(['-O1', '-o', 'something.bc'], 1, [], 0, 0),
# non-wasm
(['-s', 'WASM=0', '-o', 'something.js'], 0, None, 0, 1),
(['-s', 'WASM=0', '-o', 'something.js', '-O0'], 0, None, 0, 0),
(['-s', 'WASM=0', '-o', 'something.js', '-O1'], 1, None, 0, 0),
(['-s', 'WASM=0', '-o', 'something.js', '-O1', '-g'], 1, None, 0, 0), # no closure since debug
(['-s', 'WASM=0', '-o', 'something.js', '-O2'], 2, None, 0, 1),
(['-s', 'WASM=0', '-o', 'something.js', '-O2', '-g'], 2, None, 0, 0),
(['-s', 'WASM=0', '-o', 'something.js', '-Os'], 2, None, 0, 1),
(['-s', 'WASM=0', '-o', 'something.js', '-O3'], 3, None, 0, 1),
# and, test compiling to bitcode first
(['-s', 'WASM=0', '-o', 'something.bc'], 0, ['-s', 'WASM=0'], 0, 0),
(['-s', 'WASM=0', '-o', 'something.bc', '-O0'], 0, ['-s', 'WASM=0'], 0, 0),
(['-s', 'WASM=0', '-o', 'something.bc', '-O1'], 1, ['-s', 'WASM=0', '-O1'], 0, 0),
(['-s', 'WASM=0', '-o', 'something.bc', '-O2'], 2, ['-s', 'WASM=0', '-O2'], 0, 0),
(['-s', 'WASM=0', '-o', 'something.bc', '-O3'], 3, ['-s', 'WASM=0', '-O3'], 0, 0),
(['-s', 'WASM=0', '-O1', '-o', 'something.bc'], 1, ['-s', 'WASM=0'], 0, 0),
]:
if 'WASM=0' in params and self.is_wasm_backend():
continue
print(params, opt_level, bc_params, closure, has_malloc)
self.clear()
keep_debug = '-g' in params
args = [PYTHON, compiler, path_from_root('tests', 'hello_world_loop' + ('_malloc' if has_malloc else '') + '.cpp')] + params
print('..', args)
output = run_process(args, stdout=PIPE, stderr=PIPE)
assert len(output.stdout) == 0, output.stdout
if bc_params is not None:
assert os.path.exists('something.bc'), output.stderr
bc_args = [PYTHON, compiler, 'something.bc', '-o', 'something.js'] + bc_params
print('....', bc_args)
output = run_process(bc_args, stdout=PIPE, stderr=PIPE)
assert os.path.exists('something.js'), output.stderr
self.assertContained('hello, world!', run_js('something.js'))
# Verify optimization level etc. in the generated code
# XXX these are quite sensitive, and will need updating when code generation changes
generated = open('something.js').read()
main = self.get_func(generated, '_main') if 'function _main' in generated else generated
assert 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 should be used by default'
assert 'SAFE_HEAP' not in generated, 'safe heap should not be used by default'
assert ': while(' not in main, 'when relooping we also js-optimize, so there should be no labelled whiles'
if closure:
if opt_level == 0:
assert '._main =' in generated, 'closure compiler should have been run'
elif opt_level >= 1:
assert '._main=' in generated, 'closure compiler should have been run (and output should be minified)'
else:
# closure has not been run, we can do some additional checks. TODO: figure out how to do these even with closure
assert '._main = ' not in generated, 'closure compiler should not have been run'
if keep_debug:
assert ('switch (label)' in generated or 'switch (label | 0)' in generated) == (opt_level <= 0), 'relooping should be in opt >= 1'
assert ('assert(STACKTOP < STACK_MAX' in generated) == (opt_level == 0), 'assertions should be in opt == 0'
if 'WASM=0' in params:
if opt_level >= 2 and '-g' in params:
assert re.search('HEAP8\[\$?\w+ ?\+ ?\(+\$?\w+ ?', generated) or re.search('HEAP8\[HEAP32\[', generated) or re.search('[i$]\d+ & ~\(1 << [i$]\d+\)', generated), 'eliminator should create compound expressions, and fewer one-time vars' # also in -O1, but easier to test in -O2
looks_unminified = ' = {}' in generated and ' = []' in generated
looks_minified = '={}' in generated and '=[]' and ';var' in generated
assert not (looks_minified and looks_unminified)
if opt_level == 0 or '-g' in params:
assert looks_unminified
elif opt_level >= 2:
assert looks_minified
@no_wasm_backend('tests for asmjs optimzer')
def test_emcc_5(self):
for compiler in [EMCC, EMXX]:
# asm.js optimization levels
for params, test, text in [
(['-O2'], lambda generated: 'function addRunDependency' in generated, 'shell has unminified utilities'),
(['-O2', '--closure', '1'], lambda generated: 'function addRunDependency' not in generated and ';function' in generated, 'closure minifies the shell, removes whitespace'),
(['-O2', '--closure', '1', '-g1'], lambda generated: 'function addRunDependency' not in generated and ';function' not in generated, 'closure minifies the shell, -g1 makes it keep whitespace'),
(['-O2'], lambda generated: 'var b=0' in generated and 'function _main' not in generated, 'registerize/minify is run by default in -O2'),
(['-O2', '--minify', '0'], lambda generated: 'var b = 0' in generated and 'function _main' not in generated, 'minify is cancelled, but not registerize'),
(['-O2', '--js-opts', '0'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'js opts are cancelled'),
(['-O2', '-g'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize/minify is cancelled by -g'),
(['-O2', '-g0'], lambda generated: 'var b=0' in generated and 'function _main' not in generated, 'registerize/minify is run by default in -O2 -g0'),
(['-O2', '-g1'], lambda generated: 'var b = 0' in generated and 'function _main' not in generated, 'compress is cancelled by -g1'),
(['-O2', '-g2'], lambda generated: ('var b = 0' in generated or 'var i1 = 0' in generated) and 'function _main' in generated, 'minify is cancelled by -g2'),
(['-O2', '-g3'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'registerize is cancelled by -g3'),
(['-O2', '--profiling'], lambda generated: ('var b = 0' in generated or 'var i1 = 0' in generated) and 'function _main' in generated, 'similar to -g2'),
(['-O2', '-profiling'], lambda generated: ('var b = 0' in generated or 'var i1 = 0' in generated) and 'function _main' in generated, 'similar to -g2'),
(['-O2', '--profiling-funcs'], lambda generated: 'var b=0' in generated and '"use asm";var a=' in generated and 'function _main' in generated, 'very minified, but retain function names'),
(['-O2', '-profiling-funcs'], lambda generated: 'var b=0' in generated and '"use asm";var a=' in generated and 'function _main' in generated, 'very minified, but retain function names'),
(['-O2'], lambda generated: 'var b=0' in generated and '"use asm";var a=' in generated and 'function _main' not in generated, 'very minified, no function names'),
# (['-O2', '-g4'], lambda generated: 'var b=0' not in generated and 'var b = 0' not in generated and 'function _main' in generated, 'same as -g3 for now'),
(['-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' in generated, 'no inlining without opts'),
([], lambda generated: 'Module["_dump"]' not in generated, 'dump is not exported by default'),
(['-s', 'EXPORTED_FUNCTIONS=["_main", "_dump"]'], lambda generated: 'asm["_dump"];' in generated, 'dump is now exported'),
(['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'),
([], lambda generated: '// Sometimes an existing Module' in generated, 'without opts, comments in shell code'),
(['-O2'], lambda generated: '// Sometimes an existing Module' not in generated, 'with opts, no comments in shell code'),
(['-O2', '-g2'], lambda generated: '// Sometimes an existing Module' not in generated, 'with -g2, no comments in shell code'),
(['-O2', '-g3'], lambda generated: '// Sometimes an existing Module' in generated, 'with -g3, yes comments in shell code'),
]:
print(params, text)
self.clear()
run_process([PYTHON, compiler, path_from_root('tests', 'hello_world_loop.cpp'), '-o', 'a.out.js', '-s', 'WASM=0'] + params)
self.assertContained('hello, world!', run_js('a.out.js'))
assert test(open('a.out.js').read()), text
def test_emcc_6(self):
for compiler in [EMCC, EMXX]:
# Compiling two source files into a final JS.
for args, target in [([], 'a.out.js'), (['-o', 'combined.js'], 'combined.js')]:
self.clear()
run_process([PYTHON, compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp')] + args)
self.assertContained('side got: hello from main, over', run_js(target))
# Compiling two files with -c will generate separate .bc files
self.clear()
expect_error = '-o' in args # specifying -o and -c is an error
proc = run_process([PYTHON, compiler, path_from_root('tests', 'twopart_main.cpp'), path_from_root('tests', 'twopart_side.cpp'), '-c'] + args,
stderr=PIPE, check=False)
if expect_error:
self.assertContained('fatal error', proc.stderr)
self.assertNotEqual(proc.returncode, 0)
continue
self.assertEqual(proc.returncode, 0)
assert not os.path.exists(target), 'We should only have created bitcode here: ' + proc.stderr
# Compiling one of them alone is expected to fail
proc = run_process([PYTHON, compiler, 'twopart_main.o', '-O1', '-g', '-s', 'WARN_ON_UNDEFINED_SYMBOLS=0'] + args, stdout=PIPE, stderr=PIPE)
self.assertContained('missing function', run_js(target, stderr=STDOUT, assert_returncode=None))
try_delete(target)
# Combining those object files into js should work
proc = run_process([PYTHON, compiler, 'twopart_main.o', 'twopart_side.o'] + args, stdout=PIPE, stderr=PIPE)
assert os.path.exists(target), proc.stdout + '\n' + proc.stderr
self.assertContained('side got: hello from main, over', run_js(target))
# Combining object files into another object should also work
try_delete(target)
assert not os.path.exists(target)
proc = run_process([PYTHON, compiler, 'twopart_main.o', 'twopart_side.o', '-o', 'combined.o'] + args, stdout=PIPE, stderr=PIPE)
syms = Building.llvm_nm('combined.o')
assert len(syms.defs) in (2, 3) and 'main' in syms.defs, 'Should be two functions (and in the wasm backend, also __original_main)'
proc = run_process([PYTHON, compiler, 'combined.o', '-o', 'combined.o.js'], stdout=PIPE, stderr=PIPE)
assert len(proc.stdout) == 0, proc.stdout
assert os.path.exists('combined.o.js'), 'Expected %s to exist' % ('combined.o.js')
self.assertContained('side got: hello from main, over', run_js('combined.o.js'))
def test_emcc_7(self):
for compiler in [EMCC, EMXX]:
suffix = '.c' if compiler == EMCC else '.cpp'
# --js-transform <transform>
self.clear()
trans = 't.py'
with open(trans, 'w') as f:
f.write('''
import sys
f = open(sys.argv[1], 'a')
f.write('transformed!')
f.close()
''')
run_process([PYTHON, compiler, path_from_root('tests', 'hello_world' + suffix), '--js-transform', '%s t.py' % (PYTHON)])
self.assertIn('transformed!', open('a.out.js').read())
if self.is_wasm_backend():
# The remainder of this test requires asmjs support
return
for opts in [0, 1, 2, 3]:
print('mem init in', opts)
self.clear()
run_process([PYTHON, compiler, path_from_root('tests', 'hello_world.c'), '-s', 'WASM=0', '-O' + str(opts)])
if opts >= 2:
self.assertTrue(os.path.exists('a.out.js.mem'))
else:
self.assertFalse(os.path.exists('a.out.js.mem'))
@no_wasm_backend('testing asm vs wasm behavior')
def test_emcc_asm_v_wasm(self):
for opts in ([], ['-O1'], ['-O2'], ['-O3']):
print('opts', opts)
for mode in ([], ['-s', 'WASM=0'], ['-s', 'BINARYEN=0'], ['-s', 'WASM=1'], ['-s', 'BINARYEN=1']):
self.clear()
wasm = '=0' not in str(mode)
print(' mode', mode, 'wasm?', wasm)
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c')] + opts + mode)
assert os.path.exists('a.out.js')
assert os.path.exists('a.out.wasm') == wasm
for engine in JS_ENGINES:
print(' engine', engine)
out = run_js('a.out.js', engine=engine, stderr=PIPE, full_output=True)
self.assertContained('hello, world!', out)
if not wasm and engine == SPIDERMONKEY_ENGINE:
self.validate_asmjs(out)
if not wasm:
src = open('a.out.js').read()
if opts == []:
assert 'almost asm' in src
else:
assert 'use asm' in src
@uses_canonical_tmp
def test_emcc_cflags(self):
# see we print them out
# --cflags needs to set EMCC_DEBUG=1, which needs to create canonical temp directory.
output = run_process([PYTHON, EMCC, '--cflags'], stdout=PIPE, stderr=PIPE)
flags = output.stdout.strip()
self.assertContained(' '.join(Building.doublequote_spaces(shared.COMPILER_OPTS)), flags)
# check they work
cmd = [CLANG, path_from_root('tests', 'hello_world.cpp')] + shlex.split(flags.replace('\\', '\\\\')) + ['-c', '-emit-llvm', '-o', 'a.bc']
run_process(cmd)
run_process([PYTHON, EMCC, 'a.bc'])
self.assertContained('hello, world!', run_js('a.out.js'))
def test_emar_em_config_flag(self):
# Test that the --em-config flag is accepted but not passed down do llvm-ar.
# We expand this in case the EM_CONFIG is ~/.emscripten (default)
config = os.path.expanduser(shared.EM_CONFIG)
proc = run_process([PYTHON, EMAR, '--em-config', config, '-version'], stdout=PIPE, stderr=PIPE)
self.assertEqual(proc.stderr, "")
self.assertContained('LLVM', proc.stdout)
@no_wasm_backend("see https://bugs.llvm.org/show_bug.cgi?id=40470")
def test_cmake(self):
# Test all supported generators.
if WINDOWS:
generators = ['MinGW Makefiles', 'NMake Makefiles']
else:
generators = ['Unix Makefiles', 'Ninja', 'Eclipse CDT4 - Ninja']
def nmake_detect_error(configuration):
if Building.which(configuration['build'][0]):
return None
else:
return 'Skipping NMake test for CMake support, since nmake was not found in PATH. Run this test in Visual Studio command prompt to easily access nmake.'
def check_makefile(dirname):
assert os.path.exists(dirname + '/Makefile'), 'CMake call did not produce a Makefile!'
configurations = {'MinGW Makefiles' : {'prebuild': check_makefile, # noqa
'build' : ['mingw32-make'], # noqa
},
'NMake Makefiles' : {'detect' : nmake_detect_error, # noqa
'prebuild': check_makefile, # noqa
'build' : ['nmake', '/NOLOGO'], # noqa
},
'Unix Makefiles' : {'prebuild': check_makefile, # noqa
'build' : ['make'], # noqa
},
'Ninja' : {'build' : ['ninja'], # noqa
},
'Eclipse CDT4 - Ninja': {'build' : ['ninja'], # noqa
}
}
if WINDOWS:
emconfigure = path_from_root('emconfigure.bat')
else:
emconfigure = path_from_root('emconfigure')
for generator in generators:
conf = configurations[generator]
make = conf['build']
detector = conf.get('detect')
prebuild = conf.get('prebuild')
if detector:
error = detector(conf)
elif len(make) == 1 and not Building.which(make[0]):
# Use simple test if applicable
error = 'Skipping %s test for CMake support, since it could not be detected.' % generator
else:
error = None
if error:
print(error)
continue
# ('directory to the test', 'output filename', ['extra args to pass to
# CMake']) Testing all combinations would be too much work and the test
# would take 10 minutes+ to finish (CMake feature detection is slow), so
# combine multiple features into one to try to cover as much as possible
# while still keeping this test in sensible time limit.
cases = [
('target_js', 'test_cmake.js', ['-DCMAKE_BUILD_TYPE=Debug']),
('target_html', 'hello_world_gles.html', ['-DCMAKE_BUILD_TYPE=Release', '-DBUILD_SHARED_LIBS=OFF']),
('target_library', 'libtest_cmake.a', ['-DCMAKE_BUILD_TYPE=MinSizeRel', '-DBUILD_SHARED_LIBS=OFF']),
('target_library', 'libtest_cmake.a', ['-DCMAKE_BUILD_TYPE=RelWithDebInfo', '-DCPP_LIBRARY_TYPE=STATIC']),
('target_library', 'libtest_cmake.so', ['-DCMAKE_BUILD_TYPE=Release', '-DBUILD_SHARED_LIBS=ON']),
('target_library', 'libtest_cmake.so', ['-DCMAKE_BUILD_TYPE=Release', '-DBUILD_SHARED_LIBS=ON', '-DCPP_LIBRARY_TYPE=SHARED']),
('stdproperty', 'helloworld.js', [])
]
for test_dir, output_file, cmake_args in cases:
cmakelistsdir = path_from_root('tests', 'cmake', test_dir)
with temp_directory(self.get_dir()) as tempdirname:
# Run Cmake
cmd = [emconfigure, 'cmake'] + cmake_args + ['-G', generator, cmakelistsdir]
env = os.environ.copy()
# https://github.com/emscripten-core/emscripten/pull/5145: Check that CMake works even if EMCC_SKIP_SANITY_CHECK=1 is passed.
if test_dir == 'target_html':
env['EMCC_SKIP_SANITY_CHECK'] = '1'
print(str(cmd))
ret = run_process(cmd, env=env, stdout=None if EM_BUILD_VERBOSE >= 2 else PIPE, stderr=None if EM_BUILD_VERBOSE >= 1 else PIPE)
if ret.stderr is not None and len(ret.stderr.strip()):
print(ret.stderr) # If there were any errors, print them directly to console for diagnostics.
if ret.stderr is not None and 'error' in ret.stderr.lower():
print('Failed command: ' + ' '.join(cmd))
print('Result:\n' + ret.stderr)
self.fail('cmake call failed!')
if prebuild:
prebuild(tempdirname)
# Build
cmd = make
if EM_BUILD_VERBOSE >= 3 and 'Ninja' not in generator:
cmd += ['VERBOSE=1']
ret = run_process(cmd, stdout=None if EM_BUILD_VERBOSE >= 2 else PIPE)
if ret.stderr is not None and len(ret.stderr.strip()):
print(ret.stderr) # If there were any errors, print them directly to console for diagnostics.
if ret.stdout is not None and 'error' in ret.stdout.lower() and '0 error(s)' not in ret.stdout.lower():
print('Failed command: ' + ' '.join(cmd))
print('Result:\n' + ret.stdout)
self.fail('make failed!')
assert os.path.exists(tempdirname + '/' + output_file), 'Building a cmake-generated Makefile failed to produce an output file %s!' % tempdirname + '/' + output_file
# Run through node, if CMake produced a .js file.
if output_file.endswith('.js'):
ret = run_process(NODE_JS + [tempdirname + '/' + output_file], stdout=PIPE).stdout
self.assertTextDataIdentical(open(cmakelistsdir + '/out.txt').read().strip(), ret.strip())
# Test that the various CMAKE_xxx_COMPILE_FEATURES that are advertised for the Emscripten toolchain match with the actual language features that Clang supports.
# If we update LLVM version and this test fails, copy over the new advertised features from Clang and place them to cmake/Modules/Platform/Emscripten.cmake.
@no_windows('Skipped on Windows because CMake does not configure native Clang builds well on Windows.')
def test_cmake_compile_features(self):
with temp_directory(self.get_dir()):
cmd = ['cmake', '-DCMAKE_C_COMPILER=' + CLANG_CC, '-DCMAKE_CXX_COMPILER=' + CLANG_CPP, path_from_root('tests', 'cmake', 'stdproperty')]
print(str(cmd))
native_features = run_process(cmd, stdout=PIPE).stdout
if WINDOWS:
emconfigure = path_from_root('emcmake.bat')
else:
emconfigure = path_from_root('emcmake')
with temp_directory(self.get_dir()):
cmd = [emconfigure, 'cmake', path_from_root('tests', 'cmake', 'stdproperty')]
print(str(cmd))
emscripten_features = run_process(cmd, stdout=PIPE).stdout
native_features = '\n'.join([x for x in native_features.split('\n') if '***' in x])
emscripten_features = '\n'.join([x for x in emscripten_features.split('\n') if '***' in x])
self.assertTextDataIdentical(native_features, emscripten_features)
# Tests that it's possible to pass C++11 or GNU++11 build modes to CMake by building code that needs C++11 (embind)
def test_cmake_with_embind_cpp11_mode(self):
for args in [[], ['-DNO_GNU_EXTENSIONS=1']]:
with temp_directory(self.get_dir()) as tempdirname:
configure = [path_from_root('emcmake.bat' if WINDOWS else 'emcmake'), 'cmake', path_from_root('tests', 'cmake', 'cmake_with_emval')] + args
print(str(configure))
run_process(configure)
build = ['cmake', '--build', '.']
print(str(build))
run_process(build)
ret = run_process(NODE_JS + [os.path.join(tempdirname, 'cpp_with_emscripten_val.js')], stdout=PIPE).stdout.strip()
if '-DNO_GNU_EXTENSIONS=1' in args:
self.assertTextDataIdentical('Hello! __STRICT_ANSI__: 1, __cplusplus: 201103', ret)
else:
self.assertTextDataIdentical('Hello! __STRICT_ANSI__: 0, __cplusplus: 201103', ret)
# Tests that the Emscripten CMake toolchain option
# -DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=ON works.
def test_cmake_bitcode_static_libraries(self):
if WINDOWS:
emcmake = path_from_root('emcmake.bat')
else:
emcmake = path_from_root('emcmake')
# Test that building static libraries by default generates UNIX archives (.a, with the emar tool)
self.clear()
run_process([emcmake, 'cmake', path_from_root('tests', 'cmake', 'static_lib')])
run_process([Building.which('cmake'), '--build', '.'])
assert Building.is_ar('libstatic_lib.a')
run_process([PYTHON, EMAR, 'x', 'libstatic_lib.a'])
found = False # hashing makes the object name random
for x in os.listdir('.'):
if x.endswith('.o'):
found = True
if self.is_wasm_backend():
assert Building.is_wasm(x)
else:
assert Building.is_bitcode(x)
assert found
# Test that passing the -DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=ON
# directive causes CMake to generate LLVM bitcode files as static libraries
# (.bc)
self.clear()
run_process([emcmake, 'cmake', '-DEMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=ON', path_from_root('tests', 'cmake', 'static_lib')])
run_process([Building.which('cmake'), '--build', '.'])
if self.is_wasm_backend():
assert Building.is_wasm('libstatic_lib.bc')
else:
assert Building.is_bitcode('libstatic_lib.bc')
assert not Building.is_ar('libstatic_lib.bc')
# Test that one is able to fake custom suffixes for static libraries.
# (sometimes projects want to emulate stuff, and do weird things like files
# with ".so" suffix which are in fact either ar archives or bitcode files)
self.clear()
run_process([emcmake, 'cmake', '-DSET_FAKE_SUFFIX_IN_PROJECT=1', path_from_root('tests', 'cmake', 'static_lib')])
run_process([Building.which('cmake'), '--build', '.'])
assert Building.is_ar('myprefix_static_lib.somecustomsuffix')
# Tests that the CMake variable EMSCRIPTEN_VERSION is properly provided to user CMake scripts
def test_cmake_emscripten_version(self):
if WINDOWS:
emcmake = path_from_root('emcmake.bat')
else:
emcmake = path_from_root('emcmake')
run_process([emcmake, 'cmake', path_from_root('tests', 'cmake', 'emscripten_version')])
def test_system_include_paths(self):
# Verify that all default include paths are within `emscripten/system`
def verify_includes(stderr):
self.assertContained('<...> search starts here:', stderr)
assert stderr.count('End of search list.') == 1, stderr
start = stderr.index('<...> search starts here:')
end = stderr.index('End of search list.')
includes = stderr[start:end]
includes = [i.strip() for i in includes.splitlines()[1:-1]]
for i in includes:
self.assertContained(path_from_root('system'), i)
err = run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-v'], stderr=PIPE).stderr
verify_includes(err)
err = run_process([PYTHON, EMXX, path_from_root('tests', 'hello_world.cpp'), '-v'], stderr=PIPE).stderr
verify_includes(err)
def test_failure_error_code(self):
for compiler in [EMCC, EMXX]:
# Test that if one file is missing from the build, then emcc shouldn't succeed, and shouldn't produce an output file.
proc = run_process([PYTHON, compiler, path_from_root('tests', 'hello_world.c'), 'this_file_is_missing.c', '-o', 'out.js'], stderr=PIPE, check=False)
self.assertNotEqual(proc.returncode, 0)
self.assertFalse(os.path.exists('out.js'))
def test_use_cxx(self):
create_test_file('empty_file', ' ')
dash_xc = run_process([PYTHON, EMCC, '-v', '-xc', 'empty_file'], stderr=PIPE).stderr
self.assertNotContained('-std=c++03', dash_xc)
dash_xcpp = run_process([PYTHON, EMCC, '-v', '-xc++', 'empty_file'], stderr=PIPE).stderr
self.assertContained('-std=c++03', dash_xcpp)
def test_cxx03(self):
for compiler in [EMCC, EMXX]:
run_process([PYTHON, compiler, path_from_root('tests', 'hello_cxx03.cpp')])
def test_cxx11(self):
for std in ['-std=c++11', '--std=c++11']:
for compiler in [EMCC, EMXX]:
run_process([PYTHON, compiler, std, path_from_root('tests', 'hello_cxx11.cpp')])
# Regression test for issue #4522: Incorrect CC vs CXX detection
def test_incorrect_c_detection(self):
create_test_file('test.c', 'foo\n')
for compiler in [EMCC, EMXX]:
run_process([PYTHON, compiler, '--bind', '--embed-file', 'test.c', path_from_root('tests', 'hello_world.cpp')])
def test_odd_suffixes(self):
for suffix in ['CPP', 'c++', 'C++', 'cxx', 'CXX', 'cc', 'CC', 'i', 'ii']:
if self.is_wasm_backend() and suffix == 'ii':
# wasm backend treats .i and .ii specially and considers them already
# pre-processed. Because if this is strips all the -D command line
# flags, including the __EMSCRIPTEN__ define, which makes this fail
# to compile since libcxx/__config depends in __EMSCRIPTEN__.
continue
self.clear()
print(suffix)
shutil.copyfile(path_from_root('tests', 'hello_world.c'), 'test.' + suffix)
run_process([PYTHON, EMCC, self.in_dir('test.' + suffix)])
self.assertContained('hello, world!', run_js('a.out.js'))
for suffix in ['lo']:
self.clear()
print(suffix)
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', 'binary.' + suffix])
run_process([PYTHON, EMCC, 'binary.' + suffix])
self.assertContained('hello, world!', run_js('a.out.js'))
def test_ubsan(self):
create_test_file('test.cpp', r'''
#include <vector>
#include <stdio.h>
class Test {
public:
std::vector<int> vector;
};
Test globalInstance;
int main() {
printf("hello, world!\n");
return 0;
}
''')
run_process([PYTHON, EMCC, 'test.cpp', '-fsanitize=undefined'])
self.assertContained('hello, world!', run_js('a.out.js'))
def test_ubsan_add_overflow(self):
create_test_file('test.cpp', r'''
#include <stdio.h>
int main(int argc, char **argv) {
printf("hello, world!\n");
fflush(stdout);
int k = 0x7fffffff;
k += argc;
return k;
}
''')
run_process([PYTHON, EMCC, 'test.cpp', '-fsanitize=undefined'])
output = run_js('a.out.js', assert_returncode=1, stderr=STDOUT)
self.assertContained('hello, world!', output)
self.assertContained('Undefined behavior! ubsan_handle_add_overflow:', output)
@no_wasm_backend()
def test_asm_minify(self):
def test(args):
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop_malloc.cpp'), '-s', 'WASM=0'] + args)
self.assertContained('hello, world!', run_js('a.out.js'))
return open('a.out.js').read()
src = test([])
assert 'function _malloc' in src
src = test(['-O2', '-s', 'ASM_JS=1'])
normal_size = len(src)
print('normal', normal_size)
assert 'function _malloc' not in src
src = test(['-O2', '-s', 'ASM_JS=1', '--minify', '0'])
unminified_size = len(src)
print('unminified', unminified_size)
assert unminified_size > normal_size
assert 'function _malloc' not in src
src = test(['-O2', '-s', 'ASM_JS=1', '-g'])
debug_size = len(src)
print('debug', debug_size)
assert debug_size > unminified_size
assert 'function _malloc' in src
@no_wasm_backend('test asm only function pointer handling')
def test_dangerous_func_cast(self):
src = r'''
#include <stdio.h>
typedef void (*voidfunc)();
int my_func() {
printf("my func\n");
return 10;
}
int main(int argc, char **argv) {
voidfunc fps[10];
for (int i = 0; i < 10; i++)
fps[i] = (i == argc) ? (void (*)())my_func : NULL;
fps[2 * (argc-1) + 1]();
return 0;
}
'''
create_test_file('src.c', src)
def test(args, expected):
print(args, expected)
run_process([PYTHON, EMCC, 'src.c'] + args, stderr=PIPE)
self.assertContained(expected, run_js('a.out.js', stderr=PIPE, full_output=True, assert_returncode=None))
if self.is_wasm_backend():
return
print('in asm.js')
run_process([PYTHON, EMCC, 'src.c', '-s', 'WASM=0'] + args, stderr=PIPE)
self.assertContained(expected, run_js('a.out.js', stderr=PIPE, full_output=True, assert_returncode=None))
# TODO: emulation function support in wasm is imperfect
print('with emulated function pointers in asm.js')
run_process([PYTHON, EMCC, '-s', 'WASM=0', 'src.c', '-s', 'ASSERTIONS=1'] + args + ['-s', 'EMULATED_FUNCTION_POINTERS=1'], stderr=PIPE)
out = run_js('a.out.js', stderr=PIPE, full_output=True, assert_returncode=None)
self.assertContained(expected, out)
# fastcomp. all asm, so it can't just work with wrong sigs. but,
# ASSERTIONS=2 gives much better info to debug
# Case 1: No useful info, but does mention ASSERTIONS
test(['-O1'], 'ASSERTIONS')
# Case 2: Some useful text
test(['-O1', '-s', 'ASSERTIONS=1'], [
"Invalid function pointer called with signature 'v'. Perhaps this is an invalid value",
'Build with ASSERTIONS=2 for more info'
])
# Case 3: actually useful identity of the bad pointer, with comparisons to
# what it would be in other types/tables
test(['-O1', '-s', 'ASSERTIONS=2'], [
"Invalid function pointer '0' called with signature 'v'. Perhaps this is an invalid value",
'This pointer might make sense in another type signature:',
"Invalid function pointer '1' called with signature 'v'. Perhaps this is an invalid value",
"i: asm['_my_func']"
])
# Case 4: emulate so it works
test(['-O1', '-s', 'EMULATE_FUNCTION_POINTER_CASTS=1'], 'my func\n')
def test_emulate_function_pointer_casts_assertions_2(self):
# check empty tables work with assertions 2 in this mode (#6554)
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'EMULATED_FUNCTION_POINTERS=1', '-s', 'ASSERTIONS=2'])
def test_l_link(self):
# Linking with -lLIBNAME and -L/DIRNAME should work, also should work with spaces
def build(path, args):
run_process([PYTHON, EMCC, self.in_dir(*path)] + args)
create_test_file('main.cpp', '''
extern void printey();
int main() {
printey();
return 0;
}
''')
try:
os.makedirs('libdir')
except:
pass
create_test_file(os.path.join('libdir', 'libfile.cpp'), '''
#include <stdio.h>
void printey() {
printf("hello from lib\\n");
}
''')
libfile = self.in_dir('libdir', 'libfile.so')
aout = 'a.out.js'
# Test linking the library built here by emcc
build(['libdir', 'libfile.cpp'], ['-c'])
shutil.move('libfile.o', libfile)
build(['main.cpp'], ['-L' + 'libdir', '-lfile'])
self.assertContained('hello from lib', run_js(aout))
# Also test execution with `-l c` and space-separated library linking syntax
os.remove(aout)
build(['libdir', 'libfile.cpp'], ['-c', '-l', 'c'])
shutil.move('libfile.o', libfile)
build(['main.cpp'], ['-L', 'libdir', '-l', 'file'])
self.assertContained('hello from lib', run_js(aout))
assert not os.path.exists('a.out') and not os.path.exists('a.exe'), 'Must not leave unneeded linker stubs'
def test_commons_link(self):
create_test_file('a.h', r'''
#if !defined(A_H)
#define A_H
extern int foo[8];
#endif
''')
create_test_file('a.c', r'''
#include "a.h"
int foo[8];
''')
create_test_file('main.c', r'''
#include <stdio.h>
#include "a.h"
int main() {
printf("|%d|\n", foo[0]);
return 0;
}
''')
run_process([PYTHON, EMCC, '-o', 'a.o', 'a.c'])
run_process([PYTHON, EMAR, 'rv', 'library.a', 'a.o'])
run_process([PYTHON, EMCC, '-o', 'main.o', 'main.c'])
run_process([PYTHON, EMCC, '-o', 'a.js', 'main.o', 'library.a'])
self.assertContained('|0|', run_js('a.js'))
@no_wasm_backend('outlining is an asmjs only feature')
def test_outline(self):
if WINDOWS and not Building.which('mingw32-make'):
self.skipTest('Skipping other.test_outline: This test requires "mingw32-make" tool in PATH on Windows to drive a Makefile build of zlib')
def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp'):
print(name)
def measure_funcs(filename):
i = 0
start = -1
curr = None
ret = {}
for line in open(filename):
i += 1
if line.startswith('function '):
start = i
curr = line
elif line.startswith('}') and curr:
size = i - start
ret[curr] = size
curr = None
return ret
for debug, outlining_limits in [
([], (1000,)),
(['-g1'], (1000,)),
(['-g2'], (1000,)),
(['-g'], (100, 250, 500, 1000, 2000, 5000, 0))
]:
for outlining_limit in outlining_limits:
print('\n', Building.COMPILER_TEST_OPTS, debug, outlining_limit, '\n')
# TODO: test without -g3, tell all sorts
run_process([PYTHON, EMCC, src] + libs + ['-o', 'test.js', '-O2', '-s', 'WASM=0'] + debug + ['-s', 'OUTLINING_LIMIT=%d' % outlining_limit] + args)
shutil.copyfile('test.js', '%d_test.js' % outlining_limit)
for engine in JS_ENGINES:
if engine == V8_ENGINE:
continue # ban v8, weird failures
out = run_js('test.js', engine=engine, stderr=PIPE, full_output=True)
self.assertContained(expected, out)
if engine == SPIDERMONKEY_ENGINE:
self.validate_asmjs(out)
if debug == ['-g']:
low = expected_ranges[outlining_limit][0]
seen = max(measure_funcs('test.js').values())
high = expected_ranges[outlining_limit][1]
print(Building.COMPILER_TEST_OPTS, outlining_limit, ' ', low, '<=', seen, '<=', high)
self.assertLess(low, seen)
self.assertLess(seen, high)
for test_opts, expected_ranges in [
([], {
100: (150, 500), # noqa
250: (150, 800), # noqa
500: (150, 900), # noqa
1000: (200, 1000), # noqa
2000: (250, 2000), # noqa
5000: (500, 5000), # noqa
0: (1000, 5000) # noqa
}),
(['-O2'], {
100: (0, 1600), # noqa
250: (0, 1600), # noqa
500: (0, 1600), # noqa
1000: (0, 1600), # noqa
2000: (0, 2000), # noqa
5000: (0, 5000), # noqa
0: (0, 5000) # noqa
}),
]:
Building.COMPILER_TEST_OPTS = test_opts
test('zlib', path_from_root('tests', 'zlib', 'example.c'),
get_zlib_library(self),
open(path_from_root('tests', 'zlib', 'ref.txt')).read(),
expected_ranges,
args=['-I' + path_from_root('tests', 'zlib')], suffix='c')
@no_wasm_backend('outlining is an asmjs only feature')
def test_outline_stack(self):
create_test_file('src.c', r'''
#include <stdio.h>
#include <stdlib.h>
void *p = NULL;
void foo() {
int * x = alloca(1);
};
int main() {
printf("Hello, world!\n");
for (int i=0; i<100000; i++) {
free(p);
foo();
}
}
''')
for limit in [0, 1000, 2500, 5000]:
print(limit)
run_process([PYTHON, EMCC, 'src.c', '-s', 'ASSERTIONS=2', '-s', 'OUTLINING_LIMIT=%d' % limit, '-s', 'TOTAL_STACK=10000'])
self.assertContained('Hello, world!', run_js('a.out.js'))
@no_windows('Windows does not support symlinks')
def test_symlink(self):
create_test_file('foobar.xxx', 'int main(){ return 0; }')
os.symlink('foobar.xxx', 'foobar.c')
run_process([PYTHON, EMCC, 'foobar.c', '-o', 'foobar.bc'])
try_delete('foobar.bc')
try_delete('foobar.xxx')
try_delete('foobar.c')
create_test_file('foobar.c', 'int main(){ return 0; }')
os.symlink('foobar.c', 'foobar.xxx')
run_process([PYTHON, EMCC, 'foobar.xxx', '-o', 'foobar.bc'])
def test_multiply_defined_libsymbols(self):
lib = "int mult() { return 1; }"
lib_name = 'libA.c'
create_test_file(lib_name, lib)
a2 = "void x() {}"
a2_name = 'a2.c'
create_test_file(a2_name, a2)
b2 = "void y() {}"
b2_name = 'b2.c'
create_test_file(b2_name, b2)
main = r'''
#include <stdio.h>
int mult();
int main() {
printf("result: %d\n", mult());
return 0;
}
'''
main_name = 'main.c'
create_test_file(main_name, main)
Building.emcc(lib_name, output_filename='libA.so')
Building.emcc(a2_name, ['-L.', '-lA'])
Building.emcc(b2_name, ['-L.', '-lA'])
Building.emcc(main_name, ['-L.', '-lA', a2_name + '.o', b2_name + '.o'], output_filename='a.out.js')
self.assertContained('result: 1', run_js('a.out.js'))
def test_multiply_defined_libsymbols_2(self):
a = "int x() { return 55; }"
a_name = 'a.c'
create_test_file(a_name, a)
b = "int y() { return 2; }"
b_name = 'b.c'
create_test_file(b_name, b)
c = "int z() { return 5; }"
c_name = 'c.c'
create_test_file(c_name, c)
main = r'''
#include <stdio.h>
int x();
int y();
int z();
int main() {
printf("result: %d\n", x() + y() + z());
return 0;
}
'''
main_name = 'main.c'
create_test_file(main_name, main)
Building.emcc(a_name) # a.c.o
Building.emcc(b_name) # b.c.o
Building.emcc(c_name) # c.c.o
lib_name = 'libLIB.a'
Building.emar('cr', lib_name, [a_name + '.o', b_name + '.o']) # libLIB.a with a and b
# a is in the lib AND in an .o, so should be ignored in the lib. We do still need b from the lib though
Building.emcc(main_name, [a_name + '.o', c_name + '.o', '-L.', '-lLIB'], output_filename='a.out.js')
self.assertContained('result: 62', run_js('a.out.js'))
@no_wasm_backend('not relevent with lld')
def test_link_group(self):
lib_src_name = 'lib.c'
create_test_file(lib_src_name, 'int x() { return 42; }')
main_name = 'main.c'
create_test_file(main_name, r'''
#include <stdio.h>
int x();
int main() {
printf("result: %d\n", x());
return 0;
}
''')
Building.emcc(lib_src_name) # lib.c.o
lib_name = 'libLIB.a'
Building.emar('cr', lib_name, [lib_src_name + '.o']) # libLIB.a with lib.c.o
def test(lib_args, err_expected):
print(err_expected)
output = run_process([PYTHON, EMCC, main_name, '-o', 'a.out.js'] + lib_args, stdout=PIPE, stderr=PIPE, check=not err_expected)
if err_expected:
self.assertContained(err_expected, output.stderr)
else:
self.assertNotContained('undefined symbol', output.stderr)
out_js = 'a.out.js'
assert os.path.exists(out_js), output.stdout + '\n' + output.stderr
self.assertContained('result: 42', run_js(out_js))
test(['-Wl,--start-group', lib_name, '-Wl,--start-group'], 'Nested --start-group, missing --end-group?')
test(['-Wl,--end-group', lib_name, '-Wl,--start-group'], '--end-group without --start-group')
test(['-Wl,--start-group', lib_name, '-Wl,--end-group'], None)
test(['-Wl,--start-group', lib_name], None)
print('embind test with groups')
main_name = 'main.cpp'
create_test_file(main_name, r'''
#include <stdio.h>
#include <emscripten/val.h>
using namespace emscripten;
extern "C" int x();
int main() {
int y = -x();
y = val::global("Math").call<int>("abs", y);
printf("result: %d\n", y);
return 0;
}
''')
test(['-Wl,--start-group', lib_name, '-Wl,--end-group', '--bind'], None)
def test_whole_archive(self):
# Verify that -Wl,--whole-archive includes the static constructor from the
# otherwise unreferenced library.
run_process([PYTHON, EMCC, '-c', '-o', 'main.o', path_from_root('tests', 'test_whole_archive', 'main.c')])
run_process([PYTHON, EMCC, '-c', '-o', 'testlib.o', path_from_root('tests', 'test_whole_archive', 'testlib.c')])
run_process([PYTHON, EMAR, 'crs', 'libtest.a', 'testlib.o'])
run_process([PYTHON, EMCC, '-Wl,--whole-archive', 'libtest.a', '-Wl,--no-whole-archive', 'main.o'])
self.assertContained('foo is: 42\n', run_js('a.out.js'))
run_process([PYTHON, EMCC, '-Wl,-whole-archive', 'libtest.a', '-Wl,-no-whole-archive', 'main.o'])
self.assertContained('foo is: 42\n', run_js('a.out.js'))
# Verify the --no-whole-archive prevents the inclusion of the ctor
run_process([PYTHON, EMCC, '-Wl,-whole-archive', '-Wl,--no-whole-archive', 'libtest.a', 'main.o'])
self.assertContained('foo is: 0\n', run_js('a.out.js'))
def test_link_group_bitcode(self):
create_test_file('1.c', r'''
int f(void);
int main() {
f();
return 0;
}
''')
create_test_file('2.c', r'''
#include <stdio.h>
int f() {
printf("Hello\n");
return 0;
}
''')
run_process([PYTHON, EMCC, '-o', '1.o', '1.c'])
run_process([PYTHON, EMCC, '-o', '2.o', '2.c'])
run_process([PYTHON, EMAR, 'crs', '2.a', '2.o'])
run_process([PYTHON, EMCC, '-o', 'out.bc', '-Wl,--start-group', '2.a', '1.o', '-Wl,--end-group'])
run_process([PYTHON, EMCC, 'out.bc'])
self.assertContained('Hello', run_js('a.out.js'))
@no_wasm_backend('lld resolves circular lib dependencies')
def test_circular_libs(self):
def tmp_source(name, code):
with open(name, 'w') as f:
f.write(code)
tmp_source('a.c', 'int z(); int x() { return z(); }')
tmp_source('b.c', 'int x(); int y() { return x(); } int z() { return 42; }')
tmp_source('c.c', 'int q() { return 0; }')
tmp_source('main.c', r'''
#include <stdio.h>
int y();
int main() {
printf("result: %d\n", y());
return 0;
}
''')
Building.emcc('a.c') # a.c.o
Building.emcc('b.c') # b.c.o
Building.emcc('c.c')
Building.emar('cr', 'libA.a', ['a.c.o', 'c.c.o'])
Building.emar('cr', 'libB.a', ['b.c.o', 'c.c.o'])
args = ['main.c', '-o', 'a.out.js']
libs_list = ['libA.a', 'libB.a']
# 'libA.a' does not satisfy any symbols from main, so it will not be included,
# and there will be an undefined symbol.
proc = run_process([PYTHON, EMCC] + args + libs_list, stdout=PIPE, stderr=STDOUT, check=False)
if proc.returncode == 0:
print(proc.stdout)
self.assertNotEqual(proc.returncode, 0)
self.assertContained('error: undefined symbol: x', proc.stdout)
# -Wl,--start-group and -Wl,--end-group around the libs will cause a rescan
# of 'libA.a' after 'libB.a' adds undefined symbol "x", so a.c.o will now be
# included (and the link will succeed).
libs = ['-Wl,--start-group'] + libs_list + ['-Wl,--end-group']
output = run_process([PYTHON, EMCC] + args + libs, stdout=PIPE, stderr=PIPE)
out_js = 'a.out.js'
assert os.path.exists(out_js), output.stdout + '\n' + output.stderr
self.assertContained('result: 42', run_js(out_js))
# -( and -) should also work.
args = ['main.c', '-o', 'a2.out.js']
libs = ['-Wl,-('] + libs_list + ['-Wl,-)']
output = run_process([PYTHON, EMCC] + args + libs, stdout=PIPE, stderr=PIPE)
out_js = 'a2.out.js'
assert os.path.exists(out_js), output.stdout + '\n' + output.stderr
self.assertContained('result: 42', run_js(out_js))
@needs_dlfcn
def test_redundant_link(self):
lib = "int mult() { return 1; }"
lib_name = 'libA.c'
create_test_file(lib_name, lib)
main = r'''
#include <stdio.h>
int mult();
int main() {
printf("result: %d\n", mult());
return 0;
}
'''
main_name = 'main.c'
create_test_file(main_name, main)
Building.emcc(lib_name, output_filename='libA.so')
Building.emcc(main_name, ['libA.so'] * 2, output_filename='a.out.js')
self.assertContained('result: 1', run_js('a.out.js'))
@no_wasm_backend('archive contents handling differ with lld')
def test_dot_a_all_contents_invalid(self):
# check that we warn if an object file in a .a is not valid bitcode.
# do not silently ignore native object files, which may have been
# built by mistake
create_test_file('side.cpp', r'''int side() { return 5; }''')
create_test_file('main.cpp', r'''extern int side(); int main() { return side(); }''')
run_process([CLANG, 'side.cpp', '-c', '-o', 'native.o'])
run_process([PYTHON, EMAR, 'crs', 'foo.a', 'native.o'])
proc = run_process([PYTHON, EMCC, 'main.cpp', 'foo.a', '-s', 'ERROR_ON_UNDEFINED_SYMBOLS=0'], stderr=PIPE)
self.assertContained('is not a valid object file for emscripten, cannot link', proc.stderr)
def test_export_all(self):
lib = r'''
#include <stdio.h>
void libf1() { printf("libf1\n"); }
void libf2() { printf("libf2\n"); }
'''
lib_name = 'lib.c'
create_test_file(lib_name, lib)
create_test_file('main.js', '''
var Module = {
onRuntimeInitialized: function() {
_libf1();
_libf2();
}
};
''')
Building.emcc(lib_name, ['-s', 'EXPORT_ALL=1', '-s', 'LINKABLE=1', '--pre-js', 'main.js'], output_filename='a.out.js')
self.assertContained('libf1\nlibf2\n', run_js('a.out.js'))
def test_stdin(self):
def _test():
for engine in JS_ENGINES:
if engine == V8_ENGINE:
continue # no stdin support in v8 shell
engine[0] = os.path.normpath(engine[0])
print(engine, file=sys.stderr)
# work around a bug in python's subprocess module
# (we'd use run_js() normally)
try_delete('out.txt')
jscommand = shared.make_js_command(os.path.normpath(exe), engine)
if WINDOWS:
os.system('type "in.txt" | {} >out.txt'.format(' '.join(Building.doublequote_spaces(jscommand))))
else: # posix
os.system('cat in.txt | {} > out.txt'.format(' '.join(Building.doublequote_spaces(jscommand))))
self.assertContained('abcdef\nghijkl\neof', open('out.txt').read())
Building.emcc(path_from_root('tests', 'module', 'test_stdin.c'), output_filename='a.out.js')
create_test_file('in.txt', 'abcdef\nghijkl')
exe = 'a.out.js'
_test()
Building.emcc(path_from_root('tests', 'module', 'test_stdin.c'),
['-O2', '--closure', '1'],
output_filename='a.out.js')
_test()
def test_ungetc_fscanf(self):
create_test_file('main.cpp', r'''
#include <stdio.h>
int main(int argc, char const *argv[])
{
char str[4] = {0};
FILE* f = fopen("my_test.input", "r");
if (f == NULL) {
printf("cannot open file\n");
return -1;
}
ungetc('x', f);
ungetc('y', f);
ungetc('z', f);
fscanf(f, "%3s", str);
printf("%s\n", str);
return 0;
}
''')
create_test_file('my_test.input', 'abc')
Building.emcc('main.cpp', ['--embed-file', 'my_test.input'], output_filename='a.out.js')
self.assertContained('zyx', run_process(JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
def test_abspaths(self):
# Includes with absolute paths are generally dangerous, things like -I/usr/.. will get to system local headers, not our portable ones.
shutil.copyfile(path_from_root('tests', 'hello_world.c'), 'main.c')
for args, expected in [(['-I/usr/something', '-Wwarn-absolute-paths'], True),
(['-L/usr/something', '-Wwarn-absolute-paths'], True),
(['-I/usr/something'], False),
(['-L/usr/something'], False),
(['-I/usr/something', '-Wno-warn-absolute-paths'], False),
(['-L/usr/something', '-Wno-warn-absolute-paths'], False),
(['-Isubdir/something', '-Wwarn-absolute-paths'], False),
(['-Lsubdir/something', '-Wwarn-absolute-paths'], False),
([], False)]:
print(args, expected)
proc = run_process([PYTHON, EMCC, 'main.c'] + args, stderr=PIPE)
assert ('encountered. If this is to a local system header/library, it may cause problems (local system files make sense for compiling natively on your system, but not necessarily to JavaScript)' in proc.stderr) == expected, proc.stderr
if not expected:
assert proc.stderr == '', proc.stderr
def test_local_link(self):
# Linking a local library directly, like /usr/lib/libsomething.so, cannot work of course since it
# doesn't contain bitcode. However, when we see that we should look for a bitcode file for that
# library in the -L paths and system/lib
create_test_file('main.cpp', '''
extern void printey();
int main() {
printey();
return 0;
}
''')
os.makedirs('subdir')
open(os.path.join('subdir', 'libfile.so'), 'w').write('this is not llvm bitcode!')
create_test_file('libfile.cpp', '''
#include <stdio.h>
void printey() {
printf("hello from lib\\n");
}
''')
run_process([PYTHON, EMCC, 'libfile.cpp', '-o', 'libfile.so'], stderr=PIPE)
run_process([PYTHON, EMCC, 'main.cpp', os.path.join('subdir', 'libfile.so'), '-L.'])
self.assertContained('hello from lib', run_js('a.out.js'))
def test_identical_basenames(self):
# Issue 287: files in different dirs but with the same basename get confused as the same,
# causing multiply defined symbol errors
os.mkdir('foo')
os.mkdir('bar')
open(os.path.join('foo', 'main.cpp'), 'w').write('''
extern void printey();
int main() {
printey();
return 0;
}
''')
open(os.path.join('bar', 'main.cpp'), 'w').write('''
#include <stdio.h>
void printey() { printf("hello there\\n"); }
''')
run_process([PYTHON, EMCC, os.path.join('foo', 'main.cpp'), os.path.join('bar', 'main.cpp')])
self.assertContained('hello there', run_js('a.out.js'))
# ditto with first creating .o files
try_delete('a.out.js')
run_process([PYTHON, EMCC, os.path.join('foo', 'main.cpp'), '-o', os.path.join('foo', 'main.o')])
run_process([PYTHON, EMCC, os.path.join('bar', 'main.cpp'), '-o', os.path.join('bar', 'main.o')])
run_process([PYTHON, EMCC, os.path.join('foo', 'main.o'), os.path.join('bar', 'main.o')])
self.assertContained('hello there', run_js('a.out.js'))
def test_main_a(self):
# if main() is in a .a, we need to pull in that .a
main_name = 'main.c'
create_test_file(main_name, r'''
#include <stdio.h>
extern int f();
int main() {
printf("result: %d.\n", f());
return 0;
}
''')
other_name = 'other.c'
create_test_file(other_name, r'''
#include <stdio.h>
int f() { return 12346; }
''')
run_process([PYTHON, EMCC, main_name, '-c', '-o', main_name + '.bc'])
run_process([PYTHON, EMCC, other_name, '-c', '-o', other_name + '.bc'])
run_process([PYTHON, EMAR, 'cr', main_name + '.a', main_name + '.bc'])
run_process([PYTHON, EMCC, other_name + '.bc', main_name + '.a'])
self.assertContained('result: 12346.', run_js('a.out.js'))
def test_multiple_archives_duplicate_basenames(self):
create_test_file('common.c', r'''
#include <stdio.h>
void a(void) {
printf("a\n");
}
''')
run_process([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o'])
try_delete('liba.a')
run_process([PYTHON, EMAR, 'rc', 'liba.a', 'common.o'])
create_test_file('common.c', r'''
#include <stdio.h>
void b(void) {
printf("b\n");
}
''')
run_process([PYTHON, EMCC, 'common.c', '-c', '-o', 'common.o'])
try_delete('libb.a')
run_process([PYTHON, EMAR, 'rc', 'libb.a', 'common.o'])
create_test_file('main.c', r'''
void a(void);
void b(void);
int main() {
a();
b();
}
''')
run_process([PYTHON, EMCC, 'main.c', '-L.', '-la', '-lb'])
self.assertContained('a\nb\n', run_js('a.out.js'))
def test_archive_duplicate_basenames(self):
os.mkdir('a')
create_test_file(os.path.join('a', 'common.c'), r'''
#include <stdio.h>
void a(void) {
printf("a\n");
}
''')
run_process([PYTHON, EMCC, os.path.join('a', 'common.c'), '-c', '-o', os.path.join('a', 'common.o')])
os.mkdir('b')
create_test_file(os.path.join('b', 'common.c'), r'''
#include <stdio.h>
void b(void) {
printf("b...\n");
}
''')
run_process([PYTHON, EMCC, os.path.join('b', 'common.c'), '-c', '-o', os.path.join('b', 'common.o')])
try_delete('liba.a')
run_process([PYTHON, EMAR, 'rc', 'liba.a', os.path.join('a', 'common.o'), os.path.join('b', 'common.o')])
# Verify that archive contains basenames with hashes to avoid duplication
text = run_process([PYTHON, EMAR, 't', 'liba.a'], stdout=PIPE).stdout
self.assertNotIn('common.o', text)
assert text.count('common_') == 2, text
for line in text.split('\n'):
assert len(line) < 20, line # should not have huge hash names
create_test_file('main.c', r'''
void a(void);
void b(void);
int main() {
a();
b();
}
''')
err = run_process([PYTHON, EMCC, 'main.c', '-L.', '-la'], stderr=PIPE).stderr
self.assertNotIn('archive file contains duplicate entries', err)
self.assertContained('a\nb...\n', run_js('a.out.js'))
# Using llvm-ar directly should cause duplicate basenames
try_delete('libdup.a')
run_process([LLVM_AR, 'rc', 'libdup.a', os.path.join('a', 'common.o'), os.path.join('b', 'common.o')])
text = run_process([PYTHON, EMAR, 't', 'libdup.a'], stdout=PIPE).stdout
assert text.count('common.o') == 2, text
# With fastcomp we don't support duplicate members so this should generate
# a warning. With the wasm backend (lld) this is fully supported.
proc = run_process([PYTHON, EMCC, 'main.c', '-L.', '-ldup'], check=False, stderr=PIPE)
if self.is_wasm_backend():
self.assertEqual(proc.returncode, 0)
self.assertEqual(proc.stderr, '')
self.assertContained('a\nb...\n', run_js('a.out.js'))
else:
self.assertNotEqual(proc.returncode, 0)
self.assertIn('libdup.a: archive file contains duplicate entries', proc.stderr)
self.assertIn('error: undefined symbol: a', proc.stderr)
# others are not duplicates - the hashing keeps them separate
self.assertEqual(proc.stderr.count('duplicate: '), 1)
self.assertContained('a\nb...\n', run_js('a.out.js'))
def test_export_from_archive(self):
export_name = 'this_is_an_entry_point'
full_export_name = '_' + export_name
# The wasm backend exports symbols without the leading '_'
if self.is_wasm_backend():
expect_export = export_name
else:
expect_export = full_export_name
create_test_file('export.c', r'''
#include <stdio.h>
void %s(void) {
printf("Hello, world!\n");
}
''' % export_name)
run_process([PYTHON, EMCC, 'export.c', '-c', '-o', 'export.o'])
run_process([PYTHON, EMAR, 'rc', 'libexport.a', 'export.o'])
create_test_file('main.c', r'''
int main() {
return 0;
}
''')
# Sanity check: the symbol should not be linked in if not requested.
run_process([PYTHON, EMCC, 'main.c', '-L.', '-lexport'])
self.assertFalse(self.is_exported_in_wasm(expect_export, 'a.out.wasm'))
# Sanity check: exporting without a definition does not cause it to appear.
# Note: exporting main prevents emcc from warning that it generated no code.
run_process([PYTHON, EMCC, 'main.c', '-s', 'ERROR_ON_UNDEFINED_SYMBOLS=0', '-s', "EXPORTED_FUNCTIONS=['_main', '%s']" % full_export_name])
self.assertFalse(self.is_exported_in_wasm(expect_export, 'a.out.wasm'))
# Actual test: defining symbol in library and exporting it causes it to appear in the output.
run_process([PYTHON, EMCC, 'main.c', '-L.', '-lexport', '-s', "EXPORTED_FUNCTIONS=['%s']" % full_export_name])
self.assertTrue(self.is_exported_in_wasm(expect_export, 'a.out.wasm'))
def test_embed_file(self):
create_test_file('somefile.txt', 'hello from a file with lots of data and stuff in it thank you very much')
create_test_file('main.cpp', r'''
#include <stdio.h>
int main() {
FILE *f = fopen("somefile.txt", "r");
char buf[100];
fread(buf, 1, 20, f);
buf[20] = 0;
fclose(f);
printf("|%s|\n", buf);
return 0;
}
''')
run_process([PYTHON, EMCC, 'main.cpp', '--embed-file', 'somefile.txt'])
self.assertContained('|hello from a file wi|', run_js('a.out.js'))
# preload twice, should not err
run_process([PYTHON, EMCC, 'main.cpp', '--embed-file', 'somefile.txt', '--embed-file', 'somefile.txt'])
self.assertContained('|hello from a file wi|', run_js('a.out.js'))
def test_embed_file_dup(self):
try_delete('tst')
os.mkdir('tst')
os.mkdir(self.in_dir('tst', 'test1'))
os.mkdir(self.in_dir('tst', 'test2'))
open(self.in_dir('tst', 'aa.txt'), 'w').write('''frist''')
open(self.in_dir('tst', 'test1', 'aa.txt'), 'w').write('''sacond''')
open(self.in_dir('tst', 'test2', 'aa.txt'), 'w').write('''thard''')
create_test_file('main.cpp', r'''
#include <stdio.h>
#include <string.h>
void print_file(const char *name) {
FILE *f = fopen(name, "r");
char buf[100];
memset(buf, 0, 100);
fread(buf, 1, 20, f);
buf[20] = 0;
fclose(f);
printf("|%s|\n", buf);
}
int main() {
print_file("tst/aa.txt");
print_file("tst/test1/aa.txt");
print_file("tst/test2/aa.txt");
return 0;
}
''')
run_process([PYTHON, EMCC, 'main.cpp', '--embed-file', 'tst'])
self.assertContained('|frist|\n|sacond|\n|thard|\n', run_js('a.out.js'))
def test_exclude_file(self):
try_delete('tst')
os.mkdir('tst')
os.mkdir(self.in_dir('tst', 'abc.exe'))
os.mkdir(self.in_dir('tst', 'abc.txt'))
open(self.in_dir('tst', 'hello.exe'), 'w').write('''hello''')
open(self.in_dir('tst', 'hello.txt'), 'w').write('''world''')
open(self.in_dir('tst', 'abc.exe', 'foo'), 'w').write('''emscripten''')
open(self.in_dir('tst', 'abc.txt', 'bar'), 'w').write('''!!!''')
create_test_file('main.cpp', r'''
#include <stdio.h>
int main() {
if(fopen("tst/hello.exe", "rb")) printf("Failed\n");
if(!fopen("tst/hello.txt", "rb")) printf("Failed\n");
if(fopen("tst/abc.exe/foo", "rb")) printf("Failed\n");
if(!fopen("tst/abc.txt/bar", "rb")) printf("Failed\n");
return 0;
}
''')
run_process([PYTHON, EMCC, 'main.cpp', '--embed-file', 'tst', '--exclude-file', '*.exe'])
output = run_js('a.out.js')
assert output == '' or output == ' \n'
def test_multidynamic_link(self):
# Linking the same dynamic library in statically will error, normally, since we statically link it, causing dupe symbols
def test(link_cmd, lib_suffix=''):
print(link_cmd, lib_suffix)
self.clear()
os.mkdir('libdir')
create_test_file('main.cpp', r'''
#include <stdio.h>
extern void printey();
extern void printother();
int main() {
printf("*");
printey();
printf("\n");
printother();
printf("\n");
printf("*");
return 0;
}
''')
open(os.path.join('libdir', 'libfile.cpp'), 'w').write('''
#include <stdio.h>
void printey() {
printf("hello from lib");
}
''')
open(os.path.join('libdir', 'libother.cpp'), 'w').write('''
#include <stdio.h>
extern void printey();
void printother() {
printf("|");
printey();
printf("|");
}
''')
compiler = [PYTHON, EMCC]
# Build libfile normally into an .so
run_process(compiler + [os.path.join('libdir', 'libfile.cpp'), '-o', os.path.join('libdir', 'libfile.so' + lib_suffix)])
# Build libother and dynamically link it to libfile
run_process(compiler + [os.path.join('libdir', 'libother.cpp')] + link_cmd + ['-o', os.path.join('libdir', 'libother.so')])
# Build the main file, linking in both the libs
run_process(compiler + [os.path.join('main.cpp')] + link_cmd + ['-lother', '-c'])
print('...')
# The normal build system is over. We need to do an additional step to link in the dynamic libraries, since we ignored them before
run_process([PYTHON, EMCC, 'main.o'] + link_cmd + ['-lother', '-s', 'EXIT_RUNTIME=1'])
self.assertContained('*hello from lib\n|hello from lib|\n*', run_js('a.out.js'))
test(['-L' + 'libdir', '-lfile']) # -l, auto detection from library path
test(['-L' + 'libdir', self.in_dir('libdir', 'libfile.so.3.1.4.1.5.9')], '.3.1.4.1.5.9') # handle libX.so.1.2.3 as well
def test_js_link(self):
create_test_file('main.cpp', '''
#include <stdio.h>
int main() {
printf("hello from main\\n");
return 0;
}
''')
create_test_file('before.js', '''
var MESSAGE = 'hello from js';
// Module is initialized with empty object by default, so if there are no keys - nothing was run yet
if (Object.keys(Module).length) throw 'This code should run before anything else!';
''')
create_test_file('after.js', '''
out(MESSAGE);
''')
run_process([PYTHON, EMCC, 'main.cpp', '--pre-js', 'before.js', '--post-js', 'after.js', '-s', 'BINARYEN_ASYNC_COMPILATION=0'])
self.assertContained('hello from main\nhello from js\n', run_js('a.out.js'))
def test_sdl_endianness(self):
create_test_file('main.cpp', r'''
#include <stdio.h>
#include <SDL/SDL.h>
int main() {
printf("%d, %d, %d\n", SDL_BYTEORDER, SDL_LIL_ENDIAN, SDL_BIG_ENDIAN);
return 0;
}
''')
run_process([PYTHON, EMCC, 'main.cpp'])
self.assertContained('1234, 1234, 4321\n', run_js('a.out.js'))
def test_libpng(self):
shutil.copyfile(path_from_root('tests', 'pngtest.png'), 'pngtest.png')
Building.emcc(path_from_root('tests', 'pngtest.c'), ['--embed-file', 'pngtest.png', '-s', 'USE_ZLIB=1', '-s', 'USE_LIBPNG=1'], output_filename='a.out.js')
self.assertContained('TESTS PASSED', run_process(JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
def test_bullet(self):
Building.emcc(path_from_root('tests', 'bullet_hello_world.cpp'), ['-s', 'USE_BULLET=1'], output_filename='a.out.js')
self.assertContained('BULLET RUNNING', run_process(JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
def test_vorbis(self):
# This will also test if ogg compiles, because vorbis depends on ogg
Building.emcc(path_from_root('tests', 'vorbis_test.c'), ['-s', 'USE_VORBIS=1'], output_filename='a.out.js')
self.assertContained('ALL OK', run_process(JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
def test_freetype(self):
# copy the Liberation Sans Bold truetype file located in the
# <emscripten_root>/tests/freetype to the compilation folder
shutil.copy2(path_from_root('tests/freetype', 'LiberationSansBold.ttf'), os.getcwd())
# build test program with the font file embed in it
Building.emcc(path_from_root('tests', 'freetype_test.c'), ['-s', 'USE_FREETYPE=1', '--embed-file', 'LiberationSansBold.ttf'], output_filename='a.out.js')
# the test program will print an ascii representation of a bitmap where the
# 'w' character has been rendered using the Liberation Sans Bold font
expectedOutput = '*** +***+ **\n' + \
'***+ +***+ +**\n' + \
'***+ ***** +**\n' + \
'+**+ +**+**+ +**\n' + \
'+*** +**+**+ ***\n' + \
' *** +** **+ ***\n' + \
' ***+**+ +**+**+\n' + \
' +**+**+ +**+**+\n' + \
' +***** +*****+\n' + \
' ***** ***** \n' + \
' ****+ +***+ \n' + \
' +***+ +***+ \n'
self.assertContained(expectedOutput, run_process(JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
def test_link_memcpy(self):
# memcpy can show up *after* optimizations, so after our opportunity to link in libc, so it must be special-cased
create_test_file('main.cpp', r'''
#include <stdio.h>
int main(int argc, char **argv) {
int num = argc + 10;
char buf[num], buf2[num];
for (int i = 0; i < num; i++) {
buf[i] = i*i+i/3;
}
for (int i = 1; i < num; i++) {
buf[i] += buf[i-1];
}
for (int i = 0; i < num; i++) {
buf2[i] = buf[i];
}
for (int i = 1; i < num; i++) {
buf2[i] += buf2[i-1];
}
for (int i = 0; i < num; i++) {
printf("%d:%d\n", i, buf2[i]);
}
return 0;
}
''')
run_process([PYTHON, EMCC, '-O2', 'main.cpp'])
output = run_js('a.out.js', full_output=True, stderr=PIPE)
self.assertContained('''0:0
1:1
2:6
3:21
4:53
5:111
6:-49
7:98
8:55
9:96
10:-16
''', output)
self.assertNotContained('warning: library.js memcpy should not be running, it is only for testing!', output)
def test_undefined_function(self):
cmd = [PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')]
run_process(cmd)
# adding a missing symbol to EXPORTED_FUNCTIONS should cause failure
cmd += ['-s', "EXPORTED_FUNCTIONS=['foobar']"]
proc = run_process(cmd, stderr=PIPE, check=False)
self.assertNotEqual(proc.returncode, 0)
self.assertContained('undefined exported function: "foobar"', proc.stderr)
# setting ERROR_ON_UNDEFINED_SYMBOLS=0 suppresses error
cmd += ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=0']
proc = run_process(cmd)
def test_undefined_symbols(self):
create_test_file('main.cpp', r'''
#include <stdio.h>
#include <SDL.h>
#include "SDL/SDL_opengl.h"
extern "C" {
void something();
void elsey();
}
int main() {
printf("%p", SDL_GL_GetProcAddress("glGenTextures")); // pull in gl proc stuff, avoid warnings on emulation funcs
something();
elsey();
return 0;
}
''')
for args in ([], ['-O2']):
for action in ('WARN', 'ERROR', None):
for value in ([0, 1]):
try_delete('a.out.js')
print('checking "%s" %s=%s' % (args, action, value))
extra = ['-s', action + '_ON_UNDEFINED_SYMBOLS=%d' % value] if action else []
proc = run_process([PYTHON, EMCC, 'main.cpp'] + extra + args, stderr=PIPE, check=False)
print(proc.stderr)
if value or action is None:
# The default is that we error in undefined symbols
self.assertContained('error: undefined symbol: something', proc.stderr)
self.assertContained('error: undefined symbol: elsey', proc.stderr)
check_success = False
elif action == 'ERROR' and not value:
# Error disables, should only warn
self.assertContained('warning: undefined symbol: something', proc.stderr)
self.assertContained('warning: undefined symbol: elsey', proc.stderr)
self.assertNotContained('undefined symbol: emscripten_', proc.stderr)
check_success = True
elif action == 'WARN' and not value:
# Disabled warning should imply disabling errors
self.assertNotContained('undefined symbol', proc.stderr)
check_success = True
if check_success:
self.assertEqual(proc.returncode, 0)
self.assertTrue(os.path.exists('a.out.js'))
else:
self.assertNotEqual(proc.returncode, 0)
self.assertFalse(os.path.exists('a.out.js'))
def test_GetProcAddress_LEGACY_GL_EMULATION(self):
# without legacy gl emulation, getting a proc from there should fail
self.do_other_test(os.path.join('other', 'GetProcAddress_LEGACY_GL_EMULATION'), run_args=['0'], emcc_args=['-s', 'LEGACY_GL_EMULATION=0'])
# with it, it should work
self.do_other_test(os.path.join('other', 'GetProcAddress_LEGACY_GL_EMULATION'), run_args=['1'], emcc_args=['-s', 'LEGACY_GL_EMULATION=1'])
@no_wasm_backend('linker detects out-of-memory')
def test_toobig(self):
# very large [N x i8], we should not oom in the compiler
create_test_file('main.cpp', r'''
#include <stdio.h>
#define BYTES (50 * 1024 * 1024)
int main(int argc, char **argv) {
if (argc == 100) {
static char buf[BYTES];
static char buf2[BYTES];
for (int i = 0; i < BYTES; i++) {
buf[i] = i*i;
buf2[i] = i/3;
}
for (int i = 0; i < BYTES; i++) {
buf[i] = buf2[i/2];
buf2[i] = buf[i/3];
}
printf("%d\n", buf[10] + buf2[20]);
}
return 0;
}
''')
run_process([PYTHON, EMCC, 'main.cpp'])
assert os.path.exists('a.out.js')
def test_prepost(self):
create_test_file('main.cpp', '''
#include <stdio.h>
int main() {
printf("hello from main\\n");
return 0;
}
''')
create_test_file('pre.js', '''
var Module = {
preRun: function() { out('pre-run') },
postRun: function() { out('post-run') }
};
''')
run_process([PYTHON, EMCC, 'main.cpp', '--pre-js', 'pre.js', '-s', 'BINARYEN_ASYNC_COMPILATION=0'])
self.assertContained('pre-run\nhello from main\npost-run\n', run_js('a.out.js'))
# addRunDependency during preRun should prevent main, and post-run from
# running.
with open('pre.js', 'a') as f:
f.write('Module.preRun = function() { out("add-dep"); addRunDependency(); }\n')
run_process([PYTHON, EMCC, 'main.cpp', '--pre-js', 'pre.js', '-s', 'BINARYEN_ASYNC_COMPILATION=0'])
output = run_js('a.out.js')
self.assertContained('add-dep\n', output)
self.assertNotContained('hello from main\n', output)
self.assertNotContained('post-run\n', output)
# noInitialRun prevents run
for no_initial_run, run_dep in [(0, 0), (1, 0), (0, 1)]:
print(no_initial_run, run_dep)
args = ['-s', 'BINARYEN_ASYNC_COMPILATION=0']
if no_initial_run:
args += ['-s', 'INVOKE_RUN=0']
if run_dep:
create_test_file('pre.js', 'Module.preRun = function() { addRunDependency("test"); }')
create_test_file('post.js', 'removeRunDependency("test");')
args += ['--pre-js', 'pre.js', '--post-js', 'post.js']
run_process([PYTHON, EMCC, 'main.cpp'] + args)
output = run_js('a.out.js')
if no_initial_run:
self.assertNotContained('hello from main', output)
else:
self.assertContained('hello from main', output)
if no_initial_run:
# Calling main later should still work, filesystem etc. must be set up.
print('call main later')
src = open('a.out.js').read()
src += '\nModule.callMain();\n'
create_test_file('a.out.js', src)
self.assertContained('hello from main', run_js('a.out.js'))
# Use postInit
create_test_file('pre.js', '''
var Module = {
preRun: function() { out('pre-run') },
postRun: function() { out('post-run') },
preInit: function() { out('pre-init') }
};
''')
run_process([PYTHON, EMCC, 'main.cpp', '--pre-js', 'pre.js'])
self.assertContained('pre-init\npre-run\nhello from main\npost-run\n', run_js('a.out.js'))
def test_prepost2(self):
create_test_file('main.cpp', '''
#include <stdio.h>
int main() {
printf("hello from main\\n");
return 0;
}
''')
create_test_file('pre.js', '''
var Module = {
preRun: function() { out('pre-run') },
};
''')
create_test_file('pre2.js', '''
Module.postRun = function() { out('post-run') };
''')
run_process([PYTHON, EMCC, 'main.cpp', '--pre-js', 'pre.js', '--pre-js', 'pre2.js'])
self.assertContained('pre-run\nhello from main\npost-run\n', run_js('a.out.js'))
def test_prepre(self):
create_test_file('main.cpp', '''
#include <stdio.h>
int main() {
printf("hello from main\\n");
return 0;
}
''')
create_test_file('pre.js', '''
var Module = {
preRun: [function() { out('pre-run') }],
};
''')
create_test_file('pre2.js', '''
Module.preRun.push(function() { out('prepre') });
''')
run_process([PYTHON, EMCC, 'main.cpp', '--pre-js', 'pre.js', '--pre-js', 'pre2.js'])
self.assertContained('prepre\npre-run\nhello from main\n', run_js('a.out.js'))
@no_wasm_backend('depends on bc output')
def test_save_bc(self):
for save in [0, 1]:
self.clear()
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world_loop_malloc.cpp')] + ([] if not save else ['--save-bc', 'my_bitcode.bc']))
assert 'hello, world!' in run_js('a.out.js')
assert os.path.exists('my_bitcode.bc') == save
if save:
try_delete('a.out.js')
Building.llvm_dis('my_bitcode.bc', 'my_ll.ll')
with env_modify({'EMCC_LEAVE_INPUTS_RAW': '1'}):
run_process([PYTHON, EMCC, 'my_ll.ll', '-o', 'two.js'])
assert 'hello, world!' in run_js('two.js')
def test_js_optimizer(self):
ACORN_PASSES = ['JSDCE', 'AJSDCE', 'applyImportAndExportNameChanges', 'emitDCEGraph', 'applyDCEGraphRemovals']
for input, expected, passes in [
(path_from_root('tests', 'optimizer', 'eliminateDeadGlobals.js'), open(path_from_root('tests', 'optimizer', 'eliminateDeadGlobals-output.js')).read(),
['eliminateDeadGlobals']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-output.js')).read(),
['hoistMultiples', 'removeAssignsToUndefined', 'simplifyExpressions']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-output.js')).read(),
['asm', 'simplifyExpressions']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-si.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-si-output.js')).read(),
['simplifyIfs']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-regs.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-regs-output.js')).read(),
['registerize']),
(path_from_root('tests', 'optimizer', 'eliminator-test.js'), open(path_from_root('tests', 'optimizer', 'eliminator-test-output.js')).read(),
['eliminate']),
(path_from_root('tests', 'optimizer', 'safe-eliminator-test.js'), open(path_from_root('tests', 'optimizer', 'safe-eliminator-test-output.js')).read(),
['eliminateMemSafe']),
(path_from_root('tests', 'optimizer', 'asm-eliminator-test.js'), open(path_from_root('tests', 'optimizer', 'asm-eliminator-test-output.js')).read(),
['asm', 'eliminate']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-output.js')).read(),
['asm', 'registerize']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-harder.js'), [open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-harder-output.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-harder-output2.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-harder-output3.js')).read()],
['asm', 'registerizeHarder']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-min.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-regs-min-output.js')).read(),
['asm', 'registerize', 'minifyLocals']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre.js'), [open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-output.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-output2.js')).read()],
['asm', 'simplifyExpressions']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-f32.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-output-f32.js')).read(),
['asm', 'asmPreciseF32', 'simplifyExpressions', 'optimizeFrounds']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-f32.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-pre-output-f32-nosimp.js')).read(),
['asm', 'asmPreciseF32', 'optimizeFrounds']),
(path_from_root('tests', 'optimizer', 'test-reduce-dead-float-return.js'), open(path_from_root('tests', 'optimizer', 'test-reduce-dead-float-return-output.js')).read(),
['asm', 'optimizeFrounds', 'registerizeHarder']),
(path_from_root('tests', 'optimizer', 'test-no-reduce-dead-float-return-to-nothing.js'), open(path_from_root('tests', 'optimizer', 'test-no-reduce-dead-float-return-to-nothing-output.js')).read(),
['asm', 'registerizeHarder']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-last.js'), [open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-lastOpts-output.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-lastOpts-output2.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-lastOpts-output3.js')).read()],
['asm', 'asmLastOpts']),
(path_from_root('tests', 'optimizer', 'asmLastOpts.js'), open(path_from_root('tests', 'optimizer', 'asmLastOpts-output.js')).read(),
['asm', 'asmLastOpts']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-last.js'), [open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-last-output.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-last-output2.js')).read(), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-last-output3.js')).read()],
['asm', 'asmLastOpts', 'last']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-relocate.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-relocate-output.js')).read(),
['asm', 'relocate']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline1.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline1-output.js')).read(),
['asm', 'outline']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline2.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline2-output.js')).read(),
['asm', 'outline']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline3.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline3-output.js')).read(),
['asm', 'outline']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline4.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-outline4-output.js')).read(),
['asm', 'outline']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-minlast.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-asm-minlast-output.js')).read(),
['asm', 'minifyWhitespace', 'asmLastOpts', 'last']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-shiftsAggressive.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-shiftsAggressive-output.js')).read(),
['asm', 'aggressiveVariableElimination']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-localCSE.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-localCSE-output.js')).read(),
['asm', 'localCSE']),
(path_from_root('tests', 'optimizer', 'test-js-optimizer-ensureLabelSet.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-ensureLabelSet-output.js')).read(),
['asm', 'ensureLabelSet']),
(path_from_root('tests', 'optimizer', '3154.js'), open(path_from_root('tests', 'optimizer', '3154-output.js')).read(),
['asm', 'eliminate', 'registerize', 'asmLastOpts', 'last']),
(path_from_root('tests', 'optimizer', 'simd.js'), open(path_from_root('tests', 'optimizer', 'simd-output.js')).read(),
['asm', 'eliminate']), # eliminate, just enough to trigger asm normalization/denormalization
(path_from_root('tests', 'optimizer', 'simd.js'), open(path_from_root('tests', 'optimizer', 'simd-output-memSafe.js')).read(),
['asm', 'eliminateMemSafe']),
(path_from_root('tests', 'optimizer', 'safeLabelSetting.js'), open(path_from_root('tests', 'optimizer', 'safeLabelSetting-output.js')).read(),
['asm', 'safeLabelSetting']), # eliminate, just enough to trigger asm normalization/denormalization
(path_from_root('tests', 'optimizer', 'null_if.js'), [open(path_from_root('tests', 'optimizer', 'null_if-output.js')).read(), open(path_from_root('tests', 'optimizer', 'null_if-output2.js')).read()],
['asm', 'registerizeHarder', 'asmLastOpts', 'minifyWhitespace']), # issue 3520
(path_from_root('tests', 'optimizer', 'null_else.js'), [open(path_from_root('tests', 'optimizer', 'null_else-output.js')).read(), open(path_from_root('tests', 'optimizer', 'null_else-output2.js')).read()],
['asm', 'registerizeHarder', 'asmLastOpts', 'minifyWhitespace']), # issue 3549
(path_from_root('tests', 'optimizer', 'test-js-optimizer-splitMemory.js'), open(path_from_root('tests', 'optimizer', 'test-js-optimizer-splitMemory-output.js')).read(),
['splitMemory']),
(path_from_root('tests', 'optimizer', 'JSDCE.js'), open(path_from_root('tests', 'optimizer', 'JSDCE-output.js')).read(),
['JSDCE']),
(path_from_root('tests', 'optimizer', 'JSDCE-hasOwnProperty.js'), open(path_from_root('tests', 'optimizer', 'JSDCE-hasOwnProperty-output.js')).read(),
['JSDCE']),
(path_from_root('tests', 'optimizer', 'JSDCE-fors.js'), open(path_from_root('tests', 'optimizer', 'JSDCE-fors-output.js')).read(),
['JSDCE']),
(path_from_root('tests', 'optimizer', 'AJSDCE.js'), open(path_from_root('tests', 'optimizer', 'AJSDCE-output.js')).read(),
['AJSDCE']),
(path_from_root('tests', 'optimizer', 'emitDCEGraph.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph-output.js')).read(),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'emitDCEGraph2.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph2-output.js')).read(),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'emitDCEGraph3.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph3-output.js')).read(),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'emitDCEGraph4.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph4-output.js')).read(),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'emitDCEGraph5.js'), open(path_from_root('tests', 'optimizer', 'emitDCEGraph5-output.js')).read(),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'applyDCEGraphRemovals.js'), open(path_from_root('tests', 'optimizer', 'applyDCEGraphRemovals-output.js')).read(),
['applyDCEGraphRemovals']),
(path_from_root('tests', 'optimizer', 'applyImportAndExportNameChanges.js'), open(path_from_root('tests', 'optimizer', 'applyImportAndExportNameChanges-output.js')).read(),
['applyImportAndExportNameChanges']),
(path_from_root('tests', 'optimizer', 'applyImportAndExportNameChanges2.js'), open(path_from_root('tests', 'optimizer', 'applyImportAndExportNameChanges2-output.js')).read(),
['applyImportAndExportNameChanges']),
(path_from_root('tests', 'optimizer', 'detectSign-modulus-emterpretify.js'), open(path_from_root('tests', 'optimizer', 'detectSign-modulus-emterpretify-output.js')).read(),
['noPrintMetadata', 'emterpretify', 'noEmitAst']),
(path_from_root('tests', 'optimizer', 'minimal-runtime-emitDCEGraph.js'), open(path_from_root('tests', 'optimizer', 'minimal-runtime-emitDCEGraph-output.js')).read(),
['emitDCEGraph', 'noPrint']),
(path_from_root('tests', 'optimizer', 'emittedJSPreservesParens.js'), open(path_from_root('tests', 'optimizer', 'emittedJSPreservesParens-output.js')).read(),
['asm']),
]:
print(input, passes)
if not isinstance(expected, list):
expected = [expected]
expected = [out.replace('\n\n', '\n').replace('\n\n', '\n') for out in expected]
acorn = any([p for p in passes if p in ACORN_PASSES])
# test calling optimizer
if not acorn:
print(' js')
output = run_process(NODE_JS + [path_from_root('tools', 'js-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).stdout
else:
print(' acorn')
output = run_process(NODE_JS + [path_from_root('tools', 'acorn-optimizer.js'), input] + passes, stdin=PIPE, stdout=PIPE).stdout
def check_js(js, expected):
# print >> sys.stderr, 'chak\n==========================\n', js, '\n===========================\n'
if 'registerizeHarder' in passes:
# registerizeHarder is hard to test, as names vary by chance, nondeterminstically FIXME
def fix(src):
if type(src) is list:
return list(map(fix, src))
src = '\n'.join([line for line in src.split('\n') if 'var ' not in line]) # ignore vars
def reorder(func):
def swap(func, stuff):
# emit EYE_ONE always before EYE_TWO, replacing i1,i2 or i2,i1 etc
for i in stuff:
if i not in func:
return func
indexes = [[i, func.index(i)] for i in stuff]
indexes.sort(key=lambda x: x[1])
for j in range(len(indexes)):
func = func.replace(indexes[j][0], 'STD_' + str(j))
return func
func = swap(func, ['i1', 'i2', 'i3'])
func = swap(func, ['i1', 'i2'])
func = swap(func, ['i4', 'i5'])
return func
src = 'function '.join(map(reorder, src.split('function ')))
return src
js = fix(js)
expected = fix(expected)
self.assertIdentical(expected, js.replace('\r\n', '\n').replace('\n\n', '\n').replace('\n\n', '\n'))
if input not in [ # blacklist of tests that are native-optimizer only
path_from_root('tests', 'optimizer', 'asmLastOpts.js'),
path_from_root('tests', 'optimizer', '3154.js')
]:
check_js(output, expected)
else:
print('(skip non-native)')
if not self.is_wasm_backend() and tools.js_optimizer.use_native(passes) and tools.js_optimizer.get_native_optimizer():
# test calling native
def check_json():
run_process(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), output_temp, 'receiveJSON'], stdin=PIPE, stdout=open(output_temp + '.js', 'w'))
output = open(output_temp + '.js').read()
check_js(output, expected)
self.clear()
input_temp = 'temp.js'
output_temp = 'output.js'
shutil.copyfile(input, input_temp)
run_process(listify(NODE_JS) + [path_from_root('tools', 'js-optimizer.js'), input_temp, 'emitJSON'], stdin=PIPE, stdout=open(input_temp + '.js', 'w'))
original = open(input).read()
if '// EXTRA_INFO:' in original:
json = open(input_temp + '.js').read()
json += '\n' + original[original.find('// EXTRA_INFO:'):]
create_test_file(input_temp + '.js', json)
# last is only relevant when we emit JS
if 'last' not in passes and \
'null_if' not in input and 'null_else' not in input: # null-* tests are js optimizer or native, not a mixture (they mix badly)
print(' native (receiveJSON)')
output = run_process([tools.js_optimizer.get_native_optimizer(), input_temp + '.js'] + passes + ['receiveJSON', 'emitJSON'], stdin=PIPE, stdout=open(output_temp, 'w')).stdout
check_json()
print(' native (parsing JS)')
output = run_process([tools.js_optimizer.get_native_optimizer(), input] + passes + ['emitJSON'], stdin=PIPE, stdout=open(output_temp, 'w')).stdout
check_json()
print(' native (emitting JS)')
output = run_process([tools.js_optimizer.get_native_optimizer(), input] + passes, stdin=PIPE, stdout=PIPE).stdout
check_js(output, expected)
def test_m_mm(self):
create_test_file('foo.c', '''#include <emscripten.h>''')
for opt in ['M', 'MM']:
proc = run_process([PYTHON, EMCC, 'foo.c', '-' + opt], stdout=PIPE, stderr=PIPE)
assert 'foo.o: ' in proc.stdout, '-%s failed to produce the right output: %s' % (opt, proc.stdout)
assert 'error' not in proc.stderr, 'Unexpected stderr: ' + proc.stderr
@uses_canonical_tmp
def test_emcc_debug_files(self):
for opts in [0, 1, 2, 3]:
for debug in [None, '1', '2']:
print(opts, debug)
if os.path.exists(self.canonical_temp_dir):
shutil.rmtree(self.canonical_temp_dir)
env = os.environ.copy()
if debug is None:
env.pop('EMCC_DEBUG', None)
else:
env['EMCC_DEBUG'] = debug
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '-O' + str(opts)], stderr=PIPE, env=env)
if debug is None:
self.assertFalse(os.path.exists(self.canonical_temp_dir))
elif debug == '1':
if self.is_wasm_backend():
assert os.path.exists(os.path.join(self.canonical_temp_dir, 'emcc-0-original.js'))
else:
assert os.path.exists(os.path.join(self.canonical_temp_dir, 'emcc-0-linktime.bc'))
assert os.path.exists(os.path.join(self.canonical_temp_dir, 'emcc-1-original.js'))
elif debug == '2':
if self.is_wasm_backend():
assert os.path.exists(os.path.join(self.canonical_temp_dir, 'emcc-0-original.js'))
else:
assert os.path.exists(os.path.join(self.canonical_temp_dir, 'emcc-0-basebc.bc'))
assert os.path.exists(os.path.join(self.canonical_temp_dir, 'emcc-1-linktime.bc'))
assert os.path.exists(os.path.join(self.canonical_temp_dir, 'emcc-2-original.js'))
@uses_canonical_tmp
def test_debuginfo(self):
with env_modify({'EMCC_DEBUG': '1'}):
for args, expect_debug in [
(['-O0'], False),
(['-O0', '-g'], True),
(['-O0', '-g4'], True),
(['-O1'], False),
(['-O1', '-g'], True),
(['-O2'], False),
(['-O2', '-g'], True),
]:
print(args, expect_debug)
err = run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')] + args, stdout=PIPE, stderr=PIPE).stderr
lines = err.splitlines()
if self.is_wasm_backend():
finalize = [l for l in lines if 'wasm-emscripten-finalize' in l][0]
if expect_debug:
self.assertIn(' -g ', finalize)
else:
self.assertNotIn(' -g ', finalize)
else:
if expect_debug:
self.assertNotIn('strip-debug', err)
else:
self.assertIn('strip-debug', err)
@unittest.skipIf(not scons_path, 'scons not found in PATH')
@with_env_modify({'EMSCRIPTEN_ROOT': path_from_root()})
def test_scons(self):
# this test copies the site_scons directory alongside the test
shutil.copytree(path_from_root('tests', 'scons'), 'test')
shutil.copytree(path_from_root('tools', 'scons', 'site_scons'), os.path.join('test', 'site_scons'))
with chdir('test'):
run_process(['scons'])
output = run_js('scons_integration.js', assert_returncode=5)
self.assertContained('If you see this - the world is all right!', output)
@unittest.skipIf(not scons_path, 'scons not found in PATH')
@with_env_modify({'EMSCRIPTEN_TOOLPATH': path_from_root('tools', 'scons', 'site_scons'),
'EMSCRIPTEN_ROOT': path_from_root()})
def test_emscons(self):
# uses the emscons wrapper which requires EMSCRIPTEN_TOOLPATH to find
# site_scons
shutil.copytree(path_from_root('tests', 'scons'), 'test')
with chdir('test'):
run_process([path_from_root('emscons'), 'scons'])
output = run_js('scons_integration.js', assert_returncode=5)
self.assertContained('If you see this - the world is all right!', output)
def test_embind(self):
environ = os.environ.copy()
environ['EMCC_CLOSURE_ARGS'] = environ.get('EMCC_CLOSURE_ARGS', '') + " --externs " + pipes.quote(path_from_root('tests', 'embind', 'underscore-externs.js'))
test_cases = [
([], True), # without --bind, we fail
(['--bind'], False),
(['--bind', '-O1'], False),
(['--bind', '-O2'], False),
(['--bind', '-O2', '-s', 'ALLOW_MEMORY_GROWTH=1', path_from_root('tests', 'embind', 'isMemoryGrowthEnabled=true.cpp')], False),
]
without_utf8_args = ['-s', 'EMBIND_STD_STRING_IS_UTF8=0']
test_cases_without_utf8 = []
for args, fail in test_cases:
test_cases_without_utf8.append((args + without_utf8_args, fail))
test_cases += test_cases_without_utf8
test_cases.extend([(args[:] + ['-s', 'DYNAMIC_EXECUTION=0'], status) for args, status in test_cases])
test_cases.append((['--bind', '-O2', '--closure', '1'], False)) # closure compiler doesn't work with DYNAMIC_EXECUTION=0
test_cases = [(args + ['-s', 'IN_TEST_HARNESS=1'], status) for args, status in test_cases]
for args, fail in test_cases:
print(args, fail)
self.clear()
try_delete('a.out.js')
testFiles = [
path_from_root('tests', 'embind', 'underscore-1.4.2.js'),
path_from_root('tests', 'embind', 'imvu_test_adapter.js'),
path_from_root('tests', 'embind', 'embind.test.js'),
]
proc = run_process(
[PYTHON, EMCC, path_from_root('tests', 'embind', 'embind_test.cpp'),
'--pre-js', path_from_root('tests', 'embind', 'test.pre.js'),
'--post-js', path_from_root('tests', 'embind', 'test.post.js'),
'-s', 'BINARYEN_ASYNC_COMPILATION=0'] + args,
stderr=PIPE if fail else None,
check=not fail,
env=environ)
if fail:
self.assertNotEqual(proc.returncode, 0)
else:
if 'DYNAMIC_EXECUTION=0' in args:
with open('a.out.js') as js_binary_file:
js_binary_str = js_binary_file.read()
self.assertNotIn('new Function(', js_binary_str, 'Found "new Function(" with DYNAMIC_EXECUTION=0')
self.assertNotIn('eval(', js_binary_str, 'Found "eval(" with DYNAMIC_EXECUTION=0')
with open('a.out.js', 'ab') as f:
for tf in testFiles:
f.write(open(tf, 'rb').read())
output = run_js('a.out.js', stdout=PIPE, stderr=PIPE, full_output=True, assert_returncode=0, engine=NODE_JS)
assert "FAIL" not in output, output
@no_wasm_backend('cannot nativize a wasm object file (...yet?)')
@no_windows('test_llvm_nativizer does not work on Windows')
def test_llvm_nativizer(self):
if MACOS:
self.skipTest('test_llvm_nativizer does not work on macOS')
if Building.which('as') is None:
self.skipTest('no gnu as, cannot run nativizer')
# avoid impure_ptr problems etc.
create_test_file('somefile.binary', 'waka waka############################')
create_test_file('test.file', 'ay file..............,,,,,,,,,,,,,,')
create_test_file('stdin', 'inter-active')
run_process([PYTHON, EMCC, path_from_root('tests', 'files.cpp'), '-c'])
run_process([PYTHON, path_from_root('tools', 'nativize_llvm.py'), 'files.o'])
proc = run_process([os.path.abspath('files.o.run')], stdin=open('stdin'), stdout=PIPE, stderr=PIPE)
self.assertContained('''\
size: 37
data: 119,97,107,97,32,119,97,107,97,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35
loop: 119 97 107 97 32 119 97 107 97 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 35 ''' + '''
input:inter-active
texto
$
5 : 10,30,20,11,88
other=ay file...
seeked= file.
''', proc.stdout)
self.assertContained('texte\n', proc.stderr)
def test_emconfig(self):
output = run_process([PYTHON, EMCONFIG, 'LLVM_ROOT'], stdout=PIPE, stderr=PIPE).stdout.strip()
self.assertEqual(output, LLVM_ROOT)
invalid = 'Usage: em-config VAR_NAME'
# Don't accept variables that do not exist
output = run_process([PYTHON, EMCONFIG, 'VAR_WHICH_DOES_NOT_EXIST'], stdout=PIPE, stderr=PIPE, check=False).stdout.strip()
self.assertEqual(output, invalid)
# Don't accept no arguments
output = run_process([PYTHON, EMCONFIG], stdout=PIPE, stderr=PIPE, check=False).stdout.strip()
self.assertEqual(output, invalid)
# Don't accept more than one variable
output = run_process([PYTHON, EMCONFIG, 'LLVM_ROOT', 'EMCC'], stdout=PIPE, stderr=PIPE, check=False).stdout.strip()
self.assertEqual(output, invalid)
# Don't accept arbitrary python code
output = run_process([PYTHON, EMCONFIG, 'sys.argv[1]'], stdout=PIPE, stderr=PIPE, check=False).stdout.strip()
self.assertEqual(output, invalid)
def test_link_s(self):
# -s OPT=VALUE can conflict with -s as a linker option. We warn and ignore
create_test_file('main.cpp', r'''
extern "C" {
void something();
}
int main() {
something();
return 0;
}
''')
create_test_file('supp.cpp', r'''
#include <stdio.h>
extern "C" {
void something() {
printf("yello\n");
}
}
''')
run_process([PYTHON, EMCC, 'main.cpp', '-o', 'main.o'])
run_process([PYTHON, EMCC, 'supp.cpp', '-o', 'supp.o'])
run_process([PYTHON, EMCC, 'main.o', '-s', 'supp.o', '-s', 'SAFE_HEAP=1'])
self.assertContained('yello', run_js('a.out.js'))
code = open('a.out.js').read()
assert 'SAFE_HEAP' in code, 'valid -s option had an effect'
def test_conftest_s_flag_passing(self):
create_test_file('conftest.c', r'''
int main() {
return 0;
}
''')
with env_modify({'EMMAKEN_JUST_CONFIGURE': '1'}):
cmd = [PYTHON, EMCC, '-s', 'ASSERTIONS=1', 'conftest.c', '-o', 'conftest']
output = run_process(cmd, stderr=PIPE)
self.assertNotContained('emcc: warning: treating -s as linker option', output.stderr)
assert os.path.exists('conftest')
def test_file_packager(self):
os.mkdir('subdir')
create_test_file('data1.txt', 'data1')
os.chdir('subdir')
create_test_file('data2.txt', 'data2')
# relative path to below the current dir is invalid
proc = run_process([PYTHON, FILE_PACKAGER, 'test.data', '--preload', '../data1.txt'], stderr=PIPE, stdout=PIPE, check=False)
self.assertNotEqual(proc.returncode, 0)
self.assertEqual(len(proc.stdout), 0)
self.assertContained('below the current directory', proc.stderr)
# relative path that ends up under us is cool
proc = run_process([PYTHON, FILE_PACKAGER, 'test.data', '--preload', '../subdir/data2.txt'], stderr=PIPE, stdout=PIPE)
self.assertGreater(len(proc.stdout), 0)
self.assertNotContained('below the current directory', proc.stderr)
# direct path leads to the same code being generated - relative path does not make us do anything different
proc2 = run_process([PYTHON, FILE_PACKAGER, 'test.data', '--preload', 'data2.txt'], stderr=PIPE, stdout=PIPE)
self.assertGreater(len(proc2.stdout), 0)
self.assertNotContained('below the current directory', proc2.stderr)
def clean(txt):
return [line for line in txt.split('\n') if 'PACKAGE_UUID' not in line and 'loadPackage({' not in line]
assert clean(proc.stdout) == clean(proc2.stdout)
# verify '--separate-metadata' option produces separate metadata file
os.chdir('..')
run_process([PYTHON, FILE_PACKAGER, 'test.data', '--preload', 'data1.txt', '--preload', 'subdir/data2.txt', '--js-output=immutable.js', '--separate-metadata'])
assert os.path.isfile('immutable.js.metadata')
# verify js output file is immutable when metadata is separated
shutil.copy2('immutable.js', 'immutable.js.copy') # copy with timestamp preserved
run_process([PYTHON, FILE_PACKAGER, 'test.data', '--preload', 'data1.txt', '--preload', 'subdir/data2.txt', '--js-output=immutable.js', '--separate-metadata'])
assert filecmp.cmp('immutable.js.copy', 'immutable.js')
# assert both file content and timestamp are the same as reference copy
self.assertEqual(str(os.path.getmtime('immutable.js.copy')), str(os.path.getmtime('immutable.js')))
# verify the content of metadata file is correct
with open('immutable.js.metadata') as f:
metadata = json.load(f)
self.assertEqual(len(metadata['files']), 2)
assert metadata['files'][0]['start'] == 0 and metadata['files'][0]['end'] == len('data1') and metadata['files'][0]['filename'] == '/data1.txt'
assert metadata['files'][1]['start'] == len('data1') and metadata['files'][1]['end'] == len('data1') + len('data2') and metadata['files'][1]['filename'] == '/subdir/data2.txt'
assert metadata['remote_package_size'] == len('data1') + len('data2')
# can only assert the uuid format is correct, the uuid's value is expected to differ in between invocation
uuid.UUID(metadata['package_uuid'], version=4)
def test_file_packager_unicode(self):
unicode_name = 'unicode…☃'
if not os.path.exists(unicode_name):
try:
os.mkdir(unicode_name)
except:
print("we failed to even create a unicode dir, so on this OS, we can't test this")
return
full = os.path.join(unicode_name, 'data.txt')
create_test_file(full, 'data')
proc = run_process([PYTHON, FILE_PACKAGER, 'test.data', '--preload', full], stdout=PIPE, stderr=PIPE)
assert len(proc.stdout), proc.stderr
assert unicode_name in proc.stdout, proc.stdout
print(len(proc.stderr))
def test_file_packager_mention_FORCE_FILESYSTEM(self):
MESSAGE = 'Remember to build the main file with -s FORCE_FILESYSTEM=1 so that it includes support for loading this file package'
create_test_file('data.txt', 'data1')
# mention when running standalone
err = run_process([PYTHON, FILE_PACKAGER, 'test.data', '--preload', 'data.txt'], stdout=PIPE, stderr=PIPE).stderr
self.assertContained(MESSAGE, err)
# do not mention from emcc
err = run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '--preload-file', 'data.txt'], stdout=PIPE, stderr=PIPE).stderr
assert len(err) == 0, err
def test_headless(self):
shutil.copyfile(path_from_root('tests', 'screenshot.png'), 'example.png')
run_process([PYTHON, EMCC, path_from_root('tests', 'sdl_headless.c'), '-s', 'HEADLESS=1'])
output = run_js('a.out.js', stderr=PIPE)
assert '''Init: 0
Font: 0x1
Sum: 0
you should see two lines of text in different colors and a blue rectangle
SDL_Quit called (and ignored)
done.
''' in output, output
def test_preprocess(self):
self.clear()
out = run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-E'], stdout=PIPE).stdout
assert not os.path.exists('a.out.js')
# Test explicitly that the output contains a line typically written by the preprocessor.
# Clang outputs on Windows lines like "#line 1", on Unix '# 1 '.
# TODO: This is one more of those platform-specific discrepancies, investigate more if this ever becomes an issue,
# ideally we would have emcc output identical data on all platforms.
assert '''#line 1 ''' in out or '''# 1 ''' in out
assert '''hello_world.c"''' in out
assert '''printf("hello, world!''' in out
def test_demangle(self):
create_test_file('src.cpp', '''
#include <stdio.h>
#include <emscripten.h>
void two(char c) {
EM_ASM(out(stackTrace()));
}
void one(int x) {
two(x % 17);
}
int main() {
EM_ASM(out(demangle('__Znwm'))); // check for no aborts
EM_ASM(out(demangle('_main')));
EM_ASM(out(demangle('__Z2f2v')));
EM_ASM(out(demangle('__Z12abcdabcdabcdi')));
EM_ASM(out(demangle('__ZL12abcdabcdabcdi')));
EM_ASM(out(demangle('__Z4testcsifdPvPiPc')));
EM_ASM(out(demangle('__ZN4test5moarrEcslfdPvPiPc')));
EM_ASM(out(demangle('__ZN4Waka1f12a234123412345pointEv')));
EM_ASM(out(demangle('__Z3FooIiEvv')));
EM_ASM(out(demangle('__Z3FooIidEvi')));
EM_ASM(out(demangle('__ZN3Foo3BarILi5EEEvv')));
EM_ASM(out(demangle('__ZNK10__cxxabiv120__si_class_type_info16search_below_dstEPNS_19__dynamic_cast_infoEPKvib')));
EM_ASM(out(demangle('__Z9parsewordRPKciRi')));
EM_ASM(out(demangle('__Z5multiwahtjmxyz')));
EM_ASM(out(demangle('__Z1aA32_iPA5_c')));
EM_ASM(out(demangle('__ZN21FWakaGLXFleeflsMarfooC2EjjjPKvbjj')));
EM_ASM(out(demangle('__ZN5wakaw2Cm10RasterBaseINS_6watwat9PolocatorEE8merbine1INS4_2OREEEvPKjj'))); // we get this wrong, but at least emit a '?'
one(17);
return 0;
}
''')
# full demangle support
run_process([PYTHON, EMCC, 'src.cpp', '-s', 'DEMANGLE_SUPPORT=1'])
output = run_js('a.out.js')
self.assertContained('''operator new(unsigned long)
_main
f2()
abcdabcdabcd(int)
abcdabcdabcd(int)
test(char, short, int, float, double, void*, int*, char*)
test::moarr(char, short, long, float, double, void*, int*, char*)
Waka::f::a23412341234::point()
void Foo<int>()
void Foo<int, double>(int)
void Foo::Bar<5>()
__cxxabiv1::__si_class_type_info::search_below_dst(__cxxabiv1::__dynamic_cast_info*, void const*, int, bool) const
parseword(char const*&, int, int&)
multi(wchar_t, signed char, unsigned char, unsigned short, unsigned int, unsigned long, long long, unsigned long long, ...)
a(int [32], char (*) [5])
FWakaGLXFleeflsMarfoo::FWakaGLXFleeflsMarfoo(unsigned int, unsigned int, unsigned int, void const*, bool, unsigned int, unsigned int)
void wakaw::Cm::RasterBase<wakaw::watwat::Polocator>::merbine1<wakaw::Cm::RasterBase<wakaw::watwat::Polocator>::OR>(unsigned int const*, unsigned int)
''', output)
# test for multiple functions in one stack trace
run_process([PYTHON, EMCC, 'src.cpp', '-s', 'DEMANGLE_SUPPORT=1', '-g'])
output = run_js('a.out.js')
self.assertIn('one(int)', output)
self.assertIn('two(char)', output)
def test_demangle_cpp(self):
create_test_file('src.cpp', '''
#include <stdio.h>
#include <emscripten.h>
#include <cxxabi.h>
#include <assert.h>
int main() {
char out[256];
int status = 1;
size_t length = 255;
abi::__cxa_demangle("_ZN4Waka1f12a234123412345pointEv", out, &length, &status);
assert(status == 0);
printf("%s\\n", out);
return 0;
}
''')
run_process([PYTHON, EMCC, 'src.cpp'])
output = run_js('a.out.js')
self.assertContained('Waka::f::a23412341234::point()', output)
def test_module_exports_with_closure(self):
# This test checks that module.export is retained when JavaScript is minified by compiling with --closure 1
# This is important as if module.export is not present the Module object will not be visible to node.js
# Run with ./runner.py other.test_module_exports_with_closure
# First make sure test.js isn't present.
self.clear()
# compile with -O2 --closure 0
run_process([PYTHON, EMCC, path_from_root('tests', 'Module-exports', 'test.c'), '-o', 'test.js', '-O2', '--closure', '0', '--pre-js', path_from_root('tests', 'Module-exports', 'setup.js'), '-s', 'EXPORTED_FUNCTIONS=["_bufferTest"]', '-s', 'EXTRA_EXPORTED_RUNTIME_METHODS=["ccall", "cwrap"]', '-s', 'BINARYEN_ASYNC_COMPILATION=0'], stdout=PIPE, stderr=PIPE)
# Check that compilation was successful
assert os.path.exists('test.js')
test_js_closure_0 = open('test.js').read()
# Check that test.js compiled with --closure 0 contains "module['exports'] = Module;"
assert ("module['exports'] = Module;" in test_js_closure_0) or ('module["exports"]=Module' in test_js_closure_0) or ('module["exports"] = Module;' in test_js_closure_0)
# Check that main.js (which requires test.js) completes successfully when run in node.js
# in order to check that the exports are indeed functioning correctly.
shutil.copyfile(path_from_root('tests', 'Module-exports', 'main.js'), 'main.js')
if NODE_JS in JS_ENGINES:
self.assertContained('bufferTest finished', run_js('main.js', engine=NODE_JS))
# Delete test.js again and check it's gone.
try_delete(path_from_root('tests', 'Module-exports', 'test.js'))
assert not os.path.exists(path_from_root('tests', 'Module-exports', 'test.js'))
# compile with -O2 --closure 1
run_process([PYTHON, EMCC, path_from_root('tests', 'Module-exports', 'test.c'), '-o', path_from_root('tests', 'Module-exports', 'test.js'), '-O2', '--closure', '1', '--pre-js', path_from_root('tests', 'Module-exports', 'setup.js'), '-s', 'EXPORTED_FUNCTIONS=["_bufferTest"]', '-s', 'BINARYEN_ASYNC_COMPILATION=0'], stdout=PIPE, stderr=PIPE)
# Check that compilation was successful
assert os.path.exists(path_from_root('tests', 'Module-exports', 'test.js'))
test_js_closure_1 = open(path_from_root('tests', 'Module-exports', 'test.js')).read()
# Check that test.js compiled with --closure 1 contains "module.exports", we want to verify that
# "module['exports']" got minified to "module.exports" when compiling with --closure 1
assert "module.exports" in test_js_closure_1
# Check that main.js (which requires test.js) completes successfully when run in node.js
# in order to check that the exports are indeed functioning correctly.
if NODE_JS in JS_ENGINES:
self.assertContained('bufferTest finished', run_js('main.js', engine=NODE_JS))
# Tidy up files that might have been created by this test.
try_delete(path_from_root('tests', 'Module-exports', 'test.js'))
try_delete(path_from_root('tests', 'Module-exports', 'test.js.map'))
try_delete(path_from_root('tests', 'Module-exports', 'test.js.mem'))
def test_node_catch_exit(self):
# Test that in node.js exceptions are not caught if NODEJS_EXIT_CATCH=0
if NODE_JS not in JS_ENGINES:
return
create_test_file('count.c', '''
#include <string.h>
int count(const char *str) {
return (int)strlen(str);
}
''')
create_test_file('index.js', '''
const count = require('./count.js');
console.log(xxx); //< here is the ReferenceError
''')
reference_error_text = 'console.log(xxx); //< here is the ReferenceError'
run_process([PYTHON, EMCC, 'count.c', '-o', 'count.js'])
# Check that the ReferenceError is caught and rethrown and thus the original error line is masked
self.assertNotContained(reference_error_text,
run_js('index.js', engine=NODE_JS, stderr=STDOUT, assert_returncode=None))
run_process([PYTHON, EMCC, 'count.c', '-o', 'count.js', '-s', 'NODEJS_CATCH_EXIT=0'])
# Check that the ReferenceError is not caught, so we see the error properly
self.assertContained(reference_error_text,
run_js('index.js', engine=NODE_JS, stderr=STDOUT, assert_returncode=None))
def test_extra_exported_methods(self):
# Test with node.js that the EXTRA_EXPORTED_RUNTIME_METHODS setting is considered by libraries
if NODE_JS not in JS_ENGINES:
self.skipTest("node engine required for this test")
create_test_file('count.c', '''
#include <string.h>
int count(const char *str) {
return (int)strlen(str);
}
''')
create_test_file('index.js', '''
const count = require('./count.js');
console.log(count.FS_writeFile);
''')
reference_error_text = 'undefined'
run_process([PYTHON, EMCC, 'count.c', '-s', 'FORCE_FILESYSTEM=1', '-s',
'EXTRA_EXPORTED_RUNTIME_METHODS=["FS_writeFile"]', '-o', 'count.js'])
# Check that the Module.FS_writeFile exists
self.assertNotContained(reference_error_text,
run_js('index.js', engine=NODE_JS, stderr=STDOUT, assert_returncode=None))
run_process([PYTHON, EMCC, 'count.c', '-s', 'FORCE_FILESYSTEM=1', '-o', 'count.js'])
# Check that the Module.FS_writeFile is not exported
self.assertContained(reference_error_text,
run_js('index.js', engine=NODE_JS, stderr=STDOUT, assert_returncode=None))
def test_fs_stream_proto(self):
open('src.cpp', 'wb').write(br'''
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
int main()
{
long file_size = 0;
int h = open("src.cpp", O_RDONLY, 0666);
if (0 != h)
{
FILE* file = fdopen(h, "rb");
if (0 != file)
{
fseek(file, 0, SEEK_END);
file_size = ftell(file);
fseek(file, 0, SEEK_SET);
}
else
{
printf("fdopen() failed: %s\n", strerror(errno));
return 10;
}
close(h);
printf("File size: %ld\n", file_size);
}
else
{
printf("open() failed: %s\n", strerror(errno));
return 10;
}
return 0;
}
''')
run_process([PYTHON, EMCC, 'src.cpp', '--embed-file', 'src.cpp'])
for engine in JS_ENGINES:
out = run_js('a.out.js', engine=engine, stderr=PIPE, full_output=True)
self.assertContained('File size: 724', out)
@no_wasm_backend('uses MAIN_MODULE')
def test_proxyfs(self):
# This test supposes that 3 different programs share the same directory and files.
# The same JS object is not used for each of them
# But 'require' function caches JS objects.
# If we just load same js-file multiple times like following code,
# these programs (m0,m1,m2) share the same JS object.
#
# var m0 = require('./proxyfs_test.js');
# var m1 = require('./proxyfs_test.js');
# var m2 = require('./proxyfs_test.js');
#
# To separate js-objects for each of them, following 'require' use different js-files.
#
# var m0 = require('./proxyfs_test.js');
# var m1 = require('./proxyfs_test1.js');
# var m2 = require('./proxyfs_test2.js');
#
create_test_file('proxyfs_test_main.js', r'''
var m0 = require('./proxyfs_test.js');
var m1 = require('./proxyfs_test1.js');
var m2 = require('./proxyfs_test2.js');
var section;
function print(str){
process.stdout.write(section+":"+str+":");
}
m0.FS.mkdir('/working');
m0.FS.mount(m0.PROXYFS,{root:'/',fs:m1.FS},'/working');
m0.FS.mkdir('/working2');
m0.FS.mount(m0.PROXYFS,{root:'/',fs:m2.FS},'/working2');
section = "child m1 reads and writes local file.";
print("m1 read embed");
m1.ccall('myreade','number',[],[]);
print("m1 write");console.log("");
m1.ccall('mywrite0','number',['number'],[1]);
print("m1 read");
m1.ccall('myread0','number',[],[]);
section = "child m2 reads and writes local file.";
print("m2 read embed");
m2.ccall('myreade','number',[],[]);
print("m2 write");console.log("");
m2.ccall('mywrite0','number',['number'],[2]);
print("m2 read");
m2.ccall('myread0','number',[],[]);
section = "child m1 reads local file.";
print("m1 read");
m1.ccall('myread0','number',[],[]);
section = "parent m0 reads and writes local and children's file.";
print("m0 read embed");
m0.ccall('myreade','number',[],[]);
print("m0 read m1");
m0.ccall('myread1','number',[],[]);
print("m0 read m2");
m0.ccall('myread2','number',[],[]);
section = "m0,m1 and m2 verify local files.";
print("m0 write");console.log("");
m0.ccall('mywrite0','number',['number'],[0]);
print("m0 read");
m0.ccall('myread0','number',[],[]);
print("m1 read");
m1.ccall('myread0','number',[],[]);
print("m2 read");
m2.ccall('myread0','number',[],[]);
print("m0 read embed");
m0.ccall('myreade','number',[],[]);
print("m1 read embed");
m1.ccall('myreade','number',[],[]);
print("m2 read embed");
m2.ccall('myreade','number',[],[]);
section = "parent m0 writes and reads children's files.";
print("m0 write m1");console.log("");
m0.ccall('mywrite1','number',[],[]);
print("m0 read m1");
m0.ccall('myread1','number',[],[]);
print("m0 write m2");console.log("");
m0.ccall('mywrite2','number',[],[]);
print("m0 read m2");
m0.ccall('myread2','number',[],[]);
print("m1 read");
m1.ccall('myread0','number',[],[]);
print("m2 read");
m2.ccall('myread0','number',[],[]);
print("m0 read m0");
m0.ccall('myread0','number',[],[]);
''')
create_test_file('proxyfs_pre.js', r'''
if (typeof Module === 'undefined') Module = {};
Module["noInitialRun"]=true;
Module["noExitRuntime"]=true;
''')
create_test_file('proxyfs_embed.txt', r'''test
''')
create_test_file('proxyfs_test.c', r'''
#include <stdio.h>
int
mywrite1(){
FILE* out = fopen("/working/hoge.txt","w");
fprintf(out,"test1\n");
fclose(out);
return 0;
}
int
myread1(){
FILE* in = fopen("/working/hoge.txt","r");
char buf[1024];
int len;
if(in==NULL)
printf("open failed\n");
while(! feof(in)){
if(fgets(buf,sizeof(buf),in)==buf){
printf("%s",buf);
}
}
fclose(in);
return 0;
}
int
mywrite2(){
FILE* out = fopen("/working2/hoge.txt","w");
fprintf(out,"test2\n");
fclose(out);
return 0;
}
int
myread2(){
{
FILE* in = fopen("/working2/hoge.txt","r");
char buf[1024];
int len;
if(in==NULL)
printf("open failed\n");
while(! feof(in)){
if(fgets(buf,sizeof(buf),in)==buf){
printf("%s",buf);
}
}
fclose(in);
}
return 0;
}
int
mywrite0(int i){
FILE* out = fopen("hoge.txt","w");
fprintf(out,"test0_%d\n",i);
fclose(out);
return 0;
}
int
myread0(){
{
FILE* in = fopen("hoge.txt","r");
char buf[1024];
int len;
if(in==NULL)
printf("open failed\n");
while(! feof(in)){
if(fgets(buf,sizeof(buf),in)==buf){
printf("%s",buf);
}
}
fclose(in);
}
return 0;
}
int
myreade(){
{
FILE* in = fopen("proxyfs_embed.txt","r");
char buf[1024];
int len;
if(in==NULL)
printf("open failed\n");
while(! feof(in)){
if(fgets(buf,sizeof(buf),in)==buf){
printf("%s",buf);
}
}
fclose(in);
}
return 0;
}
''')
run_process([PYTHON, EMCC,
'-o', 'proxyfs_test.js', 'proxyfs_test.c',
'--embed-file', 'proxyfs_embed.txt', '--pre-js', 'proxyfs_pre.js',
'-s', 'EXTRA_EXPORTED_RUNTIME_METHODS=["ccall", "cwrap"]',
'-s', 'BINARYEN_ASYNC_COMPILATION=0',
'-s', 'MAIN_MODULE=1',
'-s', 'EXPORT_ALL=1'])
# Following shutil.copyfile just prevent 'require' of node.js from caching js-object.
# See https://nodejs.org/api/modules.html
shutil.copyfile('proxyfs_test.js', 'proxyfs_test1.js')
shutil.copyfile('proxyfs_test.js', 'proxyfs_test2.js')
out = run_js('proxyfs_test_main.js')
section = "child m1 reads and writes local file."
self.assertContained(section + ":m1 read embed:test", out)
self.assertContained(section + ":m1 write:", out)
self.assertContained(section + ":m1 read:test0_1", out)
section = "child m2 reads and writes local file."
self.assertContained(section + ":m2 read embed:test", out)
self.assertContained(section + ":m2 write:", out)
self.assertContained(section + ":m2 read:test0_2", out)
section = "child m1 reads local file."
self.assertContained(section + ":m1 read:test0_1", out)
section = "parent m0 reads and writes local and children's file."
self.assertContained(section + ":m0 read embed:test", out)
self.assertContained(section + ":m0 read m1:test0_1", out)
self.assertContained(section + ":m0 read m2:test0_2", out)
section = "m0,m1 and m2 verify local files."
self.assertContained(section + ":m0 write:", out)
self.assertContained(section + ":m0 read:test0_0", out)
self.assertContained(section + ":m1 read:test0_1", out)
self.assertContained(section + ":m2 read:test0_2", out)
self.assertContained(section + ":m0 read embed:test", out)
self.assertContained(section + ":m1 read embed:test", out)
self.assertContained(section + ":m2 read embed:test", out)
section = "parent m0 writes and reads children's files."
self.assertContained(section + ":m0 write m1:", out)
self.assertContained(section + ":m0 read m1:test1", out)
self.assertContained(section + ":m0 write m2:", out)
self.assertContained(section + ":m0 read m2:test2", out)
self.assertContained(section + ":m1 read:test1", out)
self.assertContained(section + ":m2 read:test2", out)
self.assertContained(section + ":m0 read m0:test0_0", out)
def check_simd(self, expected_simds, expected_out):
if SPIDERMONKEY_ENGINE in JS_ENGINES:
out = run_js('a.out.js', engine=SPIDERMONKEY_ENGINE, stderr=PIPE, full_output=True)
self.validate_asmjs(out)
else:
out = run_js('a.out.js')
self.assertContained(expected_out, out)
src = open('a.out.js').read()
asm = src[src.find('// EMSCRIPTEN_START_FUNCS'):src.find('// EMSCRIPTEN_END_FUNCS')]
simds = asm.count('SIMD_')
assert simds >= expected_simds, 'expecting to see at least %d SIMD* uses, but seeing %d' % (expected_simds, simds)
@unittest.skip("autovectorization of this stopped in LLVM 6.0")
def test_autovectorize_linpack(self):
# TODO: investigate when SIMD arrives in wasm
run_process([PYTHON, EMCC, path_from_root('tests', 'linpack.c'), '-O2', '-s', 'SIMD=1', '-DSP', '-s', 'PRECISE_F32=1', '--profiling', '-s', 'WASM=0'])
self.check_simd(30, 'Unrolled Single Precision')
def test_dependency_file(self):
# Issue 1732: -MMD (and friends) create dependency files that need to be
# copied from the temporary directory.
create_test_file('test.cpp', r'''
#include "test.hpp"
void my_function()
{
}
''')
create_test_file('test.hpp', r'''
void my_function();
''')
run_process([PYTHON, EMCC, '-MMD', '-c', 'test.cpp', '-o', 'test.o'])
assert os.path.exists('test.d'), 'No dependency file generated'
deps = open('test.d').read()
# Look for ': ' instead of just ':' to not confuse C:\path\ notation with make "target: deps" rule. Not perfect, but good enough for this test.
head, tail = deps.split(': ', 2)
assert 'test.o' in head, 'Invalid dependency target'
assert 'test.cpp' in tail and 'test.hpp' in tail, 'Invalid dependencies generated'
def test_dependency_file_2(self):
self.clear()
shutil.copyfile(path_from_root('tests', 'hello_world.c'), 'a.c')
run_process([PYTHON, EMCC, 'a.c', '-MMD', '-MF', 'test.d', '-c'])
self.assertContained(open('test.d').read(), 'a.o: a.c\n')
self.clear()
shutil.copyfile(path_from_root('tests', 'hello_world.c'), 'a.c')
run_process([PYTHON, EMCC, 'a.c', '-MMD', '-MF', 'test.d', '-c', '-o', 'test.o'])
self.assertContained(open('test.d').read(), 'test.o: a.c\n')
self.clear()
shutil.copyfile(path_from_root('tests', 'hello_world.c'), 'a.c')
os.mkdir('obj')
run_process([PYTHON, EMCC, 'a.c', '-MMD', '-MF', 'test.d', '-c', '-o', 'obj/test.o'])
self.assertContained(open('test.d').read(), 'obj/test.o: a.c\n')
def test_quoted_js_lib_key(self):
create_test_file('lib.js', r'''
mergeInto(LibraryManager.library, {
__internal_data:{
'<' : 0,
'white space' : 1
},
printf__deps: ['__internal_data', 'fprintf']
});
''')
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '--js-library', 'lib.js'])
self.assertContained('hello, world!', run_js('a.out.js'))
def test_exported_js_lib(self):
create_test_file('lib.js', r'''
mergeInto(LibraryManager.library, {
jslibfunc: function(x) { return 2 * x }
});
''')
create_test_file('src.cpp', r'''
#include <emscripten.h>
#include <stdio.h>
extern "C" int jslibfunc(int x);
int main() {
printf("c calling: %d\n", jslibfunc(6));
EM_ASM({
out('js calling: ' + Module['_jslibfunc'](5) + '.');
});
}
''')
run_process([PYTHON, EMCC, 'src.cpp', '--js-library', 'lib.js', '-s', 'EXPORTED_FUNCTIONS=["_main", "_jslibfunc"]'])
self.assertContained('c calling: 12\njs calling: 10.', run_js('a.out.js'))
def test_js_lib_using_asm_lib(self):
create_test_file('lib.js', r'''
mergeInto(LibraryManager.library, {
jslibfunc__deps: ['asmlibfunc'],
jslibfunc: function(x) {
return 2 * _asmlibfunc(x);
},
asmlibfunc__asm: true,
asmlibfunc__sig: 'ii',
asmlibfunc: function(x) {
x = x | 0;
return x + 1 | 0;
}
});
''')
create_test_file('src.cpp', r'''
#include <stdio.h>
extern "C" int jslibfunc(int x);
int main() {
printf("c calling: %d\n", jslibfunc(6));
}
''')
run_process([PYTHON, EMCC, 'src.cpp', '--js-library', 'lib.js'])
self.assertContained('c calling: 14\n', run_js('a.out.js'))
def test_EMCC_BUILD_DIR(self):
# EMCC_BUILD_DIR env var contains the dir we were building in, when running the js compiler (e.g. when
# running a js library). We force the cwd to be src/ for technical reasons, so this lets you find out
# where you were.
create_test_file('lib.js', r'''
printErr('dir was ' + process.env.EMCC_BUILD_DIR);
''')
err = run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp'), '--js-library', 'lib.js'], stderr=PIPE).stderr
self.assertContained('dir was ' + os.path.realpath(os.path.normpath(self.get_dir())), err)
def test_float_h(self):
process = run_process([PYTHON, EMCC, path_from_root('tests', 'float+.c')], stdout=PIPE, stderr=PIPE)
assert process.returncode is 0, 'float.h should agree with our system: ' + process.stdout + '\n\n\n' + process.stderr
def test_default_obj_ext(self):
outdir = 'out_dir' + '/'
self.clear()
os.mkdir(outdir)
err = run_process([PYTHON, EMCC, '-c', path_from_root('tests', 'hello_world.c'), '-o', outdir], stderr=PIPE).stderr
assert not err, err
assert os.path.isfile(outdir + 'hello_world.o')
self.clear()
os.mkdir(outdir)
err = run_process([PYTHON, EMCC, '-c', path_from_root('tests', 'hello_world.c'), '-o', outdir, '--default-obj-ext', 'obj'], stderr=PIPE).stderr
assert not err, err
assert os.path.isfile(outdir + 'hello_world.obj')
def test_doublestart_bug(self):
create_test_file('code.cpp', r'''
#include <stdio.h>
#include <emscripten.h>
void main_loop(void) {
static int cnt = 0;
if (++cnt >= 10) emscripten_cancel_main_loop();
}
int main(void) {
printf("This should only appear once.\n");
emscripten_set_main_loop(main_loop, 10, 0);
return 0;
}
''')
create_test_file('pre.js', r'''
if (!Module['preRun']) Module['preRun'] = [];
Module["preRun"].push(function () {
addRunDependency('test_run_dependency');
removeRunDependency('test_run_dependency');
});
''')
run_process([PYTHON, EMCC, 'code.cpp', '--pre-js', 'pre.js'])
output = run_js('a.out.js', engine=NODE_JS)
assert output.count('This should only appear once.') == 1, output
def test_module_print(self):
create_test_file('code.cpp', r'''
#include <stdio.h>
int main(void) {
printf("123456789\n");
return 0;
}
''')
create_test_file('pre.js', r'''
var Module = { print: function(x) { throw '<{(' + x + ')}>' } };
''')
run_process([PYTHON, EMCC, 'code.cpp', '--pre-js', 'pre.js'])
output = run_js('a.out.js', stderr=PIPE, full_output=True, engine=NODE_JS, assert_returncode=None)
assert r'<{(123456789)}>' in output, output
def test_precompiled_headers(self):
for suffix in ['gch', 'pch']:
print(suffix)
self.clear()
create_test_file('header.h', '#define X 5\n')
run_process([PYTHON, EMCC, '-xc++-header', 'header.h', '-c'])
assert os.path.exists('header.h.gch') # default output is gch
if suffix != 'gch':
run_process([PYTHON, EMCC, '-xc++-header', 'header.h', '-o', 'header.h.' + suffix])
assert open('header.h.gch', 'rb').read() == open('header.h.' + suffix, 'rb').read()
create_test_file('src.cpp', r'''
#include <stdio.h>
int main() {
printf("|%d|\n", X);
return 0;
}
''')
run_process([PYTHON, EMCC, 'src.cpp', '-include', 'header.h'])
output = run_js('a.out.js', stderr=PIPE, full_output=True, engine=NODE_JS)
assert '|5|' in output, output
# also verify that the gch is actually used
err = run_process([PYTHON, EMCC, 'src.cpp', '-include', 'header.h', '-Xclang', '-print-stats'], stderr=PIPE).stderr
self.assertTextDataContained('*** PCH/Modules Loaded:\nModule: header.h.' + suffix, err)
# and sanity check it is not mentioned when not
try_delete('header.h.' + suffix)
err = run_process([PYTHON, EMCC, 'src.cpp', '-include', 'header.h', '-Xclang', '-print-stats'], stderr=PIPE).stderr
assert '*** PCH/Modules Loaded:\nModule: header.h.' + suffix not in err.replace('\r\n', '\n'), err
# with specified target via -o
try_delete('header.h.' + suffix)
run_process([PYTHON, EMCC, '-xc++-header', 'header.h', '-o', 'my.' + suffix])
assert os.path.exists('my.' + suffix)
# -include-pch flag
run_process([PYTHON, EMCC, '-xc++-header', 'header.h', '-o', 'header.h.' + suffix])
run_process([PYTHON, EMCC, 'src.cpp', '-include-pch', 'header.h.' + suffix])
output = run_js('a.out.js')
assert '|5|' in output, output
@no_wasm_backend()
def test_warn_unaligned(self):
create_test_file('src.cpp', r'''
#include <stdio.h>
struct packey {
char x;
int y;
double z;
} __attribute__((__packed__));
int main() {
volatile packey p;
p.x = 0;
p.y = 1;
p.z = 2;
return 0;
}
''')
output = run_process([PYTHON, EMCC, 'src.cpp', '-s', 'WASM=0', '-s', 'WARN_UNALIGNED=1'], stderr=PIPE)
output = run_process([PYTHON, EMCC, 'src.cpp', '-s', 'WASM=0', '-s', 'WARN_UNALIGNED=1', '-g'], stderr=PIPE)
assert 'emcc: warning: unaligned store' in output.stderr, output.stderr
assert 'emcc: warning: unaligned store' in output.stderr, output.stderr
assert '@line 11 "src.cpp"' in output.stderr, output.stderr
def test_LEGACY_VM_SUPPORT(self):
# when modern features are lacking, we can polyfill them or at least warn
create_test_file('pre.js', 'Math.imul = undefined;')
def test(expected, opts=[]):
print(opts)
result = run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '--pre-js', 'pre.js'] + opts, stderr=PIPE, check=False)
if result.returncode == 0:
self.assertContained(expected, run_js('a.out.js', stderr=PIPE, full_output=True, engine=NODE_JS, assert_returncode=None))
else:
self.assertContained(expected, result.stderr)
# when legacy is needed, we show an error indicating so
test('build with LEGACY_VM_SUPPORT')
# wasm is on by default, and does not mix with legacy, so we show an error
test('LEGACY_VM_SUPPORT is only supported for asm.js, and not wasm. Build with -s WASM=0', ['-s', 'LEGACY_VM_SUPPORT=1'])
# legacy + disabling wasm works
if self.is_wasm_backend():
return
test('hello, world!', ['-s', 'LEGACY_VM_SUPPORT=1', '-s', 'WASM=0'])
def test_on_abort(self):
expected_output = 'Module.onAbort was called'
def add_on_abort_and_verify(extra=''):
with open('a.out.js') as f:
js = f.read()
with open('a.out.js', 'w') as f:
f.write("var Module = { onAbort: function() { console.log('%s') } };\n" % expected_output)
f.write(extra + '\n')
f.write(js)
self.assertContained(expected_output, run_js('a.out.js', assert_returncode=None))
# test direct abort() C call
create_test_file('src.c', '''
#include <stdlib.h>
int main() {
abort();
}
''')
run_process([PYTHON, EMCC, 'src.c', '-s', 'BINARYEN_ASYNC_COMPILATION=0'])
add_on_abort_and_verify()
# test direct abort() JS call
create_test_file('src.c', '''
#include <emscripten.h>
int main() {
EM_ASM({ abort() });
}
''')
run_process([PYTHON, EMCC, 'src.c', '-s', 'BINARYEN_ASYNC_COMPILATION=0'])
add_on_abort_and_verify()
# test throwing in an abort handler, and catching that
create_test_file('src.c', '''
#include <emscripten.h>
int main() {
EM_ASM({
try {
out('first');
abort();
} catch (e) {
out('second');
abort();
throw e;
}
});
}
''')
run_process([PYTHON, EMCC, 'src.c', '-s', 'BINARYEN_ASYNC_COMPILATION=0'])
with open('a.out.js') as f:
js = f.read()
with open('a.out.js', 'w') as f:
f.write("var Module = { onAbort: function() { console.log('%s'); throw 're-throw'; } };\n" % expected_output)
f.write(js)
out = run_js('a.out.js', stderr=STDOUT, assert_returncode=None)
print(out)
self.assertContained(expected_output, out)
self.assertContained('re-throw', out)
self.assertContained('first', out)
self.assertContained('second', out)
self.assertEqual(out.count(expected_output), 2)
# test an abort during startup
run_process([PYTHON, EMCC, path_from_root('tests', 'hello_world.c')])
os.remove('a.out.wasm') # trigger onAbort by intentionally causing startup to fail
add_on_abort_and_verify()
def test_no_exit_runtime(self):
create_test_file('code.cpp', r'''
#include <stdio.h>
template<int x>
struct Waste {
Waste() {
printf("coming around %d\n", x);
}
~Waste() {
printf("going away %d\n", x);
}
};
Waste<1> w1;
Waste<2> w2;
Waste<3> w3;
Waste<4> w4;
Waste<5> w5;
int main(int argc, char **argv) {
return 0;
}
''')