diff options
-rwxr-xr-x | emscripten.py | 10 | ||||
-rw-r--r-- | tests/runner.py | 65 | ||||
-rw-r--r-- | tests/settings.py | 13 | ||||
-rw-r--r-- | third_party/demangler.py | 9 | ||||
-rwxr-xr-x | tools/emmaken.py | 146 | ||||
-rw-r--r-- | tools/shared.py | 23 |
6 files changed, 216 insertions, 50 deletions
diff --git a/emscripten.py b/emscripten.py index a0351ab7..7a92455c 100755 --- a/emscripten.py +++ b/emscripten.py @@ -2,12 +2,14 @@ import os, sys, subprocess -COMPILER = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'src', 'compiler.js') -CONFIG_FILE = os.path.expanduser('~/.emscripten') JS_ENGINE = None -if os.path.exists(CONFIG_FILE): - exec(open(CONFIG_FILE, 'r').read()) +abspath = os.path.abspath(os.path.dirname(__file__)) +def path_from_root(*pathelems): + return os.path.join(os.path.sep, *(abspath.split(os.sep) + list(pathelems))) +exec(open(path_from_root('tools', 'shared.py'), 'r').read()) + +COMPILER = path_from_root('src', 'compiler.js') def emscripten(filename, settings): data = open(filename, 'r').read() diff --git a/tests/runner.py b/tests/runner.py index 27f93224..86647fbb 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -7,25 +7,25 @@ See settings.py file for options¶ms. Edit as needed. from subprocess import Popen, PIPE, STDOUT import os, unittest, tempfile, shutil, time, inspect, sys, math, glob -# Params +# Setup abspath = os.path.abspath(os.path.dirname(__file__)) -def path_from_root(pathelems): - return os.path.join(os.path.sep, *(abspath.split(os.sep)[:-1] + pathelems)) +def path_from_root(*pathelems): + return os.path.join(os.path.sep, *(abspath.split(os.sep)[:-1] + list(pathelems))) +exec(open(path_from_root('tools', 'shared.py'), 'r').read()) -EMSCRIPTEN = path_from_root(['emscripten.py']) +# Sanity check for config -exec(open(os.path.join(os.path.abspath(os.path.dirname(__file__)), 'settings.py'), 'r').read()) +try: + assert COMPILERS != None +except: + raise Exception('Cannot find "COMPILERS" definition. Is ~/.emscripten set up properly? You may need to copy the template at ~/tests/settings.py into it.') -def timeout_run(proc, timeout, note): - start = time.time() - if timeout is not None: - while time.time() - start < timeout and proc.poll() is None: - time.sleep(0.1) - if proc.poll() is None: - proc.kill() # XXX bug: killing emscripten.py does not kill it's child process! - raise Exception("Timed out: " + note) - return proc.communicate()[0] +# Paths + +EMSCRIPTEN = path_from_root('emscripten.py') +DEMANGLER = path_from_root('third_party', 'demangler.py') +NAMESPACER = path_from_root('tools', 'namespacer.py') class RunnerCore(unittest.TestCase): def get_dir(self): @@ -106,7 +106,7 @@ class RunnerCore(unittest.TestCase): shutil.move(os.path.join(dirname, main_file), filename) # Copy Emscripten C++ API - shutil.copy(path_from_root(['src', 'include', 'emscripten.h']), dirname) + shutil.copy(path_from_root('src', 'include', 'emscripten.h'), dirname) # C++ => LLVM binary try: @@ -1288,25 +1288,25 @@ if 'benchmark' not in sys.argv: def test_fannkuch(self): results = [ (1,0), (2,1), (3,2), (4,4), (5,7), (6,10), (7, 16), (8,22) ] for i, j in results: - src = open(path_from_root(['tests', 'fannkuch.cpp']), 'r').read() + src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read() self.do_test(src, 'Pfannkuchen(%d) = %d.' % (i,j), [str(i)], no_build=i>1) def test_raytrace(self): - src = open(path_from_root(['tests', 'raytrace.cpp']), 'r').read() - output = open(path_from_root(['tests', 'raytrace.ppm']), 'r').read() + src = open(path_from_root('tests', 'raytrace.cpp'), 'r').read() + output = open(path_from_root('tests', 'raytrace.ppm'), 'r').read() self.do_test(src, output, ['3', '16']) def test_dlmalloc(self): # XXX Warning: Running this in SpiderMonkey can lead to an extreme amount of memory being # used, see Mozilla bug 593659. - src = open(path_from_root(['tests', 'dlmalloc.c']), 'r').read() + src = open(path_from_root('tests', 'dlmalloc.c'), 'r').read() self.do_test(src, '*1,0*') def test_fasta(self): results = [ (1,'''GG*ctt**tgagc*'''), (20,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tacgtgtagcctagtgtttgtgttgcgttatagtctatttgtggacacagtatggtcaaa**tgacgtcttttgatctgacggcgttaacaaagatactctg*'''), (50,'''GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA*TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT*cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg**tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa**NtactMcSMtYtcMgRtacttctWBacgaa**agatactctgggcaacacacatacttctctcatgttgtttcttcggacctttcataacct**ttcctggcacatggttagctgcacatcacaggattgtaagggtctagtggttcagtgagc**ggaatatcattcgtcggtggtgttaatctatctcggtgtagcttataaatgcatccgtaa**gaatattatgtttatttgtcggtacgttcatggtagtggtgtcgccgatttagacgtaaa**ggcatgtatg*''') ] for i, j in results: - src = open(path_from_root(['tests', 'fasta.cpp']), 'r').read() + src = open(path_from_root('tests', 'fasta.cpp'), 'r').read() self.do_test(src, j, [str(i)], lambda x: x.replace('\n', '*'), no_build=i>1) def zzztest_gl(self): @@ -1323,7 +1323,7 @@ if 'benchmark' not in sys.argv: };''' ) open(filename, 'w').write(src) - self.do_test(path_from_root(['tests', 'gl']), '*?*', main_file='sdl_ogl.c', post_build=post) + self.do_test(path_from_root('tests', 'gl'), '*?*', main_file='sdl_ogl.c', post_build=post) def test_cubescript(self): # XXX Warning: Running this in SpiderMonkey can lead to an extreme amount of memory being @@ -1333,10 +1333,10 @@ if 'benchmark' not in sys.argv: # Overflows happen in hash loop global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1 - self.do_test(path_from_root(['tests', 'cubescript']), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') + self.do_test(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') def test_gcc_unmangler(self): - self.do_test(path_from_root(['third_party']), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'], main_file='gcc_demangler.c') + self.do_test(path_from_root('third_party'), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'], main_file='gcc_demangler.c') #### Code snippet that is helpful to search for nonportable optimizations #### #global LLVM_OPT_OPTS @@ -1350,13 +1350,13 @@ if 'benchmark' not in sys.argv: def test_bullet(self): global SAFE_HEAP; SAFE_HEAP = 0 # Too slow for that - self.do_ll_test(path_from_root(['tests', 'bullet', 'bulletTest.ll']), open(path_from_root(['tests', 'bullet', 'output.txt']), 'r').read()) + self.do_ll_test(path_from_root('tests', 'bullet', 'bulletTest.ll'), open(path_from_root('tests', 'bullet', 'output.txt'), 'r').read()) def test_lua(self): # Overflows in luaS_newlstr hash loop global CORRECT_OVERFLOWS; CORRECT_OVERFLOWS = 1 - self.do_ll_test(path_from_root(['tests', 'lua', 'lua.ll']), + self.do_ll_test(path_from_root('tests', 'lua', 'lua.ll'), 'hello lua world!\n17.00000000000\n1.00000000000\n2.00000000000\n3.00000000000\n4.00000000000\n7.00000000000', args=['-e', '''print("hello lua world!");print(17);for x = 1,4 do print(x) end;print(10-3)'''], output_nicerizer=lambda string: string.replace('\n\n', '\n').replace('\n\n', '\n')) @@ -1367,7 +1367,7 @@ if 'benchmark' not in sys.argv: global RELOOP; RELOOP = 0 # Too slow; we do care about typed arrays and OPTIMIZE though global SAFE_HEAP; SAFE_HEAP = 0 # Has bitfields which are false positives. Also the PyFloat_Init tries to detect endianness. - self.do_ll_test(path_from_root(['tests', 'python', 'python.ll']), + self.do_ll_test(path_from_root('tests', 'python', 'python.ll'), 'hello python world!\n\n[0, 2, 4, 6]\n\n5\n\n22\n\n5.470', args=['-S', '-c' '''print "hello python world!"; print [x*2 for x in range(4)]; t=2; print 10-3-t; print (lambda x: x*2)(11); print '%f' % 5.47'''], js_engines=[V8_ENGINE]) # script stack space exceeded in SpiderMonkey, TODO @@ -1376,15 +1376,15 @@ if 'benchmark' not in sys.argv: def test_cases(self): if LLVM_OPTS: return # Our code is not exactly 'normal' llvm assembly - for name in glob.glob(path_from_root(['tests', 'cases', '*.ll'])): + for name in glob.glob(path_from_root('tests', 'cases', '*.ll')): shortname = name.replace('.ll', '') print "Testing case '%s'..." % shortname - output_file = path_from_root(['tests', 'cases', shortname + '.txt']) + output_file = path_from_root('tests', 'cases', shortname + '.txt') if os.path.exists(output_file): output = open(output_file, 'r').read() else: output = 'hello, world!' - self.do_ll_test(path_from_root(['tests', 'cases', name]), output) + self.do_ll_test(path_from_root('tests', 'cases', name), output) ### Integration tests @@ -1417,7 +1417,6 @@ if 'benchmark' not in sys.argv: 'Module["_"] = ' + open(filename + '.tmp2', 'r').read().rstrip() + ';\n\n' + script_src + '\n\n' + '// {{MODULE_ADDITIONS}' ) - open(filename, 'w').write(src) self.do_test(src, '*166*\n*ok*', post_build=post) @@ -1598,12 +1597,12 @@ else: self.do_benchmark(src, [], 'lastprime: 348949.') def test_fannkuch(self): - src = open(path_from_root(['tests', 'fannkuch.cpp']), 'r').read() + src = open(path_from_root('tests', 'fannkuch.cpp'), 'r').read() self.do_benchmark(src, ['9'], 'Pfannkuchen(9) = 30.') def test_raytrace(self): - src = open(path_from_root(['tests', 'raytrace.cpp']), 'r').read() - self.do_benchmark(src, ['5', '64'], open(path_from_root(['tests', 'raytrace_5_64.ppm']), 'r').read()) + src = open(path_from_root('tests', 'raytrace.cpp'), 'r').read() + self.do_benchmark(src, ['5', '64'], open(path_from_root('tests', 'raytrace_5_64.ppm'), 'r').read()) if __name__ == '__main__': sys.argv = [sys.argv[0]] + ['-v'] + sys.argv[1:] # Verbose output by default diff --git a/tests/settings.py b/tests/settings.py index a52d6c6f..c2607a49 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,10 +1,3 @@ -# XXX: Aside from these settings, you should create ~/.emscripten, and fill it with -# something like this: -# -# JS_ENGINE=[os.path.expanduser('~/Dev/tracemonkey/js/src/js'), '-m'] -# JS_ENGINE_PARAMS=[] -# - TEMP_DIR='/dev/shm' LLVM_ROOT=os.path.expanduser('~/Dev/llvm-2.8/cbuild/Release/bin') # Might not need 'Release' @@ -43,6 +36,10 @@ V8_ENGINE = [os.path.expanduser('~/Dev/v8/d8')] #COMPILER_ENGINE=SPIDERMONKEY_ENGINE COMPILER_ENGINE=V8_ENGINE +JS_ENGINE=V8_ENGINE +JS_ENGINE_PARAMS = ['--'] # For V8 +JS_ENGINE_PARAMS = [] # For SpiderMonkey + OUTPUT_TO_SCREEN = 0 # useful for debugging specific tests, or for subjectively seeing what parts are slow TIMEOUT = None @@ -50,6 +47,4 @@ TIMEOUT = None # Tools CLOSURE_COMPILER = os.path.expanduser('~/Dev/closure-compiler/compiler.jar') -DEMANGLER = path_from_root(['third_party', 'demangler.py']) -NAMESPACER = path_from_root(['tools', 'namespacer.py']) diff --git a/third_party/demangler.py b/third_party/demangler.py index b9b4b1ef..f3407825 100644 --- a/third_party/demangler.py +++ b/third_party/demangler.py @@ -21,8 +21,10 @@ JS_ENGINE_PARAMS=[] import os, sys, subprocess -CONFIG_FILE = os.path.expanduser('~/.emscripten') -exec(open(CONFIG_FILE, 'r').read()) +abspath = os.path.abspath(os.path.dirname(__file__)) +def path_from_root(*pathelems): + return os.path.join(os.path.sep, *(abspath.split(os.sep)[:-1] + list(pathelems))) +exec(open(path_from_root('tools', 'shared.py'), 'r').read()) data = open(sys.argv[1], 'r').readlines() splitter = sys.argv[2] @@ -35,8 +37,7 @@ for line in data: func = line.lstrip().split(splitter)[0] if func in SEEN: continue SEEN[func] = True - args = JS_ENGINE + [os.path.join(os.path.dirname(__file__), 'gcc_demangler.js')] + JS_ENGINE_PARAMS + [func[1:]] - cleaned = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).communicate()[0] + cleaned = run_js(JS_ENGINE, path_from_root('third_party', 'gcc_demangler.js'), [func[1:]]) if cleaned is None: continue if 'Fatal exception' in cleaned: continue cleaned = cleaned[1:-2] diff --git a/tools/emmaken.py b/tools/emmaken.py new file mode 100755 index 00000000..1b29f7a9 --- /dev/null +++ b/tools/emmaken.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python + +''' +emmaken - the emscripten make proxy tool +======================================== + +Tell your build system to use this instead of the compiler, linker, ar and +ranlib. All the normal build commands will be sent to this script, which +will proxy them to the appropriate LLVM build commands, in order to +generate proper code for Emscripten to later process. + +For example, compilation will be translated into calls to llvm-gcc +with -emit-llvm, and linking will be translated into calls to llvm-link, +and so forth. + +Example uses: + + * With configure, do something like + + RANLIB=PATH/emmaken.py AR=PATH/emmaken.py CXX=PATH/emmaken.py CC=PATH/emmaken.py ./configure [options] + + where PATH is the path to this file. + + * With CMake, do something like + + SET(CMAKE_C_COMPILER "PATH/emmaken.py") + SET(CMAKE_CXX_COMPILER "PATH/emmaken.py") + SET(CMAKE_LINKER "PATH/emmaken.py") + SET(CMAKE_CXX_LINKER "PATH/emmaken.py") + SET(CMAKE_C_LINK_EXECUTABLE "PATH/emmaken.py") + SET(CMAKE_CXX_LINK_EXECUTABLE "PATH/emmaken.py") + SET(CMAKE_AR "PATH/emmaken.py") + SET(CMAKE_RANLIB "PATH/emmaken.py") + +After setting that up, run your build system normally. It should generate +LLVM instead of the normal output, and end up with .ll files that you can +give to Emscripten. Note that this tool doesn't run Emscripten itself. Note +also that you may need to do some manual fiddling later, for example to +link files that weren't linked, and them llvm-dis them. +''' + +import sys +import os +import subprocess + +abspath = os.path.abspath(os.path.dirname(__file__)) +def path_from_root(*pathelems): + return os.path.join(os.path.sep, *(abspath.split(os.sep)[:-1] + list(pathelems))) +exec(open(path_from_root('tools', 'shared.py'), 'r').read()) + +CONFIG_FILE = os.path.expanduser('~/.emscripten') +assert os.path.exists(CONFIG_FILE) +exec(open(CONFIG_FILE, 'r').read()) + +try: + print >> sys.stderr, 'emmaken.py: ', ' '.join(sys.argv) + + CC='/home/alon/Dev/llvm-gcc-4.2-2.8.source/cbuild/install/bin/llvm-g++' + CC_ARG_SKIP = ['-g', '-O1', '-O2', '-O3'] + CC_ADDITIONAL_ARGS = ['-m32', '-U__i386__', '-U__x86_64__', '-UX87_DOUBLE_ROUNDING', '-UHAVE_GCC_ASM_FOR_X87'] + + #CC='llvm-gcc' + #CC_ARG_SKIP = ['-g', '-O1', '-O2', '-O3'] + #CC_ADDITIONAL_ARGS = ['-U__i386__', '-U__x86_64__'] + + LLVM_LINK = '/home/alon/Dev/llvm-2.8/cbuild/Release/bin/llvm-link' + LLVM_DIS = '/home/alon/Dev/llvm-2.8/cbuild/Release/bin/llvm-dis' + ALLOWED_LINK_ARGS = ['-f', '-help', '-o', '-print-after', '-print-after-all', '-print-before', + '-print-before-all', '-time-passes', '-v', '-verify-dom-info', '-version' ] + #LINK_ARG_SKIP = ['-pthread', '-DNDEBUG', '-g', '-O3', '-Wall', '-Wstrict-prototypes', + # '-lpthread', '-ldl', '-lutil', '-Xlinker', '-export-dynamic', '-lm', '-shared'] + + # ---------------- End configs ------------- + + if len(sys.argv) == 2 and 'conftest' not in ' '.join(sys.argv): # Avoid messing with configure, see below too + # ranlib + os.execvp(LLVM_DIS, ['-show-annotations', sys.argv[1]]) + sys.exit(0) + if sys.argv[1] in ['x', 't']: + # noop ar + sys.exit(0) + + use_linker = True + #use_linker = False + + opts = [] + files = [] + for arg in sys.argv[1:]: + if arg.startswith('-'): + opts.append(arg) + else: + files.append(arg) + if arg.endswith(('.c', '.cc', '.cpp')): + use_linker = False + + if '--version' in opts: + use_linker = False + + if sys.argv[1] == 'cru': # ar + sys.argv = sys.argv[:1] + sys.argv[3:] + ['-o='+sys.argv[2]] + assert use_linker, 'Linker should be used in this case' + + if use_linker: + call = LLVM_LINK + newargs = [] + found_o = False + for arg in sys.argv[1:]: + if os.path.basename(sys.argv[0])=='arproxy.py': + if arg.endswith('.a'): + newargs.append('-o=%s' % arg) + elif arg.endswith('.o'): + newargs.append(arg) + else: + pass + continue + if found_o: + newargs.append('-o=%s' % arg) + found_o = False + continue + if arg.startswith('-'): + if arg == '-o': + found_o = True + continue + prefix = arg.split('=')[0] + if prefix in ALLOWED_LINK_ARGS: + newargs.append(arg) + elif arg.endswith('.so'): + continue # .so's do not exist yet, in many cases + else: + # not option, so just append + newargs.append(arg) + else: + call = CC + newargs = [ arg for arg in sys.argv[1:] if arg not in CC_ARG_SKIP ] + CC_ADDITIONAL_ARGS + if 'conftest.c' not in files: + newargs.append('-emit-llvm') + if CC=='llvm-gcc': + newargs.append('-c') + + print >> sys.stderr, "Running:", call, ' '.join(newargs) + + os.execvp(call, [call] + newargs) +except: + print 'Error in emmaken.py. Is the config file ~/.emscripten set up properly?' + raise + diff --git a/tools/shared.py b/tools/shared.py new file mode 100644 index 00000000..f6594af2 --- /dev/null +++ b/tools/shared.py @@ -0,0 +1,23 @@ + +import shutil, time +from subprocess import Popen, PIPE, STDOUT + +CONFIG_FILE = os.path.expanduser('~/.emscripten') +if not os.path.exists(CONFIG_FILE): + shutil.copy(path_from_root('tests', 'settings.py'), CONFIG_FILE) +exec(open(CONFIG_FILE, 'r').read()) + +def timeout_run(proc, timeout, note): + start = time.time() + if timeout is not None: + while time.time() - start < timeout and proc.poll() is None: + time.sleep(0.1) + if proc.poll() is None: + proc.kill() # XXX bug: killing emscripten.py does not kill it's child process! + raise Exception("Timed out: " + note) + return proc.communicate()[0] + +def run_js(engine, filename, args, check_timeout=False): + return timeout_run(Popen(engine + [filename] + (['--'] if 'v8' in engine[0] else []) + args, + stdout=PIPE, stderr=STDOUT), 120 if check_timeout else None, 'Execution') + |