def test_emcc_caching(self): INCLUDING_MESSAGE = 'including X' BUILDING_MESSAGE = 'building X for cache' ERASING_MESSAGE = 'clearing cache' EMCC_CACHE = Cache.dirname restore_and_set_up() Cache.erase() assert not os.path.exists(EMCC_CACHE) with env_modify({'EMCC_DEBUG': '1'}): # Building a file that *does* need something *should* trigger cache # generation, but only the first time for filename, libname in [('hello_libcxx.cpp', 'libcxx')]: for i in range(3): print(filename, libname, i) self.clear() output = self.do([EMCC, '-O' + str(i), '-s', '--llvm-lto', '0', path_from_root('tests', filename), '--save-bc', 'a.bc', '-s', 'DISABLE_EXCEPTION_CATCHING=0']) # print '\n\n\n', output assert INCLUDING_MESSAGE.replace('X', libname) in output if libname == 'libc': assert INCLUDING_MESSAGE.replace('X', 'libcxx') not in output # we don't need libcxx in this code else: assert INCLUDING_MESSAGE.replace('X', 'libc') in output # libcxx always forces inclusion of libc assert (BUILDING_MESSAGE.replace('X', libname) in output) == (i == 0), 'Must only build the first time' self.assertContained('hello, world!', run_js('a.out.js')) assert os.path.exists(EMCC_CACHE) full_libname = libname + '.bc' if libname != 'libcxx' else libname + '.a' assert os.path.exists(os.path.join(EMCC_CACHE, full_libname)) try_delete(CANONICAL_TEMP_DIR) restore_and_set_up() def ensure_cache(): self.do([PYTHON, EMCC, '-O2', path_from_root('tests', 'hello_world.c')]) # Manual cache clearing ensure_cache() self.assertTrue(os.path.exists(EMCC_CACHE)) output = self.do([PYTHON, EMCC, '--clear-cache']) self.assertIn(ERASING_MESSAGE, output) self.assertFalse(os.path.exists(EMCC_CACHE)) self.assertIn(SANITY_MESSAGE, output) # Changing LLVM_ROOT, even without altering .emscripten, clears the cache ensure_cache() with env_modify({'LLVM': 'waka'}): self.assertTrue(os.path.exists(EMCC_CACHE)) output = self.do([PYTHON, EMCC]) self.assertIn(ERASING_MESSAGE, output) self.assertFalse(os.path.exists(EMCC_CACHE))
def test_with_fake(report, expected): make_fake(report) with env_modify({'EMCC_DEBUG': '1'}): output = self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], expected) self.assertContained( 'config file changed since we checked vanilla', output)
def test_emcc_multiprocess_cache_access(self): restore_and_set_up() create_file( 'test.c', r''' #include <stdio.h> int main() { printf("hello, world!\n"); return 0; } ''') cache_dir_name = self.in_dir('test_cache') libname = Cache.get_lib_name('libc.a') with env_modify({'EM_CACHE': cache_dir_name}): tasks = [] num_times_libc_was_built = 0 for i in range(3): p = self.run_process( [EMCC, 'test.c', '-o', '%d.js' % i], stderr=STDOUT, stdout=PIPE) tasks += [p] for p in tasks: print('stdout:\n', p.stdout) if 'generating system library: ' + libname in p.stdout: num_times_libc_was_built += 1 # The cache directory must exist after the build self.assertExists(cache_dir_name) # The cache directory must contain a built libc self.assertExists(os.path.join(cache_dir_name, libname)) # Exactly one child process should have triggered libc build! self.assertEqual(num_times_libc_was_built, 1)
def test_llvm(self): LLVM_WARNING = 'LLVM version appears incorrect' restore_and_set_up() # Clang should report the version number we expect, and emcc should not warn assert shared.check_llvm_version() output = self.check_working(EMCC) self.assertNotContained(LLVM_WARNING, output) # Fake a different llvm version restore_and_set_up() with open(config_file, 'a') as f: f.write('LLVM_ROOT = "' + self.in_dir('fake') + '"') real_version_x, real_version_y = (int(x) for x in EXPECTED_LLVM_VERSION.split('.')) make_fake_llc(self.in_dir('fake', 'llc'), 'wasm32 - WebAssembly 32-bit') make_fake_lld(self.in_dir('fake', 'wasm-ld')) with env_modify({'EM_IGNORE_SANITY': '1'}): for inc_x in range(-2, 3): for inc_y in range(-2, 3): expected_x = real_version_x + inc_x expected_y = real_version_y + inc_y if expected_x < 0 or expected_y < 0: continue # must be a valid llvm version print("mod LLVM version: %d %d -> %d %d" % (real_version_x, real_version_x, expected_x, expected_y)) make_fake_clang(self.in_dir('fake', 'clang'), '%s.%s' % (expected_x, expected_y)) did_modify = inc_x != 0 or inc_y != 0 if did_modify: output = self.check_working(EMCC, LLVM_WARNING) else: output = self.check_working(EMCC) self.assertNotContained(LLVM_WARNING, output)
def test_js_engine_path(self): # Test that running JS commands works for node, d8, and jsc and is not path dependent restore_and_set_up() sample_script = path_from_root('tests', 'print_args.js') # Fake some JS engines # Note that the path contains 'd8'. test_path = self.in_dir('fake', 'abcd8765') ensure_dir(test_path) with env_modify({'EM_IGNORE_SANITY': '1'}): jsengines = [('d8', V8_ENGINE), ('d8_g', V8_ENGINE), ('js', SPIDERMONKEY_ENGINE), ('node', NODE_JS), ('nodejs', NODE_JS)] for filename, engine in jsengines: if type(engine) is list: engine = engine[0] if not engine: print('WARNING: Not testing engine %s, not configured.' % (filename)) continue print(filename, engine) test_engine_path = os.path.join(test_path, filename) with open(test_engine_path, 'w') as f: f.write('#!/bin/sh\n') f.write('exec %s $@\n' % (engine)) os.chmod(test_engine_path, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) out = run_js(sample_script, engine=test_engine_path, args=['--foo']) self.assertEqual('0: --foo', out.strip())
def test_wacky_env(self): restore_and_set_up() def build(): return self.check_working([EMCC] + MINIMAL_HELLO_WORLD, '') def test(): self.assertContained('hello, world!', run_js('a.out.js')) print('normal build') with env_modify({'EMCC_FORCE_STDLIBS': None}): Cache.erase() build() test() print('wacky env vars, these should not mess our bootstrapping') with env_modify({'EMCC_FORCE_STDLIBS': '1'}): Cache.erase() build() test()
def test_cache_clearing_auto(self): # Changing LLVM_ROOT, even without altering .emscripten, clears the cache restore_and_set_up() self.ensure_cache() make_fake_clang(self.in_dir('fake', 'bin', 'clang'), EXPECTED_LLVM_VERSION) make_fake_llc(self.in_dir('fake', 'bin', 'llc'), 'got wasm32 backend! WebAssembly 32-bit') with env_modify({'EM_LLVM_ROOT': self.in_dir('fake', 'bin')}): self.assertExists(Cache.dirname) output = self.do([EMCC]) self.assertIn('clearing cache', output) self.assertCacheEmpty()
def test_wacky_env(self): restore_and_set_up() def build(): return self.check_working([EMCC] + MINIMAL_HELLO_WORLD, '') def test(): self.assertContained('hello, world!', self.run_js('a.out.js')) print('normal build') with env_modify({'EMCC_FORCE_STDLIBS': None}): self.clear_cache() build() test() print('wacky env vars, these should not mess our bootstrapping') with env_modify({'EMCC_FORCE_STDLIBS': '1'}): self.clear_cache() build() test()
def test_emcc(self): SANITY_FAIL_MESSAGE = 'sanity check failed to run' # emcc should check sanity if no ${EM_CONFIG}_sanity restore_and_set_up() time.sleep(1) assert not os.path.exists( SANITY_FILE) # restore is just the settings, not the sanity output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) # EMCC should have checked sanity successfully old_sanity = open(SANITY_FILE).read() self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc run again should not sanity check, because the sanity file is newer output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # incorrect sanity contents mean we *must* check open(SANITY_FILE, 'w').write('wakawaka') output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) # correct sanity contents mean we need not check open(SANITY_FILE, 'w').write(old_sanity) output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) # but with EMCC_DEBUG=1 we should check with env_modify({'EMCC_DEBUG': '1'}): output = self.check_working(EMCC) try_delete(CANONICAL_TEMP_DIR) self.assertContained(SANITY_MESSAGE, output) output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) # also with -v, with or without inputs output = self.check_working([EMCC, '-v'], SANITY_MESSAGE) output = self.check_working([EMCC, '-v'] + MINIMAL_HELLO_WORLD + [], SANITY_MESSAGE) # Make sure the test runner didn't do anything to the setup output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc should also check sanity if the file is outdated open(CONFIG_FILE, 'a').write('# extra stuff\n') output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) self.assertNotContained(SANITY_FAIL_MESSAGE, output)
def test_emcc_caching(self): BUILDING_MESSAGE = 'generating system library: X' ERASING_MESSAGE = 'clearing cache' restore_and_set_up() self.erase_cache() # Building a file that *does* need something *should* trigger cache # generation, but only the first time libname = 'libc++' for i in range(3): print(i) self.clear() output = self.do([ EMCC, '-O' + str(i), '-s', '--llvm-lto', '0', path_from_root('tests', 'hello_libcxx.cpp'), '--save-bc', 'a.bc', '-s', 'DISABLE_EXCEPTION_CATCHING=0' ]) print('\n\n\n', output) assert (BUILDING_MESSAGE.replace('X', libname) in output) == (i == 0), 'Must only build the first time' self.assertContained('hello, world!', run_js('a.out.js')) self.assertExists(Cache.dirname) full_libname = libname + '.bc' if libname != 'libc++' else libname + '.a' self.assertExists(os.path.join(Cache.dirname, full_libname)) restore_and_set_up() # Manual cache clearing self.ensure_cache() self.assertTrue(os.path.exists(Cache.dirname)) self.assertTrue(os.path.exists(Cache.root_dirname)) output = self.do([PYTHON, EMCC, '--clear-cache']) self.assertIn(ERASING_MESSAGE, output) self.assertFalse(os.path.exists(Cache.dirname)) self.assertFalse(os.path.exists(Cache.root_dirname)) self.assertIn(SANITY_MESSAGE, output) # Changing LLVM_ROOT, even without altering .emscripten, clears the cache self.ensure_cache() make_fake_clang(path_from_root('tests', 'fake', 'bin', 'clang'), expected_llvm_version()) make_fake_llc(path_from_root('tests', 'fake', 'bin', 'llc'), 'js - JavaScript (asm.js, emscripten)') with env_modify({'LLVM': path_from_root('tests', 'fake', 'bin')}): self.assertTrue(os.path.exists(Cache.dirname)) output = self.do([PYTHON, EMCC]) self.assertIn(ERASING_MESSAGE, output) self.assertFalse(os.path.exists(Cache.dirname))
def test_d8_path(self): """ Test that running JS commands works for node, d8, and jsc and is not path dependent """ # Fake some JS engines restore_and_set_up() sample_script = path_from_root('tests', 'print_args.js') # Note that the path contains 'd8'. test_path = path_from_root('tests', 'fake', 'abcd8765') if not os.path.exists(test_path): os.makedirs(test_path) with env_modify({'EM_IGNORE_SANITY': '1'}): jsengines = [('d8', V8_ENGINE), ('d8_g', V8_ENGINE), ('js', SPIDERMONKEY_ENGINE), ('node', NODE_JS), ('nodejs', NODE_JS)] for filename, engine in jsengines: if type(engine) is list: engine = engine[0] if engine == '': print('WARNING: Not testing engine %s, not configured.' % (filename)) continue print(filename, engine) test_engine_path = os.path.join(test_path, filename) f = open(test_engine_path, 'w') f.write('#!/bin/sh\n') f.write('%s $@\n' % (engine)) f.close() os.chmod(test_engine_path, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) try: out = jsrun.run_js(sample_script, engine=test_engine_path, args=['--foo'], full_output=True, assert_returncode=0, skip_check=True) except Exception as e: if 'd8' in filename: assert False, 'Your d8 version does not correctly parse command-line arguments, please upgrade or delete from ~/.emscripten config file: %s' % ( e) else: assert False, 'Error running script command: %s' % (e) self.assertEqual('0: --foo', out.strip())
def test_em_config_env_var(self): # emcc should be configurable directly from EM_CONFIG without any config file restore_and_set_up() create_test_file('main.cpp', ''' #include <stdio.h> int main() { printf("hello from emcc with no config file\\n"); return 0; } ''') wipe() with env_modify({'EM_CONFIG': get_basic_config()}): self.run_process([EMCC, 'main.cpp', '-Wno-deprecated', '-o', 'a.out.js']) self.assertContained('hello from emcc with no config file', self.run_js('a.out.js'))
def test_node(self): NODE_WARNING = 'node version appears too old' NODE_WARNING_2 = 'cannot check node version' restore_and_set_up() # Clang should report the version number we expect, and emcc should not warn assert shared.check_node_version() output = self.check_working(EMCC) self.assertNotIn(NODE_WARNING, output) # Fake a different node version restore_and_set_up() f = open(CONFIG_FILE, 'a') f.write('NODE_JS = "' + path_from_root('tests', 'fake', 'nodejs') + '"') f.close() if not os.path.exists(path_from_root('tests', 'fake')): os.makedirs(path_from_root('tests', 'fake')) with env_modify({'EM_IGNORE_SANITY': '1'}): for version, succeed in [('v0.8.0', False), ('v4.1.0', False), ('v4.1.1', True), ('v4.2.3-pre', True), ('cheez', False)]: print(version, succeed) f = open(path_from_root('tests', 'fake', 'nodejs'), 'w') f.write('#!/bin/sh\n') f.write('''if [ $1 = "--version" ]; then echo "%s" else %s $@ fi ''' % (version, NODE_JS)) f.close() os.chmod(path_from_root('tests', 'fake', 'nodejs'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) if not succeed: if version[0] == 'v': self.check_working(EMCC, NODE_WARNING) else: self.check_working(EMCC, NODE_WARNING_2) else: output = self.check_working(EMCC) assert NODE_WARNING not in output, output
def test_node(self): NODE_WARNING = 'node version appears too old' NODE_WARNING_2 = 'cannot check node version' restore_and_set_up() # Clang should report the version number we expect, and emcc should not warn assert shared.check_node_version() output = self.check_working(EMCC) self.assertNotIn(NODE_WARNING, output) # Fake a different node version restore_and_set_up() with open(CONFIG_FILE, 'a') as f: f.write('NODE_JS = "' + self.in_dir('fake', 'nodejs') + '"') ensure_dir('fake') with env_modify({'EM_IGNORE_SANITY': '1'}): for version, succeed in [('v0.8.0', False), ('v4.1.0', False), ('v4.1.1', True), ('v4.2.3-pre', True), ('cheez', False)]: print(version, succeed) f = open(self.in_dir('fake', 'nodejs'), 'w') f.write('#!/bin/sh\n') f.write('''if [ $1 = "--version" ]; then echo "%s" else %s $@ fi ''' % (version, NODE_JS)) f.close() os.chmod(self.in_dir('fake', 'nodejs'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) if not succeed: if version[0] == 'v': self.check_working(EMCC, NODE_WARNING) else: self.check_working(EMCC, NODE_WARNING_2) else: output = self.check_working(EMCC) assert NODE_WARNING not in output, output
def test_d8_path(self): """ Test that running JS commands works for node, d8, and jsc and is not path dependent """ # Fake some JS engines restore_and_set_up() sample_script = path_from_root('tests', 'print_args.js') # Note that the path contains 'd8'. test_path = path_from_root('tests', 'fake', 'abcd8765') if not os.path.exists(test_path): os.makedirs(test_path) with env_modify({'EM_IGNORE_SANITY': '1'}): jsengines = [('d8', V8_ENGINE), ('d8_g', V8_ENGINE), ('js', SPIDERMONKEY_ENGINE), ('node', NODE_JS), ('nodejs', NODE_JS)] for filename, engine in jsengines: if type(engine) is list: engine = engine[0] if engine == '': print('WARNING: Not testing engine %s, not configured.' % (filename)) continue print(filename, engine) test_engine_path = os.path.join(test_path, filename) f = open(test_engine_path, 'w') f.write('#!/bin/sh\n') f.write('%s $@\n' % (engine)) f.close() os.chmod(test_engine_path, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) try: out = jsrun.run_js(sample_script, engine=test_engine_path, args=['--foo'], full_output=True, assert_returncode=0, skip_check=True) except Exception as e: if 'd8' in filename: assert False, 'Your d8 version does not correctly parse command-line arguments, please upgrade or delete from ~/.emscripten config file: %s' % (e) else: assert False, 'Error running script command: %s' % (e) self.assertEqual('0: --foo', out.strip())
def test_llvm(self): LLVM_WARNING = 'LLVM version appears incorrect' restore_and_set_up() # Clang should report the version number we expect, and emcc should not warn assert shared.check_llvm_version() output = self.check_working(EMCC) self.assertNotContained(LLVM_WARNING, output) # Fake a different llvm version restore_and_set_up() with open(CONFIG_FILE, 'a') as f: f.write('LLVM_ROOT = "' + path_from_root('tests', 'fake') + '"') real_version_x, real_version_y = ( int(x) for x in expected_llvm_version().split('.')) if shared.get_llvm_target() == shared.WASM_TARGET: make_fake_llc(path_from_root('tests', 'fake', 'llc'), 'wasm32 - WebAssembly 32-bit') make_fake_lld(path_from_root('tests', 'fake', 'wasm-ld')) else: make_fake_llc(path_from_root('tests', 'fake', 'llc'), 'js - JavaScript (asm.js, emscripten)') with env_modify({'EM_IGNORE_SANITY': '1'}): for inc_x in range(-2, 3): for inc_y in range(-2, 3): expected_x = real_version_x + inc_x expected_y = real_version_y + inc_y if expected_x < 0 or expected_y < 0: continue # must be a valid llvm version print("mod LLVM version: %d %d -> %d %d" % (real_version_x, real_version_x, expected_x, expected_y)) make_fake_clang(path_from_root('tests', 'fake', 'clang'), '%s.%s' % (expected_x, expected_y)) did_modify = inc_x != 0 or inc_y != 0 if did_modify: output = self.check_working(EMCC, LLVM_WARNING) else: output = self.check_working(EMCC) self.assertNotContained(LLVM_WARNING, output)
def test_em_config_env_var(self): # emcc should be configurable directly from EM_CONFIG without any config file restore_and_set_up() create_file( 'main.cpp', ''' #include <stdio.h> int main() { printf("hello from emcc with no config file\\n"); return 0; } ''') wipe() with env_modify({'EM_CONFIG': get_basic_config()}): out = self.expect_fail( [EMCC, 'main.cpp', '-Wno-deprecated', '-o', 'a.out.js']) self.assertContained( 'error: Inline EM_CONFIG data no longer supported. Please use a config file.', out)
def test_llvm(self): LLVM_WARNING = 'LLVM version appears incorrect' restore_and_set_up() # Clang should report the version number we expect, and emcc should not warn assert shared.check_clang_version() output = self.check_working(EMCC) assert LLVM_WARNING not in output, output # Fake a different llvm version restore_and_set_up() with open(CONFIG_FILE, 'a') as f: f.write('LLVM_ROOT = "' + path_from_root('tests', 'fake') + '"') if not os.path.exists(path_from_root('tests', 'fake')): os.makedirs(path_from_root('tests', 'fake')) with env_modify({'EM_IGNORE_SANITY': '1'}): for x in range(-2, 3): for y in range(-2, 3): f = open(path_from_root('tests', 'fake', 'clang'), 'w') f.write('#!/bin/sh\n') expected_x, expected_y = (int(x) for x in expected_llvm_version().split('.')) expected_x += x expected_y += y if expected_x < 0 or expected_y < 0: continue # must be a valid llvm version print(expected_llvm_version(), x, y, expected_x, expected_y) f.write('echo "clang version %d.%d" 1>&2\n' % (expected_x, expected_y)) f.close() shutil.copyfile(path_from_root('tests', 'fake', 'clang'), path_from_root('tests', 'fake', 'clang++')) os.chmod(path_from_root('tests', 'fake', 'clang'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) os.chmod(path_from_root('tests', 'fake', 'clang++'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) if x != 0 or y != 0: output = self.check_working(EMCC, LLVM_WARNING) else: output = self.check_working(EMCC) assert LLVM_WARNING not in output, output
def test_emcc(self): SANITY_FAIL_MESSAGE = 'sanity check failed to run' # emcc should check sanity if no ${EM_CONFIG}_sanity restore_and_set_up() time.sleep(1) assert not os.path.exists(SANITY_FILE) # restore is just the settings, not the sanity output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) assert os.path.exists(SANITY_FILE) # EMCC should have checked sanity successfully assert mtime(SANITY_FILE) > mtime(CONFIG_FILE) assert generate_sanity() == open(SANITY_FILE).read() self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc run again should not sanity check, because the sanity file is newer output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # correct sanity contents mean we need not check open(SANITY_FILE, 'w').write(generate_sanity()) output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) # incorrect sanity contents mean we *must* check open(SANITY_FILE, 'w').write('wakawaka') output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) # but with EMCC_DEBUG=1 we should check with env_modify({'EMCC_DEBUG': '1'}): output = self.check_working(EMCC) try_delete(CANONICAL_TEMP_DIR) self.assertContained(SANITY_MESSAGE, output) output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) # also with -v, with or without inputs output = self.check_working([EMCC, '-v'], SANITY_MESSAGE) output = self.check_working([EMCC, '-v'] + MINIMAL_HELLO_WORLD + [], SANITY_MESSAGE) # Make sure the test runner didn't do anything to the setup output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc should also check sanity if the file is outdated time.sleep(0.1) restore_and_set_up() assert mtime(SANITY_FILE) < mtime(CONFIG_FILE) output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) assert mtime(SANITY_FILE) >= mtime(CONFIG_FILE) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc should be configurable directly from EM_CONFIG without any config file restore_and_set_up() config = open(CONFIG_FILE, 'r').read() open('main.cpp', 'w').write(''' #include <stdio.h> int main() { printf("hello from emcc with no config file\\n"); return 0; } ''') wipe() with env_modify({'EM_CONFIG': config}): run_process([PYTHON, EMCC, 'main.cpp', '-o', 'a.out.js']) self.assertContained('hello from emcc with no config file', run_js('a.out.js'))
def test_with_fake(report, expected): make_fake(report) with env_modify({'EMCC_DEBUG': '1'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], expected)
def test_vanilla(self): restore_and_set_up() Cache.erase() with env_modify({'EMCC_DEBUG': '1'}): # see that we test vanilla status, and just once TESTING = 'testing for asm.js target' self.check_working(EMCC, TESTING) for i in range(3): output = self.check_working(EMCC, 'check tells us to use') assert TESTING not in output # if env var tells us, do what it says with env_modify({'EMCC_WASM_BACKEND': '1'}): self.check_working(EMCC, 'EMCC_WASM_BACKEND tells us to use wasm backend') with env_modify({'EMCC_WASM_BACKEND': '0'}): self.check_working(EMCC, 'EMCC_WASM_BACKEND tells us to use asm.js backend') def make_fake(report): with open(CONFIG_FILE, 'a') as f: f.write('LLVM_ROOT = "' + path_from_root('tests', 'fake', 'bin') + '"\n') # BINARYEN_ROOT needs to exist in the config, even though this test # doesn't actually use it. f.write('BINARYEN_ROOT= "%s"\n' % path_from_root('tests', 'fake', 'bin')) with open(path_from_root('tests', 'fake', 'bin', 'llc'), 'w') as f: f.write('#!/bin/sh\n') f.write('echo "llc fake output\nRegistered Targets:\n%s"' % report) os.chmod(path_from_root('tests', 'fake', 'bin', 'llc'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) with open(path_from_root('tests', 'fake', 'bin', 'wasm-ld'), 'w') as f: f.write('#!/bin/sh\n') f.write('exit 0\n') os.chmod(path_from_root('tests', 'fake', 'bin', 'wasm-ld'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) with env_modify({'EMCC_DEBUG': '1'}): make_fake('wasm32-unknown-unknown-elf') # see that we request the right backend from llvm with env_modify({'EMCC_WASM_BACKEND': '1'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'wasm32-unknown-unknown-elf') make_fake('asmjs-unknown-emscripten') with env_modify({'EMCC_WASM_BACKEND': '0'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'asmjs-unknown-emscripten') # check the current installed one is ok restore_and_set_up() self.check_working(EMCC) output = self.check_working(EMCC, 'check tells us to use') if 'wasm backend' in output: self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'wasm32-unknown-unknown-elf') else: assert 'asm.js backend' in output self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'asmjs-unknown-emscripten') # fake llc output try_delete(path_from_root('tests', 'fake')) os.makedirs(path_from_root('tests', 'fake', 'bin')) def test_with_fake(report, expected): make_fake(report) with env_modify({'EMCC_DEBUG': '1'}): output = self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], expected) self.assertContained('config file changed since we checked vanilla', output) test_with_fake('got js backend! JavaScript (asm.js, emscripten) backend', 'check tells us to use asm.js backend') test_with_fake('got wasm32 backend! WebAssembly 32-bit', 'check tells us to use wasm backend') # use LLVM env var to modify LLVM between vanilla checks assert not os.environ.get('LLVM'), 'we need to modify LLVM env var for this' f = open(CONFIG_FILE, 'a') f.write('LLVM_ROOT = os.getenv("LLVM", "' + path_from_root('tests', 'fake1', 'bin') + '")\n') f.close() safe_ensure_dirs(path_from_root('tests', 'fake1', 'bin')) f = open(path_from_root('tests', 'fake1', 'bin', 'llc'), 'w') f.write('#!/bin/sh\n') f.write('echo "llc fake1 output\nRegistered Targets:\n%s"' % 'got js backend! JavaScript (asm.js, emscripten) backend') f.close() os.chmod(path_from_root('tests', 'fake1', 'bin', 'llc'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) safe_ensure_dirs(path_from_root('tests', 'fake2', 'bin')) f = open(path_from_root('tests', 'fake2', 'bin', 'llc'), 'w') f.write('#!/bin/sh\n') f.write('echo "llc fake2 output\nRegistered Targets:\n%s"' % 'got wasm32 backend! WebAssembly 32-bit') f.close() os.chmod(path_from_root('tests', 'fake2', 'bin', 'llc'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) with env_modify({'EMCC_DEBUG': '1'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'use asm.js backend') with env_modify({'LLVM': path_from_root('tests', 'fake2', 'bin')}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'regenerating vanilla check since other llvm') try_delete(CANONICAL_TEMP_DIR) return # TODO: the rest of this # check separate cache dirs are used restore_and_set_up() self.check_working([EMCC], '') root_cache = os.path.expanduser('~/.emscripten_cache') if os.path.exists(os.path.join(root_cache, 'asmjs')): shutil.rmtree(os.path.join(root_cache, 'asmjs')) if os.path.exists(os.path.join(root_cache, 'wasm')): shutil.rmtree(os.path.join(root_cache, 'wasm')) with env_modify({'EMCC_WASM_BACKEND': '1'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD, '') assert os.path.exists(os.path.join(root_cache, 'wasm')) with env_modify({'EMCC_WASM_BACKEND': '0'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD, '') assert os.path.exists(os.path.join(root_cache, 'asmjs')) shutil.rmtree(os.path.join(root_cache, 'asmjs')) self.check_working([EMCC] + MINIMAL_HELLO_WORLD, '') assert os.path.exists(os.path.join(root_cache, 'asmjs'))
def test_native_optimizer(self): restore_and_set_up() def build(): return self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-O2', '-s', 'WASM=0'], 'running js post-opts') def test(): self.assertContained('hello, world!', run_js('a.out.js')) with env_modify({'EMCC_DEBUG': '1'}): # basic usage or lack of usage for native in [None, 0, 1]: print('phase 1, part', native) Cache.erase() try: if native is not None: os.environ['EMCC_NATIVE_OPTIMIZER'] = str(native) output = build() assert ('js optimizer using native' in output) == (not not (native or native is None)), output test() if native or native is None: # None means use the default, which is to use the native optimizer assert 'building native optimizer' in output, output # compile again, no rebuild of optimizer output = build() assert 'building native optimizer' not in output assert 'js optimizer using native' in output test() finally: if native is not None: del os.environ['EMCC_NATIVE_OPTIMIZER'] # force a build failure, see we fall back to non-native for native in [1, 'g']: with env_modify({'EMCC_NATIVE_OPTIMIZER': str(native)}): print('phase 2, part', native) Cache.erase() try: # break it f = path_from_root('tools', 'optimizer', 'optimizer-main.cpp') src = open(f).read() bad = src.replace('main', '!waka waka<') assert bad != src open(f, 'w').write(bad) # first try output = build() assert 'failed to build native optimizer' in output, output if native == 1: assert 'to see compiler errors, build with EMCC_NATIVE_OPTIMIZER=g' in output assert 'waka waka' not in output else: assert 'output from attempt' in output, output assert 'waka waka' in output, output assert 'js optimizer using native' not in output test() # still works, without native optimizer # second try, see previous failure output = build() assert 'failed to build native optimizer' not in output assert 'seeing that optimizer could not be built' in output test() # still works, without native optimizer # clear cache, try again Cache.erase() output = build() assert 'failed to build native optimizer' in output test() # still works, without native optimizer finally: open(f, 'w').write(src) Cache.erase() # now it should work again output = build() assert 'js optimizer using native' in output test() # still works try_delete(CANONICAL_TEMP_DIR)
def test_vanilla(self): restore_and_set_up() Cache.erase() with env_modify({'EMCC_DEBUG': '1'}): # see that we test vanilla status, and just once TESTING = 'testing for asm.js target' print(self.check_working(EMCC, TESTING)) for i in range(3): output = self.check_working(EMCC, 'check tells us to use') self.assertNotContained(TESTING, output) # if env var tells us, do what it says with env_modify({'EMCC_WASM_BACKEND': '1'}): self.check_working( EMCC, 'EMCC_WASM_BACKEND tells us to use wasm backend') with env_modify({'EMCC_WASM_BACKEND': '0'}): self.check_working( EMCC, 'EMCC_WASM_BACKEND tells us to use asm.js backend') def make_fake(report): with open(CONFIG_FILE, 'a') as f: f.write('LLVM_ROOT = "' + self.in_dir('fake', 'bin') + '"\n') # BINARYEN_ROOT needs to exist in the config, even though this test # doesn't actually use it. f.write('BINARYEN_ROOT= "%s"\n' % self.in_dir('fake', 'bin')) make_fake_clang(self.in_dir('fake', 'bin', 'clang'), expected_llvm_version()) make_fake_llc(self.in_dir('fake', 'bin', 'llc'), report) make_fake_lld(self.in_dir('fake', 'bin', 'wasm-ld')) with env_modify({'EMCC_DEBUG': '1'}): make_fake('wasm32-unknown-unknown-elf') # see that we request the right backend from llvm with env_modify({'EMCC_WASM_BACKEND': '1'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'wasm32-unknown-unknown-elf') make_fake('asmjs-unknown-emscripten') with env_modify({'EMCC_WASM_BACKEND': '0'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'asmjs-unknown-emscripten') # check the current installed one is ok restore_and_set_up() self.check_working(EMCC) output = self.check_working(EMCC, 'check tells us to use') if 'wasm backend' in output: self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'wasm32-unknown-unknown-elf') else: assert 'asm.js backend' in output self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'asmjs-unknown-emscripten') # fake llc output def test_with_fake(report, expected): make_fake(report) with env_modify({'EMCC_DEBUG': '1'}): output = self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], expected) self.assertContained( 'config file changed since we checked vanilla', output) test_with_fake( 'got js backend! JavaScript (asm.js, emscripten) backend', 'check tells us to use asm.js backend') test_with_fake('got wasm32 backend! WebAssembly 32-bit', 'check tells us to use wasm backend') # use LLVM env var to modify LLVM between vanilla checks assert not os.environ.get( 'EM_LLVM_ROOT'), 'we need to modify EM_LLVM_ROOT env var for this' f = open(CONFIG_FILE, 'a') f.write('LLVM_ROOT = "' + self.in_dir('fake1', 'bin') + '"\n') f.close() safe_ensure_dirs(self.in_dir('fake1', 'bin')) f = open(self.in_dir('fake1', 'bin', 'llc'), 'w') f.write('#!/bin/sh\n') f.write('echo "llc fake1 output\nRegistered Targets:\n%s"' % 'got js backend! JavaScript (asm.js, emscripten) backend') f.close() os.chmod(self.in_dir('fake1', 'bin', 'llc'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) safe_ensure_dirs(self.in_dir('fake2', 'bin')) f = open(self.in_dir('fake2', 'bin', 'llc'), 'w') f.write('#!/bin/sh\n') f.write('echo "llc fake2 output\nRegistered Targets:\n%s"' % 'got wasm32 backend! WebAssembly 32-bit') f.close() os.chmod(self.in_dir('fake2', 'bin', 'llc'), stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC) with env_modify({'EMCC_DEBUG': '1'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'use asm.js backend') with env_modify({'EM_LLVM_ROOT': self.in_dir('fake2', 'bin')}): self.check_working( [EMCC] + MINIMAL_HELLO_WORLD + ['-c'], 'regenerating vanilla check since other llvm') try_delete(CANONICAL_TEMP_DIR) return # TODO: the rest of this # check separate cache dirs are used restore_and_set_up() self.check_working([EMCC], '') root_cache = os.path.expanduser('~/.emscripten_cache') if os.path.exists(os.path.join(root_cache, 'asmjs')): shutil.rmtree(os.path.join(root_cache, 'asmjs')) if os.path.exists(os.path.join(root_cache, 'wasm')): shutil.rmtree(os.path.join(root_cache, 'wasm')) with env_modify({'EMCC_WASM_BACKEND': '1'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD, '') self.assertExists(os.path.join(root_cache, 'wasm')) with env_modify({'EMCC_WASM_BACKEND': '0'}): self.check_working([EMCC] + MINIMAL_HELLO_WORLD, '') self.assertExists(os.path.join(root_cache, 'asmjs')) shutil.rmtree(os.path.join(root_cache, 'asmjs')) self.check_working([EMCC] + MINIMAL_HELLO_WORLD, '') self.assertExists(os.path.join(root_cache, 'asmjs'))
def test_emcc(self): SANITY_FAIL_MESSAGE = 'sanity check failed to run' # emcc should check sanity if no ${EM_CONFIG}_sanity restore_and_set_up() time.sleep(1) assert not os.path.exists( SANITY_FILE) # restore is just the settings, not the sanity output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) # EMCC should have checked sanity successfully old_sanity = open(SANITY_FILE).read() self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc run again should not sanity check, because the sanity file is newer output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # incorrect sanity contents mean we *must* check open(SANITY_FILE, 'w').write('wakawaka') output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) # correct sanity contents mean we need not check open(SANITY_FILE, 'w').write(old_sanity) output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) # but with EMCC_DEBUG=1 we should check with env_modify({'EMCC_DEBUG': '1'}): output = self.check_working(EMCC) try_delete(CANONICAL_TEMP_DIR) self.assertContained(SANITY_MESSAGE, output) output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) # also with -v, with or without inputs output = self.check_working([EMCC, '-v'], SANITY_MESSAGE) output = self.check_working([EMCC, '-v'] + MINIMAL_HELLO_WORLD + [], SANITY_MESSAGE) # Make sure the test runner didn't do anything to the setup output = self.check_working(EMCC) self.assertNotContained(SANITY_MESSAGE, output) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc should also check sanity if the file is outdated open(CONFIG_FILE, 'a').write('# extra stuff\n') output = self.check_working(EMCC) self.assertContained(SANITY_MESSAGE, output) self.assertNotContained(SANITY_FAIL_MESSAGE, output) # emcc should be configurable directly from EM_CONFIG without any config file restore_and_set_up() config = open(CONFIG_FILE, 'r').read() open('main.cpp', 'w').write(''' #include <stdio.h> int main() { printf("hello from emcc with no config file\\n"); return 0; } ''') wipe() with env_modify({'EM_CONFIG': config}): run_process([EMCC, 'main.cpp', '-o', 'a.out.js']) self.assertContained('hello from emcc with no config file', run_js('a.out.js'))
def test_with_fake(report, expected): make_fake(report) with env_modify({'EMCC_DEBUG': '1'}): output = self.check_working([EMCC] + MINIMAL_HELLO_WORLD + ['-c'], expected) self.assertContained('config file changed since we checked vanilla', output)
def check(): print(self.do([PYTHON, EMCC, '--clear-cache'])) print(self.do([PYTHON, EMCC, '--clear-ports'])) with env_modify({'EMCC_WASM_BACKEND': '1'}): self.check_working([EMCC, path_from_root('tests', 'hello_world.c')], '')
def test_firstrun(self): for command in commands: wipe() default_config = config.embedded_config try: temp_bin = tempfile.mkdtemp() def make_new_executable(name): open(os.path.join(temp_bin, name), 'w').close() make_executable(os.path.join(temp_bin, name)) make_new_executable('llvm-dis') make_new_executable('node') with env_modify( {'PATH': temp_bin + os.pathsep + os.environ['PATH']}): output = self.do(command) finally: shutil.rmtree(temp_bin) config_data = open(default_config).read() try_delete(default_config) self.assertContained('Welcome to Emscripten!', output) self.assertContained( 'This is the first time any of the Emscripten tools has been run.', output) self.assertContained( 'A settings file has been copied to %s, at absolute path: %s' % (default_config, default_config), output) self.assertContained( 'It contains our best guesses for the important paths, which are:', output) self.assertContained('LLVM_ROOT', output) self.assertContained('NODE_JS', output) if platform.system() != 'Windows': # os.chmod can't make files executable on Windows self.assertIdentical( temp_bin, re.search("^ *LLVM_ROOT *= (.*)$", output, re.M).group(1)) possible_nodes = [os.path.join(temp_bin, 'node')] if os.path.exists('/usr/bin/nodejs'): possible_nodes.append('/usr/bin/nodejs') self.assertIdentical( possible_nodes, re.search("^ *NODE_JS *= (.*)$", output, re.M).group(1)) self.assertContained( 'Please edit the file if any of those are incorrect', output) self.assertContained( 'This command will now exit. When you are done editing those paths, re-run it.', output) self.assertTrue(output.strip().endswith('=============')) template_file = Path( path_from_root('tools/settings_template.py')).read_text() self.assertNotContained('{{{', config_data) self.assertNotContained('}}}', config_data) self.assertContained('{{{', template_file) self.assertContained('}}}', template_file) for content in [ 'EMSCRIPTEN_ROOT', 'LLVM_ROOT', 'NODE_JS', 'JS_ENGINES' ]: self.assertContained(content, config_data) # The guessed config should be ok # XXX This depends on your local system! it is possible `which` guesses wrong # try_delete('a.out.js') # output = self.run_process([EMCC, test_file('hello_world.c')], stdout=PIPE, stderr=PIPE).output # self.assertContained('hello, world!', self.run_js('a.out.js'), output) # Second run, with bad EM_CONFIG for settings in [ 'blah', 'LLVM_ROOT="blarg"; JS_ENGINES=[]; NODE_JS=[]; SPIDERMONKEY_ENGINE=[]' ]: try: with open(default_config, 'w') as f: f.write(settings) output = self.do(command) if 'blah' in settings: self.assertContained( 'Error in evaluating config file (%s)' % default_config, output) elif 'runner' not in ' '.join(command): self.assertContained( 'error: NODE_JS is set to empty value', output) # sanity check should fail finally: try_delete(default_config)