diff options
-rw-r--r-- | README.markdown | 2 | ||||
-rwxr-xr-x | emcc | 5 | ||||
-rw-r--r-- | tests/runner.py | 139 | ||||
-rw-r--r-- | tools/shared.py | 71 |
4 files changed, 206 insertions, 11 deletions
diff --git a/README.markdown b/README.markdown index eeb50156..dadbf3a4 100644 --- a/README.markdown +++ b/README.markdown @@ -4,7 +4,7 @@ Emscripten Emscripten is an LLVM-to-JavaScript compiler. It takes LLVM bitcode - which can be generated from C/C++, using llvm-gcc or clang, or any other language that can be converted into LLVM - and compiles that into JavaScript, which can be run on the web (or anywhere else JavaScript can run). -Links to **demos**, **FAQ**, etc: <https://github.com/kripken/emscripten/wiki> +Links to **demos**, **tutorial**, **FAQ**, etc: <https://github.com/kripken/emscripten/wiki> Main project page: <http://emscripten.org> @@ -82,6 +82,8 @@ TEMP_DIR = os.environ.get('EMCC_TEMP_DIR') if DEBUG: print >> sys.stderr, 'emcc: ', ' '.join(sys.argv) +shared.check_sanity() + # Handle some global flags if len(sys.argv) == 1: @@ -281,6 +283,9 @@ try: if llvm_opt_level is None: llvm_opt_level = 1 if opt_level >= 1 else 0 if closure is None: closure = 1 if opt_level >= 2 else 0 + if closure: + assert os.path.exists(shared.CLOSURE_COMPILER), 'emcc: fatal: Closure compiler (%s) does not exist' % CLOSURE_COMPILER + settings_changes = [] for i in range(len(newargs)): if newargs[i] == '-s': diff --git a/tests/runner.py b/tests/runner.py index 6d818b4d..f4cfe312 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -217,7 +217,7 @@ class RunnerCore(unittest.TestCase): sys.argv = map(lambda arg: arg if not arg.startswith('test_') else 'default.' + arg, sys.argv) -if 'benchmark' not in str(sys.argv): +if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): # Tests print "Running Emscripten tests..." @@ -5114,7 +5114,7 @@ Options that are modified or new in %s include: stdin=PIPE, stdout=PIPE).communicate(input)[0] self.assertIdentical(expected, output.replace('\n\n', '\n')) -else: +elif 'benchmark' in str(sys.argv): # Benchmarks. Run them with argument |benchmark|. To run a specific test, do # |benchmark.test_X|. @@ -5150,7 +5150,7 @@ else: JS_ENGINE = JS_ENGINES[0] for i in range(1, len(sys.argv)): arg = sys.argv[i] - if not arg.startswith('test_'): + if not arg.startswith('benchmark.test_'): JS_ENGINE = eval(arg) sys.argv[i] = None sys.argv = filter(lambda arg: arg is not None, sys.argv) @@ -5206,9 +5206,11 @@ else: f.close() final_filename = os.path.join(dirname, 'src.js') + try_delete(final_filename) output = Popen([EMCC, filename, '-O3', '-s', 'USE_TYPED_ARRAYS=1', '-s', 'QUANTUM_SIZE=1', '-s', 'TOTAL_MEMORY=100*1024*1024', '-s', 'FAST_MEMORY=10*1024*1024', '-o', final_filename] + emcc_args, stdout=PIPE, stderr=PIPE).communicate() + assert os.path.exists(final_filename), 'Failed to compile file: ' + '\n'.join(output) # Run JS global total_times, tests_done @@ -5330,14 +5332,137 @@ else: src = open(path_from_root('src', 'dlmalloc.c'), 'r').read() + '\n\n\n' + open(path_from_root('tests', 'dlmalloc_test.c'), 'r').read() self.do_benchmark(src, ['400', '400'], '*400,0*', emcc_args=['-g', '-s', 'CORRECT_SIGNS=2', '-s', 'CORRECT_SIGNS_LINES=[4820, 4195, 4250, 4203, 4209, 4239, 4231]']) +elif 'sanity' in str(sys.argv): + + # Run some sanity checks on the test runner and emcc. + + sys.argv = filter(lambda x: x != 'sanity', sys.argv) + + print + print 'Running sanity checks.' + print 'WARNING: This will modify ~/.emscripten, and in theory can break it although it should be restored properly. A backup will be saved in ~/.emscripten_backup' + print + + assert os.path.exists(CONFIG_FILE), 'To run these tests, we need a (working!) ~/.emscripten file to already exist' + + shutil.copyfile(CONFIG_FILE, CONFIG_FILE + '_backup') + def restore(): + shutil.copyfile(CONFIG_FILE + '_backup', CONFIG_FILE) + + SANITY_FILE = CONFIG_FILE + '_sanity' + + def wipe(): + try_delete(CONFIG_FILE) + try_delete(SANITY_FILE) + + commands = [[EMCC], ['python', path_from_root('tests', 'runner.py'), 'blahblah']] + + class sanity(RunnerCore): + def setUp(self): + wipe() + + def tearDown(self): + restore() + + def do(self, command): + if type(command) is not list: + command = [command] + + return Popen(command, stdout=PIPE, stderr=STDOUT).communicate()[0] + + def check_working(self, command): + if type(command) is not list: + command = [command] + + output = self.do(command) + if command[0] == EMCC: + self.assertContained('no input files', output) + else: + self.assertContained("has no attribute 'blahblah'", output) + return output + + def test_aaa_normal(self): # this should be the very first thing that runs. if this fails, everything else is irrelevant! + for command in commands: + # Your existing ~/.emscripten should work! + restore() + self.check_working(command) + + def test_firstrun(self): + for command in commands: + wipe() + output = self.do(command) + + 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 ~/.emscripten, at absolute path: %s' % CONFIG_FILE, output) + self.assertContained('Please edit that file and change the paths to fit your system', output) + self.assertContained('make sure LLVM_ROOT and NODE_JS are correct', output) + self.assertContained('This command will now exit. When you are done editing those paths, re-run it.', output) + assert output.replace('\n', '').endswith('===='), 'We should have stopped: ' + output + assert (open(CONFIG_FILE).read() == open(path_from_root('settings.py')).read()), 'Settings should be copied from settings.py' + + # Second run, with bad ~/.emscripten + for settings in ['blah', 'LLVM_ROOT="blah"; JS_ENGINES=[]; COMPILER_ENGINE=NODE_JS=SPIDERMONKEY_ENGINE=[]']: + f = open(CONFIG_FILE, 'w') + f.write(settings) + f.close() + output = self.do(command) + + if 'LLVM_ROOT' not in settings: + self.assertContained('Error in evaluating ~/.emscripten', output) + else: + self.assertContained('FATAL', output) # sanity check should fail + + def test_emcc(self): + def mtime(filename): + return os.stat(filename).st_mtime + + SANITY_MESSAGE = 'Emscripten: Running sanity checks' + SANITY_FAIL_MESSAGE = 'sanity check failed to run' + + # emcc should check sanity if no ~/.emscripten_sanity + restore() + time.sleep(0.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) + 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) + + # But the test runner should + output = self.check_working(commands[1]) + self.assertContained(SANITY_MESSAGE, output) + self.assertNotContained(SANITY_FAIL_MESSAGE, output) + + # 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() + 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) + +else: + raise Exception('Test runner is confused: ' + str(sys.argv)) if __name__ == '__main__': sys.argv = [sys.argv[0]] + ['-v'] + sys.argv[1:] # Verbose output by default # Sanity checks - if not check_engine(COMPILER_ENGINE): - print 'WARNING: The JavaScript shell used for compiling does not seem to work' + check_sanity(force=True) total_engines = len(JS_ENGINES) JS_ENGINES = filter(check_engine, JS_ENGINES) @@ -5346,10 +5471,6 @@ if __name__ == '__main__': elif len(JS_ENGINES) < total_engines: print 'WARNING: Not all the JS engines in JS_ENGINES appears to work, ignoring those.' - for cmd in [CLANG, LLVM_DIS]: - if not os.path.exists(cmd) and not os.path.exists(cmd + '.exe'): # .exe extension required for Windows - print 'WARNING: Cannot find', cmd - # Go unittest.main() diff --git a/tools/shared.py b/tools/shared.py index f109d9d2..1366f6fd 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -5,10 +5,79 @@ __rootpath__ = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) def path_from_root(*pathelems): return os.path.join(__rootpath__, *pathelems) +# Config file + CONFIG_FILE = os.path.expanduser('~/.emscripten') if not os.path.exists(CONFIG_FILE): shutil.copy(path_from_root('settings.py'), CONFIG_FILE) -exec(open(CONFIG_FILE, 'r').read()) + print >> sys.stderr, ''' +============================================================================== +Welcome to Emscripten! + +This is the first time any of the Emscripten tools has been run. + +A settings file has been copied to ~/.emscripten, at absolute path: %s + +Please edit that file and change the paths to fit your system. Specifically, +make sure LLVM_ROOT and NODE_JS are correct. + +This command will now exit. When you are done editing those paths, re-run it. +============================================================================== +''' % CONFIG_FILE + sys.exit(0) +try: + exec(open(CONFIG_FILE, 'r').read()) +except Exception, e: + print >> sys.stderr, 'Error in evaluating ~/.emscripten (at %s): %s' % (CONFIG_FILE, str(e)) + sys.exit(1) + +# Check that basic stuff we need (a JS engine to compile, Node.js, and Clang and LLVM) +# exists. +# The test runner always does this check (through |force|). emcc does this less frequently, +# only when ~/.emscripten_sanity does not exist or is older than ~/.emscripten (so, +# we re-check sanity when the settings are changed) +def check_sanity(force=False): + try: + if not force: + settings_mtime = os.stat(CONFIG_FILE).st_mtime + sanity_file = CONFIG_FILE + '_sanity' + try: + sanity_mtime = os.stat(sanity_file).st_mtime + if sanity_mtime > settings_mtime: + return # sanity has been checked + except: + pass + + print >> sys.stderr, '(Emscripten: Running sanity checks)' + + if not check_engine(COMPILER_ENGINE): + print >> sys.stderr, 'FATAL: The JavaScript shell used for compiling (%s) does not seem to work, check the paths in ~/.emscripten' % COMPILER_ENGINE + sys.exit(0) + + if NODE_JS != COMPILER_ENGINE: + if not check_engine(NODE_JS): + print >> sys.stderr, 'FATAL: Node.js (%s) does not seem to work, check the paths in ~/.emscripten' % NODE_JS + sys.exit(0) + + for cmd in [CLANG, LLVM_DIS]: + if not os.path.exists(cmd) and not os.path.exists(cmd + '.exe'): # .exe extension required for Windows + print >> sys.stderr, 'FATAL: Cannot find %s, check the paths in ~/.emscripten' % cmd + sys.exit(0) + + if not os.path.exists(CLOSURE_COMPILER): + print >> sys.stderr, 'WARNING: Closure compiler (%s) does not exist, check the paths in ~/.emscripten. -O2 and above will fail' % CLOSURE_COMPILER + + # Sanity check passed! + + if not force: + # Only create/update this file if the sanity check succeeded, i.e., we got here + f = open(sanity_file, 'w') + f.write('certified\n') + f.close() + + except Exception, e: + # Any error here is not worth failing on + print 'WARNING: sanity check failed to run', e # Tools/paths |