diff --git a/ChangeLog.markdown b/ChangeLog.markdown index 07a830fe316c6..e6d4620d8fde2 100644 --- a/ChangeLog.markdown +++ b/ChangeLog.markdown @@ -9,6 +9,7 @@ Not all changes are documented here. In particular, new features, user-oriented Current Trunk ------------- + - Breaking change: Change `NO_EXIT_RUNTIME` to 1 by default. This means that by default we don't include code to shut down the runtime, flush stdio streams, run atexits, etc., which is better for code size. When `ASSERTIONS` is on, we warn at runtime if there is text buffered in the streams that should be flushed, or atexits are used. - Meta-DCE for JS+wasm: remove unused code between JS+wasm more aggressively. This should not break valid code, but may break code that depended on unused code being kept around (like using a function from outside the emitted JS without exporting it - only exported things are guaranteed to be kept alive through optimization). v1.37.24: 12/13/2017 diff --git a/emcc.py b/emcc.py index 768bddda3d225..769741acb5f71 100755 --- a/emcc.py +++ b/emcc.py @@ -479,7 +479,9 @@ def filter_emscripten_options(argv): # The preprocessor define EMSCRIPTEN is deprecated. Don't pass it to code in strict mode. Code should use the define __EMSCRIPTEN__ instead. if not shared.Settings.STRICT: cmd += ['-DEMSCRIPTEN'] - if use_js: cmd += ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1'] # configure tests should fail when an undefined symbol exists + if use_js: + cmd += ['-s', 'ERROR_ON_UNDEFINED_SYMBOLS=1'] # configure tests should fail when an undefined symbol exists + cmd += ['-s', 'NO_EXIT_RUNTIME=0'] # configure tests want a more shell-like style, where we emit return codes on exit() logging.debug('just configuring: ' + ' '.join(cmd)) if debug_configure: open(tempout, 'a').write('emcc, just configuring: ' + ' '.join(cmd) + '\n\n') @@ -663,6 +665,8 @@ def detect_fixed_language_mode(args): if options.emrun: options.pre_js += open(shared.path_from_root('src', 'emrun_prejs.js')).read() + '\n' options.post_js += open(shared.path_from_root('src', 'emrun_postjs.js')).read() + '\n' + # emrun mode waits on program exit + shared.Settings.NO_EXIT_RUNTIME = 0 if options.cpu_profiler: options.post_js += open(shared.path_from_root('src', 'cpuprofiler.js')).read() + '\n' @@ -1072,9 +1076,12 @@ def check(input_file): if not shared.Settings.NO_FILESYSTEM and not shared.Settings.ONLY_MY_CODE: shared.Settings.EXPORTED_FUNCTIONS += ['___errno_location'] # so FS can report errno back to C - if not shared.Settings.NO_EXIT_RUNTIME: - shared.Settings.EXPORTED_FUNCTIONS += ['_fflush'] # to flush the streams on FS quit - # TODO this forces 4 syscalls, maybe we should avoid it? + # to flush streams on FS exit, we need to be able to call fflush + # we only include it if the runtime is exitable, or when ASSERTIONS + # (ASSERTIONS will check that streams do not need to be flushed, + # helping people see when they should have disabled NO_EXIT_RUNTIME) + if not shared.Settings.NO_EXIT_RUNTIME or shared.Settings.ASSERTIONS: + shared.Settings.EXPORTED_FUNCTIONS += ['_fflush'] if shared.Settings.USE_PTHREADS: if not any(s.startswith('PTHREAD_POOL_SIZE=') for s in settings_changes): @@ -1209,10 +1216,6 @@ def check(input_file): if 'interpret' in shared.Settings.BINARYEN_METHOD: logging.warning('disabling EVAL_CTORS as the bundled interpreter confuses the ctor tool') shared.Settings.EVAL_CTORS = 0 - else: - # for wasm, we really want no-exit-runtime, so that atexits don't stop us - if final_suffix in JS_CONTAINING_SUFFIXES and not shared.Settings.NO_EXIT_RUNTIME: - logging.warning('you should enable -s NO_EXIT_RUNTIME=1 so that EVAL_CTORS can work at full efficiency (it gets rid of atexit calls which might disrupt EVAL_CTORS)') # memory growth does not work in dynamic linking, except for wasm if not shared.Settings.WASM and (shared.Settings.MAIN_MODULE or shared.Settings.SIDE_MODULE): diff --git a/site/source/docs/api_reference/emscripten.h.rst b/site/source/docs/api_reference/emscripten.h.rst index cfc1df0074f8b..5337729ec7539 100644 --- a/site/source/docs/api_reference/emscripten.h.rst +++ b/site/source/docs/api_reference/emscripten.h.rst @@ -334,6 +334,8 @@ Functions The difference is that ``emscripten_force_exit`` will shut down the runtime even if you previously called :c:func:`emscripten_exit_with_live_runtime` or otherwise kept the runtime alive. In other words, this method gives you the option to completely shut down the runtime after it was kept alive beyond the completion of ``main()``. + Note that if ``NO_EXIT_RUNTIME`` is set (which it is by default) then the runtime cannot be shut down, as we do not include the code to do so. Build with ``-s NO_EXIT_RUNTIME=0`` if you want to be able to exit the runtime. + :param int status: The same as for the *libc* function `exit() `_. .. c:function:: double emscripten_get_device_pixel_ratio(void) diff --git a/site/source/docs/getting_started/FAQ.rst b/site/source/docs/getting_started/FAQ.rst index 1552175ef1e2b..62f42f3f51b0b 100644 --- a/site/source/docs/getting_started/FAQ.rst +++ b/site/source/docs/getting_started/FAQ.rst @@ -230,7 +230,7 @@ Another option is to define an ``onRuntimeInitialized`` function, Module['onRuntimeInitialized'] = function() { ... }; -That method will be called when the runtime is ready and it is ok for you to call compiled code. In practice, that is exactly the same time at which ``main()`` would be called, so ``onRuntimeInitialized`` doesn't let you do anything new, but it can be convenient in some cases - for example, if you use ``onRuntimeInitialized`` and don't define a ``main()`` function, then the runtime will not be shut down after ``main()`` exits, and you can keep calling compiled methods (you can also have a ``main()`` and build with ``-s NO_EXIT_RUNTIME=1`` to keep the runtime from being shut down). Thus, for libraries, ``onRuntimeInitialized`` can be convenient. +That method will be called when the runtime is ready and it is ok for you to call compiled code. In practice, that is exactly the same time at which ``main()`` would be called, so ``onRuntimeInitialized`` doesn't let you do anything new, but you can set it from JavaScript at runtime in a flexible way. Here is an example of how to use it: @@ -248,6 +248,27 @@ Here is an example of how to use it: The crucial thing is that ``Module`` exists, and has the property ``onRuntimeInitialized``, before the script containing emscripten output (``my_project.js`` in this example) is loaded. +.. _faq-NO_EXIT_RUNTIME: + +What does "exiting the runtime" mean? Why don't ``atexit()s`` run? +================================================================== + +(You may need this answer if you see an error saying something like ``atexit() called, but NO_EXIT_RUNTIME`` or ``stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0``.) + +By default Emscripten sets ``NO_EXIT_RUNTIME=1``, which means that we don't include code to shut down the runtime. That means that when ``main()`` exits, we don't flush the stdio streams, or call the destructors of global C++ objects, or call ``atexit`` callbacks. This lets us emit smaller code by default, and is normally what you want on the web: even though ``main()`` exited, you may have something asynchronous happening later that you want to execute. + +In some cases, though, you may want a more "commandline" experience, where we do shut down the runtime when ``main()`` exits. You can build with ``-s NO_EXIT_RUNTIME=0``, and then we will call ``atexits`` and so forth. When you build with ``ASSERTIONS``, you should get a warning when you need this. For example, if your program prints something without a newline, + +:: + + #include + + int main() { + printf("hello"); // note no newline + } + +If we don't shut down the runtime and flush the stdio streams, "hello" won't be printed. In an ``ASSERTIONS`` build you'll get a notification saying ``stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0``. + .. _faq-dead-code-elimination: Why do functions in my C/C++ source code vanish when I compile to JavaScript, and/or I get ``No functions to process``? @@ -324,6 +345,14 @@ Emscripten by default does *not* give fatal errors on undefined symbols, so you Aside from just forgetting to link in a necessary object file, one possible cause for this error is inline functions in headers. If you have a header with ``inline int my_func() { .. }`` then *Clang* may not actually inline the function (since inline is just a hint), and also not generate code for it (since it's in a header). The result is that the generated bitcode and JavaScript will not have that function implemented. One solution is to add ``static`` to the function declaration, which forces code to be generated in the object file: ``static inline int my_func() { .. }``. +Why do I get ``TypeError: Module.someThing is not a function``? +=============================================================== + +The ``Module`` object will contain exported methods. For something to appear there, you should add it to ``EXPORTED_FUNCTIONS`` for compiled code, or ``EXTRA_EXPORTED_RUNTIME_METHODS`` for a runtime method (like ``getValue``). + +.. note:: You can use runtime methods directly, without exporting them, if the compiler can see them used. For example, you can use ``getValue`` in ``EM_ASM`` code, or a ``--pre-js``, by calling it directly. The optimizer will not remove that JS runtime method because it sees it is used. You only need to use ``Module.getValue`` if you want to call that method from outside the JS code the compiler can see, and then you need to export it. + +.. note:: Emscripten used to export many runtime methods by default. This increased code size, and for that reason we've changed that default. If you depend on something that used to be exported, you should see a warning pointing you to the solution, in an unoptimized build, or a build with ``ASSERTIONS`` enabled, which we hope will minimize any annoyance. See ``Changelog.markdown`` for details. Why do I get an odd python error complaining about libcxx.bc or libcxxabi.bc? ============================================================================= diff --git a/site/source/docs/optimizing/Optimizing-Code.rst b/site/source/docs/optimizing/Optimizing-Code.rst index be31b2e41841d..9f58b738f470f 100644 --- a/site/source/docs/optimizing/Optimizing-Code.rst +++ b/site/source/docs/optimizing/Optimizing-Code.rst @@ -38,18 +38,6 @@ Advanced compiler settings There are several flags you can :ref:`pass to the compiler ` to affect code generation, which will also affect performance — for example :ref:`DISABLE_EXCEPTION_CATCHING `. These are documented in `src/settings.js `_. Some of these will be directly affected by the optimization settings (you can find out which ones by searching for ``apply_opt_level`` in `tools/shared.py `_). -A few useful flags are: - -- - .. _optimizing-code-no-exit-runtime: - - ``NO_EXIT_RUNTIME``: Building with ``-s NO_EXIT_RUNTIME=1`` lets the compiler know that you don't want to shut down the runtime environment after the ``main()`` function finishes. This allows it to discard the ``atexit`` and global destructor calls it would otherwise make, improving code size and startup speed. - - This is useful if your ``main()`` function finishes but you still want to execute code, for example in an app that uses a :ref:`main loop function `. - - .. note:: Emscripten will not shut down the runtime if it detects :c:func:`emscripten_set_main_loop`, but it is better to optimise away the unnecessary code. - - Code size ========= diff --git a/site/source/docs/tools_reference/emcc.rst b/site/source/docs/tools_reference/emcc.rst index ae3f8bcaece94..cc29232f88e2f 100644 --- a/site/source/docs/tools_reference/emcc.rst +++ b/site/source/docs/tools_reference/emcc.rst @@ -405,7 +405,7 @@ Options that are modified or new in *emcc* are listed below: .. _emcc-emrun: ``--emrun`` - Enables the generated output to be aware of the :ref:`emrun ` command line tool. This allows ``stdout``, ``stderr`` and ``exit(returncode)`` capture when running the generated application through *emrun*. + Enables the generated output to be aware of the :ref:`emrun ` command line tool. This allows ``stdout``, ``stderr`` and ``exit(returncode)`` capture when running the generated application through *emrun*. (This enables `NO_EXIT_RUNTIME=0`, allowing normal runtime exiting with return code passing.) ``--cpuprofiler`` Embeds a simple CPU profiler onto the generated page. Use this to perform cursory interactive performance profiling. diff --git a/src/library.js b/src/library.js index 6ab358296d4d5..d6de1ed5f0e42 100644 --- a/src/library.js +++ b/src/library.js @@ -596,6 +596,11 @@ LibraryManager.library = { atexit__proxy: 'sync', atexit__sig: 'ii', atexit: function(func, arg) { +#if ASSERTIONS +#if NO_EXIT_RUNTIME == 1 + Runtime.warnOnce('atexit() called, but NO_EXIT_RUNTIME is set, so atexits() will not be called. set NO_EXIT_RUNTIME to 0 (see the FAQ)'); +#endif +#endif __ATEXIT__.unshift({ func: func, arg: arg }); }, __cxa_atexit: 'atexit', diff --git a/src/library_browser.js b/src/library_browser.js index dbca0a916df3f..298982a9bbe5c 100644 --- a/src/library_browser.js +++ b/src/library_browser.js @@ -1270,6 +1270,11 @@ var LibraryBrowser = { emscripten_force_exit__proxy: 'sync', emscripten_force_exit__sig: 'vi', emscripten_force_exit: function(status) { +#if NO_EXIT_RUNTIME +#if ASSERTIONS + Runtime.warnOnce('emscripten_force_exit cannot actually shut down the runtime, as the build has NO_EXIT_RUNTIME set'); +#endif +#endif Module['noExitRuntime'] = false; Module['exit'](status); }, diff --git a/src/library_fs.js b/src/library_fs.js index 556abef51de30..c70bec244c407 100644 --- a/src/library_fs.js +++ b/src/library_fs.js @@ -1921,3 +1921,7 @@ mergeInto(LibraryManager.library, { } }); +if (FORCE_FILESYSTEM) { + DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.push('$FS'); +} + diff --git a/src/library_syscall.js b/src/library_syscall.js index 2787b6f08f19c..26f20f6530f6d 100644 --- a/src/library_syscall.js +++ b/src/library_syscall.js @@ -779,7 +779,20 @@ var SyscallsLibrary = { return SYSCALLS.doReadv(stream, iov, iovcnt); }, #if NO_FILESYSTEM - __syscall146__postset: '/* flush anything remaining in the buffer during shutdown */ __ATEXIT__.push(function() { var fflush = Module["_fflush"]; if (fflush) fflush(0); var printChar = ___syscall146.printChar; if (!printChar) return; var buffers = ___syscall146.buffers; if (buffers[1].length) printChar(1, {{{ charCode("\n") }}}); if (buffers[2].length) printChar(2, {{{ charCode("\n") }}}); });', + $flush_NO_FILESYSTEM: function() { + // flush anything remaining in the buffers during shutdown + var fflush = Module["_fflush"]; + if (fflush) fflush(0); + var printChar = ___syscall146.printChar; + if (!printChar) return; + var buffers = ___syscall146.buffers; + if (buffers[1].length) printChar(1, {{{ charCode("\n") }}}); + if (buffers[2].length) printChar(2, {{{ charCode("\n") }}}); + }, + __syscall146__deps: ['$flush_NO_FILESYSTEM'], +#if NO_EXIT_RUNTIME == 0 + __syscall146__postset: '__ATEXIT__.push(flush_NO_FILESYSTEM);', +#endif #endif __syscall146: function(which, varargs) { // writev #if NO_FILESYSTEM == 0 diff --git a/src/postamble.js b/src/postamble.js index 8e602036f7ce8..5a1f07576ba11 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -212,8 +212,16 @@ Module['callMain'] = function callMain(args) { Module.realPrint('main() took ' + (Date.now() - start) + ' milliseconds'); #endif +#if EMTERPRETIFY_ASYNC + // if we are saving the stack, then do not call exit, we are not + // really exiting now, just unwinding the JS stack + if (EmterpreterAsync.state !== 1) { +#endif // EMTERPRETIFY_ASYNC // if we're not running an evented main loop, it's time to exit - exit(ret, /* implicit = */ true); + exit(ret, /* implicit = */ true); +#if EMTERPRETIFY_ASYNC + } +#endif // EMTERPRETIFY_ASYNC } catch(e) { if (e instanceof ExitStatus) { @@ -313,17 +321,62 @@ function run(args) { Module['run'] = run; function exit(status, implicit) { - if (implicit && Module['noExitRuntime']) { #if ASSERTIONS - Module.printErr('exit(' + status + ') implicitly called by end of main(), but noExitRuntime, so not exiting the runtime (you can use emscripten_force_exit, if you want to force a true shutdown)'); +#if NO_EXIT_RUNTIME == 1 + // Compiler settings do not allow exiting the runtime, so flushing + // the streams is not possible. but in ASSERTIONS mode we check + // if there was something to flush, and if so tell the user they + // should request that the runtime be exitable. + // Normally we would not even include flush() at all, but in ASSERTIONS + // builds we do so just for this check, and here we see if there is any + // content to flush, that is, we check if there would have been + // something a non-ASSERTIONS build would have not seen. + // How we flush the streams depends on whether we are in NO_FILESYSTEM + // mode (which has its own special function for this; otherwise, all + // the code is inside libc) +#if NO_FILESYSTEM + var flush = {{{ '$flush_NO_FILESYSTEM' in addedLibraryItems ? 'flush_NO_FILESYSTEM' : 'null' }}}; +#else + var flush = {{{ '$FS' in addedLibraryItems ? 'FS.quit' : "Module['_fflush']" }}}; #endif + if (flush) { + var print = Module['print']; + var printErr = Module['printErr']; + var has = false; + Module['print'] = Module['printErr'] = function(x) { + has = true; + } + try { // it doesn't matter if it fails + flush(0); + } catch(e) {} + Module['print'] = print; + Module['printErr'] = printErr; + if (has) { + Runtime.warnOnce('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0 (see the FAQ), or make sure to emit a newline when you printf etc.'); + } + } +#endif // NO_EXIT_RUNTIME +#endif // ASSERTIONS + + // if this is just main exit-ing implicitly, and the status is 0, then we + // don't need to do anything here and can just leave. if the status is + // non-zero, though, then we need to report it. + // (we may have warned about this earlier, if a situation justifies doing so) + if (implicit && Module['noExitRuntime'] && status === 0) { return; } if (Module['noExitRuntime']) { #if ASSERTIONS - Module.printErr('exit(' + status + ') called, but noExitRuntime, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)'); -#endif + // if exit() was called, we may warn the user if the runtime isn't actually being shut down + if (!implicit) { +#if NO_EXIT_RUNTIME + Module.printErr('exit(' + status + ') called, but NO_EXIT_RUNTIME is set, so halting execution but not exiting the runtime or preventing further async execution (build with NO_EXIT_RUNTIME=0, if you want a true shutdown)'); +#else + Module.printErr('exit(' + status + ') called, but noExitRuntime is set due to an async operation, so halting execution but not exiting the runtime or preventing further async execution (you can use emscripten_force_exit, if you want to force a true shutdown)'); +#endif // NO_EXIT_RUNTIME + } +#endif // ASSERTIONS } else { #if USE_PTHREADS PThread.terminateAllThreads(); diff --git a/src/pthread-main.js b/src/pthread-main.js index 38d06e8ed3e9f..7cfd24ace4a79 100644 --- a/src/pthread-main.js +++ b/src/pthread-main.js @@ -156,8 +156,7 @@ this.onmessage = function(e) { } // The thread might have finished without calling pthread_exit(). If so, then perform the exit operation ourselves. // (This is a no-op if explicit pthread_exit() had been called prior.) - if (!Module['noExitRuntime']) PThread.threadExit(result); - else console.log('pthread noExitRuntime: not quitting.'); + PThread.threadExit(result); } else if (e.data.cmd === 'cancel') { // Main thread is asking for a pthread_cancel() on this thread. if (threadInfoStruct && PThread.thisThreadCancelState == 0/*PTHREAD_CANCEL_ENABLE*/) { PThread.threadCancel(); diff --git a/src/settings.js b/src/settings.js index e5a97c07c480f..b80643540023e 100644 --- a/src/settings.js +++ b/src/settings.js @@ -44,8 +44,12 @@ var VERBOSE = 0; // When set to 1, will generate more verbose output during comp var INVOKE_RUN = 1; // Whether we will run the main() function. Disable if you embed the generated // code in your own, and will call main() yourself at the right time (which you // can do with Module.callMain(), with an optional parameter of commandline args). -var NO_EXIT_RUNTIME = 0; // If set, the runtime is not quit when main() completes (allowing code to - // run afterwards, for example from the browser main event loop). +var NO_EXIT_RUNTIME = 1; // If 1, the runtime is not quit when main() completes (allowing code to + // run afterwards, for example from the browser main event loop). atexit()s + // are also not executed, and we can avoid including code for runtime shutdown, + // like flushing the stdio streams. + // Set this to 0 if you do want atexit()s or stdio streams to be flushed + // on exit. var MEM_INIT_METHOD = 0; // How to represent the initial memory content. // 0: embed a base64 string literal representing the initial memory data // 1: create a *.mem file containing the binary data of the initial memory; diff --git a/tests/test_benchmark.py b/tests/test_benchmark.py index 5ffcab8712144..b97973979b52a 100644 --- a/tests/test_benchmark.py +++ b/tests/test_benchmark.py @@ -138,7 +138,6 @@ def process(filename): OPTIMIZATIONS, '--memory-init-file', '0', '--js-transform', 'python hardcode.py', '-s', 'TOTAL_MEMORY=256*1024*1024', - '-s', 'NO_EXIT_RUNTIME=1', '-s', 'NO_FILESYSTEM=1', '-s', 'EXPORTED_RUNTIME_METHODS=[]', '-s', 'BENCHMARK=%d' % (1 if IGNORE_COMPILATION and not has_output_parser else 0), diff --git a/tests/test_browser.py b/tests/test_browser.py index e287029e5f1b2..2a13f99429ccc 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -791,7 +791,7 @@ def test_sdl_key(self): ''' % ('setTimeout(function() {' if delay else '', '}, 1);' if delay else '', 'setTimeout(function() {' if delay else '', '}, 1);' if delay else '')) open(os.path.join(self.get_dir(), 'sdl_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl_key.c')).read())) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html'] + defines + emterps + ['--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main']''', '-s', 'NO_EXIT_RUNTIME=1', '-lSDL', '-lGL']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl_key.c'), '-o', 'page.html'] + defines + emterps + ['--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main']''', '-lSDL', '-lGL']).communicate() self.run_browser('page.html', '', '/report_result?223092870') def test_sdl_key_proxy(self): @@ -838,7 +838,7 @@ def post(): ''') open('test.html', 'w').write(html) - self.btest('sdl_key_proxy.c', '223092870', args=['--proxy-to-worker', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-s', 'NO_EXIT_RUNTIME=1', '-lSDL', '-lGL'], manual_reference=True, post_build=post) + self.btest('sdl_key_proxy.c', '223092870', args=['--proxy-to-worker', '--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-lSDL', '-lGL'], manual_reference=True, post_build=post) def test_keydown_preventdefault_proxy(self): def post(): @@ -894,7 +894,7 @@ def post(): open('test.html', 'w').write(html) - self.btest('keydown_preventdefault_proxy.cpp', '300', args=['--proxy-to-worker', '-s', '''EXPORTED_FUNCTIONS=['_main']''', '-s', 'NO_EXIT_RUNTIME=1'], manual_reference=True, post_build=post) + self.btest('keydown_preventdefault_proxy.cpp', '300', args=['--proxy-to-worker', '-s', '''EXPORTED_FUNCTIONS=['_main']'''], manual_reference=True, post_build=post) def test_sdl_text(self): open(os.path.join(self.get_dir(), 'pre.js'), 'w').write(''' @@ -1215,7 +1215,7 @@ def test_emscripten_get_now(self): def test_fflush(self): return self.skip('Skipping due to https://github.com/kripken/emscripten/issues/2770') - self.btest('test_fflush.cpp', '0', args=['-s', 'NO_EXIT_RUNTIME=1', '--shell-file', path_from_root('tests', 'test_fflush.html')]) + self.btest('test_fflush.cpp', '0', args=['--shell-file', path_from_root('tests', 'test_fflush.html')]) def test_file_db(self): secret = str(time.time()) @@ -1250,14 +1250,14 @@ def test_fs_idbfs_fsync(self): }; ''') - args = ['--pre-js', 'pre.js', '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-lidbfs.js'] + args = ['--pre-js', 'pre.js', '-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-lidbfs.js', '-s', 'NO_EXIT_RUNTIME=0'] for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]: secret = str(time.time()) self.btest(path_from_root('tests', 'fs', 'test_idbfs_fsync.c'), '1', force_c=True, args=args + mode + ['-DFIRST', '-DSECRET=\"' + secret + '\"', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']''']) self.btest(path_from_root('tests', 'fs', 'test_idbfs_fsync.c'), '1', force_c=True, args=args + mode + ['-DSECRET=\"' + secret + '\"', '-s', '''EXPORTED_FUNCTIONS=['_main', '_success']''']) def test_fs_memfs_fsync(self): - args = ['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1']; + args = ['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-s', 'NO_EXIT_RUNTIME=0']; for mode in [[], ['-s', 'MEMFS_APPEND_TO_TYPED_ARRAYS=1']]: secret = str(time.time()) self.btest(path_from_root('tests', 'fs', 'test_memfs_fsync.c'), '1', force_c=True, args=args + mode + ['-DSECRET=\"' + secret + '\"', '-s', '''EXPORTED_FUNCTIONS=['_main']''']) @@ -1684,7 +1684,7 @@ def test_emscripten_fs_api2(self): self.btest('emscripten_fs_api_browser2.cpp', '1', args=['-s', "ASSERTIONS=1"]) def test_emscripten_main_loop(self): - for args in [[], ['-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1']]: + for args in [[], ['-s', 'USE_PTHREADS=1', '-s', 'PROXY_TO_PTHREAD=1', '-s', 'NO_EXIT_RUNTIME=0']]: self.btest('emscripten_main_loop.cpp', '0', args=args) def test_emscripten_main_loop_settimeout(self): @@ -2151,13 +2151,13 @@ def test_runtime_misuse(self): print('\n', filename, extra_args, mode) print('mem init, so async, call too early') open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + post_test + post_hook) - self.btest(filename, expected='600', args=['--post-js', 'post.js', '--memory-init-file', '1'] + extra_args + mode) + self.btest(filename, expected='600', args=['--post-js', 'post.js', '--memory-init-file', '1', '-s', 'NO_EXIT_RUNTIME=0'] + extra_args + mode) print('sync startup, call too late') open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + 'Module.postRun.push(function() { ' + post_test + ' });' + post_hook); - self.btest(filename, expected=str(second_code), args=['--post-js', 'post.js', '--memory-init-file', '0'] + extra_args + mode) + self.btest(filename, expected=str(second_code), args=['--post-js', 'post.js', '--memory-init-file', '0', '-s', 'NO_EXIT_RUNTIME=0'] + extra_args + mode) print('sync, runtime still alive, so all good') open(os.path.join(self.get_dir(), 'post.js'), 'w').write(post_prep + 'expected_ok = true; Module.postRun.push(function() { ' + post_test + ' });' + post_hook); - self.btest(filename, expected='606', args=['--post-js', 'post.js', '--memory-init-file', '0', '-s', 'NO_EXIT_RUNTIME=1'] + extra_args + mode) + self.btest(filename, expected='606', args=['--post-js', 'post.js', '--memory-init-file', '0'] + extra_args + mode) def test_cwrap_early(self): self.btest(os.path.join('browser', 'cwrap_early.cpp'), args=['-O2', '-s', 'ASSERTIONS=1', '--pre-js', path_from_root('tests', 'browser', 'cwrap_early.js'), '-s', 'EXTRA_EXPORTED_RUNTIME_METHODS=["cwrap"]'], expected='0') @@ -2296,7 +2296,7 @@ def test_html5_webgl_create_context2(self): def test_html5_webgl_destroy_context(self): for opts in [[], ['-O2', '-g1'], ['-s', 'FULL_ES2=1']]: print(opts) - self.btest(path_from_root('tests', 'webgl_destroy_context.cpp'), args=opts + ['--shell-file', path_from_root('tests/webgl_destroy_context_shell.html'), '-s', 'NO_EXIT_RUNTIME=1', '-lGL'], expected='0', timeout=20) + self.btest(path_from_root('tests', 'webgl_destroy_context.cpp'), args=opts + ['--shell-file', path_from_root('tests/webgl_destroy_context_shell.html'), '-lGL'], expected='0', timeout=20) def test_webgl_context_params(self): if WINDOWS: return self.skip('SKIPPED due to bug https://bugzilla.mozilla.org/show_bug.cgi?id=1310005 - WebGL implementation advertises implementation defined GL_IMPLEMENTATION_COLOR_READ_TYPE/FORMAT pair that it cannot read with') @@ -2473,7 +2473,7 @@ def test_asm_swapping(self): ''') for opts in [[], ['-O1'], ['-O2', '-profiling'], ['-O2']]: print(opts) - opts += ['-s', 'NO_EXIT_RUNTIME=1', '--pre-js', 'run.js', '-s', 'SWAPPABLE_ASM_MODULE=1'] # important that both modules are built with the same opts + opts += ['--pre-js', 'run.js', '-s', 'SWAPPABLE_ASM_MODULE=1'] # important that both modules are built with the same opts open('second.cpp', 'w').write(self.with_report_result(open(path_from_root('tests', 'asm_swap2.cpp')).read())) Popen([PYTHON, EMCC, 'second.cpp'] + opts).communicate() Popen([PYTHON, path_from_root('tools', 'distill_asm.py'), 'a.out.js', 'second.js', 'swap-in']).communicate() @@ -2552,7 +2552,7 @@ def test_sdl2_key(self): ''') open(os.path.join(self.get_dir(), 'sdl2_key.c'), 'w').write(self.with_report_result(open(path_from_root('tests', 'sdl2_key.c')).read())) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl2_key.c'), '-o', 'page.html'] + defines + ['-s', 'USE_SDL=2','--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'sdl2_key.c'), '-o', 'page.html'] + defines + ['-s', 'USE_SDL=2','--pre-js', 'pre.js', '-s', '''EXPORTED_FUNCTIONS=['_main', '_one']''']).communicate() self.run_browser('page.html', '', '/report_result?37182145') def test_sdl2_text(self): @@ -2882,7 +2882,7 @@ def test_emterpreter_async_sleep2(self): def test_emterpreter_async_sleep2_safeheap(self): # check that safe-heap machinery does not cause errors in async operations - self.btest('emterpreter_async_sleep2_safeheap.cpp', '17', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-Oz', '-profiling', '-s', 'SAFE_HEAP=1', '-s', 'ASSERTIONS=1', '-s', 'EMTERPRETIFY_WHITELIST=["_main","_callback","_fix"]']) + self.btest('emterpreter_async_sleep2_safeheap.cpp', '17', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-Oz', '-profiling', '-s', 'SAFE_HEAP=1', '-s', 'ASSERTIONS=1', '-s', 'EMTERPRETIFY_WHITELIST=["_main","_callback","_fix"]', '-s', 'NO_EXIT_RUNTIME=0']) def test_sdl_audio_beep_sleep(self): self.btest('sdl_audio_beep_sleep.cpp', '1', args=['-s', 'EMTERPRETIFY=1', '-s', 'EMTERPRETIFY_ASYNC=1', '-Os', '-s', 'ASSERTIONS=1', '-s', 'DISABLE_EXCEPTION_CATCHING=0', '-profiling', '-s', 'SAFE_HEAP=1', '-lSDL'], timeout=60) @@ -3154,7 +3154,7 @@ def test_pthread_proxy_to_pthread(self): # Test that a pthread can spawn another pthread of its own. def test_pthread_create_pthread(self): for opt in [['-s', 'USE_PTHREADS=2', '--separate-asm'], ['-s', 'USE_PTHREADS=1']]: - self.btest(path_from_root('tests', 'pthread', 'test_pthread_create_pthread.cpp'), expected='1', args=opt + ['-O3', '-s', 'PTHREAD_POOL_SIZE=2', '-s', 'NO_EXIT_RUNTIME=1'], timeout=30) + self.btest(path_from_root('tests', 'pthread', 'test_pthread_create_pthread.cpp'), expected='1', args=opt + ['-O3', '-s', 'PTHREAD_POOL_SIZE=2'], timeout=30) # Test another case of pthreads spawning pthreads, but this time the callers immediately join on the threads they created. def test_pthread_nested_spawns(self): diff --git a/tests/test_core.py b/tests/test_core.py index d7ca26f0d1433..1c43154b5bb22 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -154,6 +154,7 @@ def test_i64_zextneg(self): self.do_run_in_out_file_test('tests', 'core', 'test_i64_zextneg') def test_i64_7z(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_i64_7z', args=['hallo']) @@ -188,6 +189,7 @@ def test_i32_mul_precise(self): self.do_run_in_out_file_test('tests', 'core', 'test_i32_mul_precise') def test_i16_emcc_intrinsic(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_i16_emcc_intrinsic') def test_double_i64_conversion(self): @@ -504,6 +506,7 @@ def test_zerodiv(self): self.do_run_in_out_file_test('tests', 'core', 'test_zerodiv') def test_zero_multiplication(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_zero_multiplication') def test_isnan(self): @@ -538,6 +541,7 @@ def test_frexp(self): self.do_run_in_out_file_test('tests', 'core', 'test_frexp') def test_rounding(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams for precise_f32 in [0, 1]: print(precise_f32) Settings.PRECISE_F32 = precise_f32 @@ -887,6 +891,7 @@ def test_setjmp_noleak(self): def test_exceptions(self): Settings.EXCEPTION_DEBUG = 1 + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams Settings.DISABLE_EXCEPTION_CATCHING = 0 self.maybe_closure() @@ -1074,7 +1079,7 @@ def test_exceptions_white_list_2(self): def test_exceptions_uncaught(self): Settings.DISABLE_EXCEPTION_CATCHING = 0 - + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams src = r''' #include #include @@ -1111,7 +1116,7 @@ def test_exceptions_uncaught(self): def test_exceptions_uncaught_2(self): Settings.DISABLE_EXCEPTION_CATCHING = 0 - + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams src = r''' #include #include @@ -1135,6 +1140,7 @@ def test_exceptions_uncaught_2(self): def test_exceptions_typed(self): Settings.DISABLE_EXCEPTION_CATCHING = 0 + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.emcc_args += ['-s', 'SAFE_HEAP=0'] # Throwing null will cause an ignorable null pointer access. self.do_run_in_out_file_test('tests', 'core', 'test_exceptions_typed') @@ -1240,9 +1246,11 @@ def test_inherit(self): self.do_run_in_out_file_test('tests', 'core', 'test_inherit') def test_isdigit_l(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_isdigit_l') def test_iswdigit(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_iswdigit') def test_polymorph(self): @@ -1322,7 +1330,8 @@ def test_rename(self): self.do_run_in_out_file_test('tests', 'stdio', 'test_rename', force_c=True) def test_remove(self): - self.do_run_in_out_file_test('tests', 'cstdio', 'test_remove') + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams + self.do_run_in_out_file_test('tests', 'cstdio', 'test_remove') def test_alloca_stack(self): self.do_run_in_out_file_test('tests', 'core', 'test_alloca_stack') @@ -1545,6 +1554,7 @@ def test_emscripten_run_script_string_utf8(self): def test_emscripten_get_now(self): self.banned_js_engines = [V8_ENGINE] # timer limitations in v8 shell + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams if self.run_name == 'asm2': self.emcc_args += ['--closure', '1'] # Use closure here for some additional coverage @@ -2046,6 +2056,7 @@ def test_structbyval(self): def test_stdlibs(self): # safe heap prints a warning that messes up our output. Settings.SAFE_HEAP = 0 + Settings.NO_EXIT_RUNTIME = 0 # needs atexit self.do_run_in_out_file_test('tests', 'core', 'test_stdlibs') def test_stdbool(self): @@ -2097,6 +2108,7 @@ def test_strtol_oct(self): def test_atexit(self): # Confirms they are called in reverse order + Settings.NO_EXIT_RUNTIME = 0 # needs atexits self.do_run_in_out_file_test('tests', 'core', 'test_atexit') def test_pthread_specific(self): @@ -2147,6 +2159,7 @@ def test_strptime_days(self): self.do_run_in_out_file_test('tests', 'core', 'test_strptime_days') def test_strptime_reentrant(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_strptime_reentrant') def test_strftime(self): @@ -3159,7 +3172,7 @@ def test(): #include extern int sidey(); int main() { - printf("other says %d.", sidey()); + printf("other says %d.\\n", sidey()); return 0; } ''', ''' @@ -3177,7 +3190,7 @@ def test_dylink_floats(self): #include extern float sidey(); int main() { - printf("other says %.2f.", sidey()+1); + printf("other says %.2f.\\n", sidey()+1); return 0; } ''', ''' @@ -3406,12 +3419,12 @@ def test_dylink_i64(self): ''', 'other says 175a1ddee82b8c31.') def test_dylink_i64_b(self): - self.dylink_test(''' + self.dylink_test(r''' #include #include extern int64_t sidey(); int main() { - printf("other says %lld.", sidey()); + printf("other says %lld.\n", sidey()); return 0; } ''', ''' @@ -3633,7 +3646,7 @@ def test(syslibs, expect_pass=True, need_reverse=True): } ''', side=r''' #include - void side() { std::cout << "cout hello from side"; } + void side() { std::cout << "cout hello from side\n"; } ''', expected=['cout hello from side\n'], need_reverse=need_reverse) except Exception as e: @@ -3857,7 +3870,7 @@ def test_random(self): int main() { srandom(0xdeadbeef); - printf("%ld", random()); + printf("%ld\n", random()); } ''' self.do_run(src, '956867869') @@ -3954,6 +3967,7 @@ def test_transtrcase(self): def test_printf(self): self.banned_js_engines = [NODE_JS, V8_ENGINE] # SpiderMonkey and V8 do different things to float64 typed arrays, un-NaNing, etc. + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams src = open(path_from_root('tests', 'printf', 'test.c'), 'r').read() expected = open(path_from_root('tests', 'printf', 'output.txt'), 'r').read() self.do_run(src, expected) @@ -4057,11 +4071,12 @@ def test_sscanf_n(self): self.do_run_in_out_file_test('tests', 'core', 'test_sscanf_n') def test_sscanf_whitespace(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_sscanf_whitespace') def test_sscanf_other_whitespace(self): Settings.SAFE_HEAP = 0 # use i16s in printf - + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_sscanf_other_whitespace') def test_sscanf_3(self): @@ -4146,6 +4161,7 @@ def clean(out, err): @sync def test_files_m(self): # Test for Module.stdin etc. + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams post = ''' def process(filename): @@ -4450,12 +4466,14 @@ def test_wprintf(self): self.do_run_from_file(src, output) def test_direct_string_constant_usage(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.do_run_in_out_file_test('tests', 'core', 'test_direct_string_constant_usage') def test_std_cout_new(self): self.do_run_in_out_file_test('tests', 'core', 'test_std_cout_new') def test_istream(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams test_path = path_from_root('tests', 'core', 'test_istream') src, output = (test_path + s for s in ('.c', '.out')) @@ -5005,8 +5023,6 @@ def test_dlmalloc_partial_2(self): if 'SAFE_HEAP' in str(self.emcc_args): return self.skip('we do unsafe stuff here') # present part of the symbols of dlmalloc, not all. malloc is harder to link than new which is weak. - Settings.NO_EXIT_RUNTIME = 1 # if we exit, then we flush streams, but the bad malloc we install here messes that up - self.do_run_in_out_file_test('tests', 'core', 'test_dlmalloc_partial_2') def test_libcxx(self): @@ -5028,6 +5044,7 @@ def test_typeid(self): self.do_run_in_out_file_test('tests', 'core', 'test_typeid') def test_static_variable(self): + Settings.NO_EXIT_RUNTIME = 0 # needs atexit self.do_run_in_out_file_test('tests', 'core', 'test_static_variable') def test_fakestat(self): @@ -5035,6 +5052,7 @@ def test_fakestat(self): def test_mmap(self): Settings.TOTAL_MEMORY = 128*1024*1024 + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams test_path = path_from_root('tests', 'core', 'test_mmap') src, output = (test_path + s for s in ('.c', '.out')) @@ -5216,6 +5234,7 @@ def test_simd2(self): @SIMD def test_simd3(self): Settings.PRECISE_F32 = 1 # SIMD currently requires Math.fround + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.emcc_args = self.emcc_args + ['-msse2'] test_path = path_from_root('tests', 'core', 'test_simd3') src, output = (test_path + s for s in ('.c', '.out')) @@ -5236,6 +5255,7 @@ def test_simd5(self): @SIMD def test_simd6(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams # test_simd6 is to test x86 min and max intrinsics on NaN and -0.0 self.emcc_args = self.emcc_args + ['-msse'] test_path = path_from_root('tests', 'core', 'test_simd6') @@ -5298,6 +5318,7 @@ def test_simd13(self): @SIMD def test_simd14(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.emcc_args = self.emcc_args + ['-msse', '-msse2'] test_path = path_from_root('tests', 'core', 'test_simd14') src, output = (test_path + s for s in ('.c', '.out')) @@ -5529,8 +5550,6 @@ def test_poppler(self): if WINDOWS: return self.skip('test_poppler depends on freetype, which uses a ./configure script to build and therefore currently only runs on Linux and OS X.') def test(): - Settings.NO_EXIT_RUNTIME = 1 - Building.COMPILER_TEST_OPTS += [ '-I' + path_from_root('tests', 'freetype', 'include'), '-I' + path_from_root('tests', 'poppler', 'include') @@ -5664,8 +5683,6 @@ def image_compare(output, err): self.emcc_args += ['--minify', '0'] # to compare the versions - Settings.NO_EXIT_RUNTIME = 1 - def do_test(): self.do_run(open(path_from_root('tests', 'openjpeg', 'codec', 'j2k_to_image.c'), 'r').read(), 'Successfully generated', # The real test for valid output is in image_compare @@ -5757,6 +5774,8 @@ def test_lifetime(self): def test_cases(self): if Building.LLVM_OPTS: return self.skip("Our code is not exactly 'normal' llvm assembly") + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams + emcc_args = self.emcc_args # The following tests link to libc, and must be run with EMCC_LEAVE_INPUTS_RAW = 0 @@ -6293,9 +6312,6 @@ def test_tracing(self): def test_eval_ctors(self): if '-O2' not in str(self.emcc_args) or '-O1' in str(self.emcc_args): return self.skip('need js optimizations') - if self.is_wasm(): - self.emcc_args += ['-s', 'NO_EXIT_RUNTIME=1'] - orig_args = self.emcc_args[:] + ['-s', 'EVAL_CTORS=0'] print('leave printf in ctor') @@ -6421,7 +6437,6 @@ def test_embind(self): self.do_run(src, 'abs(-10): 10\nabs(-11): 11'); def test_embind_2(self): - Settings.NO_EXIT_RUNTIME = 1 # we emit some post.js that we need to see Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js'] open('post.js', 'w').write(''' function printLerp() { @@ -6447,7 +6462,6 @@ def test_embind_2(self): self.do_run(src, 'lerp 166'); def test_embind_3(self): - Settings.NO_EXIT_RUNTIME = 1 # we emit some post.js that we need to see Building.COMPILER_TEST_OPTS += ['--bind', '--post-js', 'post.js'] open('post.js', 'w').write(''' function ready() { @@ -6523,7 +6537,7 @@ def test_embind_f_no_rtti(self): #include int main(int argc, char** argv){ - printf("418"); + printf("418\n"); return 0; } ''' @@ -6857,6 +6871,7 @@ def test_float_literals(self): @sync def test_exit_status(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams src = r''' #include #include @@ -6915,6 +6930,7 @@ def test_vswprintf_utf8(self): self.do_run_from_file(path_from_root('tests', 'vswprintf_utf8.c'), path_from_root('tests', 'vswprintf_utf8.out')) def test_async(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams self.banned_js_engines = [SPIDERMONKEY_ENGINE, V8_ENGINE] # needs setTimeout which only node has src = r''' @@ -7058,6 +7074,7 @@ def test_async_exit(self): ''', 'f\nhello\nf\nhello\nf\nhello\nf\nhello\nf\nhello\nexit\n') def test_coroutine(self): + Settings.NO_EXIT_RUNTIME = 0 # needs to flush stdio streams src = r''' #include #include diff --git a/tests/test_interactive.py b/tests/test_interactive.py index 7dca0b018ea95..8b66912a0850a 100644 --- a/tests/test_interactive.py +++ b/tests/test_interactive.py @@ -39,10 +39,10 @@ def test_sdl_touch(self): self.btest(path_from_root('tests', 'sdl_touch.c'), args=['-O2', '-g1', '--closure', '1'], expected='0') def test_sdl_wm_togglefullscreen(self): - self.btest('sdl_wm_togglefullscreen.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1']) + self.btest('sdl_wm_togglefullscreen.c', expected='1') def test_sdl2_togglefullscreen(self): - self.btest('sdl_togglefullscreen.c', expected='1', args=['-s', 'USE_SDL=2', '-s', 'NO_EXIT_RUNTIME=1']) + self.btest('sdl_togglefullscreen.c', expected='1', args=['-s', 'USE_SDL=2']) def test_sdl_audio(self): shutil.copyfile(path_from_root('tests', 'sounds', 'alarmvictory_1.ogg'), os.path.join(self.get_dir(), 'sound.ogg')) @@ -147,7 +147,7 @@ def test_openal_al_soft_source_spatialize(self): self.btest('openal_playback.cpp', '1', args=['-DTEST_AL_SOFT_SOURCE_SPATIALIZE=1', '-DTEST_LOOPED_PLAYBACK=1', '--preload-file', path_from_root('tests', 'sounds', 'the_entertainer.wav') + '@/audio.wav'],) def test_openal_capture(self): - self.btest('openal_capture.c', expected='0', args=['-s', 'NO_EXIT_RUNTIME=1']) + self.btest('openal_capture.c', expected='0') def get_freealut_library(self): if WINDOWS and Building.which('cmake'): @@ -169,16 +169,16 @@ def test_glfw_dropfile(self): self.btest('test_glfw_dropfile.c', expected='1', args=['-s', 'USE_GLFW=3', '-lglfw', '-lGL']) def test_glfw_fullscreen(self): - self.btest('test_glfw_fullscreen.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1', '-s', 'USE_GLFW=3']) + self.btest('test_glfw_fullscreen.c', expected='1', args=['-s', 'USE_GLFW=3']) def test_glfw_get_key_stuck(self): - self.btest('test_glfw_get_key_stuck.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1', '-s', 'USE_GLFW=3']) + self.btest('test_glfw_get_key_stuck.c', expected='1', args=['-s', 'USE_GLFW=3']) def test_glfw_joystick(self): - self.btest('glfw_joystick.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1', '-s', 'USE_GLFW=3']) + self.btest('glfw_joystick.c', expected='1', args=['-s', 'USE_GLFW=3']) def test_glfw_pointerlock(self): - self.btest('test_glfw_pointerlock.c', expected='1', args=['-s', 'NO_EXIT_RUNTIME=1', '-s', 'USE_GLFW=3']) + self.btest('test_glfw_pointerlock.c', expected='1', args=['-s', 'USE_GLFW=3']) def test_cpuprofiler_memoryprofiler(self): self.btest('hello_world_gles.c', expected='0', args=['-DLONGTEST=1', '-DTEST_MEMORYPROFILER_ALLOCATIONS_MAP=1', '-O2', '--cpuprofiler', '--memoryprofiler']) diff --git a/tests/test_other.py b/tests/test_other.py index 489e47157705c..3954b94061a76 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -1603,7 +1603,7 @@ def test(link_cmd, lib_suffix=''): Popen(compiler + [os.path.join(self.get_dir(), 'main.cpp')] + link_cmd + ['-lother', '-c']).communicate() 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 - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o')] + link_cmd + ['-lother']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.o')] + link_cmd + ['-lother', '-s', 'NO_EXIT_RUNTIME=0']).communicate() self.assertContained('*hello from lib\n|hello from lib|\n*', run_js(os.path.join(self.get_dir(), 'a.out.js'))) @@ -1627,7 +1627,7 @@ def test_js_link(self): Module.print(MESSAGE); ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js']).communicate() self.assertContained('hello from main\nhello from js\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_sdl_endianness(self): @@ -1805,7 +1805,7 @@ def test_prepost(self): }; ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) # never run, so no preRun or postRun @@ -1816,7 +1816,7 @@ def test_prepost(self): # noInitialRun prevents run for no_initial_run, run_dep in [(0, 0), (1, 0), (0, 1)]: print(no_initial_run, run_dep) - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp')]).communicate() src = 'var Module = { noInitialRun: %d };\n' % no_initial_run + open(os.path.join(self.get_dir(), 'a.out.js')).read() if run_dep: src = src.replace('// {{PRE_RUN_ADDITIONS}}', '// {{PRE_RUN_ADDITIONS}}\naddRunDependency("test");') \ @@ -1839,7 +1839,7 @@ def test_prepost(self): preInit: function() { Module.print('pre-init') } }; ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js']).communicate() self.assertContained('pre-init\npre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_prepost2(self): @@ -1858,7 +1858,7 @@ def test_prepost2(self): open(os.path.join(self.get_dir(), 'pre2.js'), 'w').write(''' Module.postRun = function() { Module.print('post-run') }; ''') - Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'pre.js', '--pre-js', 'pre2.js']).communicate() self.assertContained('pre-run\nhello from main\npost-run\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) def test_prepre(self): @@ -3251,6 +3251,60 @@ def test_no_exit_runtime(self): assert ('_ZN5WasteILi2EED' in src) == exit, 'destructors should not appear if no exit' assert ('atexit(' in src) == exit, 'atexit should not appear or be called' + def test_no_exit_runtime_warnings_flush(self): + # check we warn if there is unflushed info + open('code.c', 'w').write(r''' +#include +int main(int argc, char **argv) { + printf("hello\n"); + printf("world"); // no newline, not flushed +#if FLUSH + printf("\n"); +#endif +} +''') + open('code.cpp', 'w').write(r''' +#include +int main() { + using namespace std; + cout << "hello" << std::endl; + cout << "world"; // no newline, not flushed +#if FLUSH + std::cout << std::endl; +#endif +} +''') + for src in ['code.c', 'code.cpp']: + for no_exit in [0, 1]: + for assertions in [0, 1]: + for flush in [0, 1]: + # TODO: also check NO_FILESYSTEM here. it never worked though, buffered output was not emitted at shutdown + print(src, no_exit, assertions, flush) + cmd = [PYTHON, EMCC, src, '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-s', 'ASSERTIONS=%d' % assertions] + if flush: cmd += ['-DFLUSH'] + subprocess.check_call(cmd) + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True) + exit = 1-no_exit + assert 'hello' in output, output + assert ('world' in output) == (exit or flush), 'unflushed content is shown only when exiting the runtime' + assert (no_exit and assertions and not flush) == ('stdio streams had content in them that was not flushed. you should set NO_EXIT_RUNTIME to 0' in output), 'warning should be shown' + + def test_no_exit_runtime_warnings_atexit(self): + open('code.cpp', 'w').write(r''' +#include +void bye() {} +int main() { + atexit(bye); +} +''') + for no_exit in [0, 1]: + for assertions in [0, 1]: + print(no_exit, assertions) + subprocess.check_call([PYTHON, EMCC, 'code.cpp', '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-s', 'ASSERTIONS=%d' % assertions]) + output = run_js(os.path.join(self.get_dir(), 'a.out.js'), stderr=PIPE, full_output=True) + exit = 1-no_exit + assert (no_exit and assertions) == ('atexit() called, but NO_EXIT_RUNTIME is set, so atexits() will not be called. set NO_EXIT_RUNTIME to 0' in output), 'warning should be shown' + def test_os_oz(self): if os.environ.get('EMCC_DEBUG'): return self.skip('cannot run in debug mode') try: @@ -3350,12 +3404,12 @@ def test_global_inits(self): ''') for opts, has_global in [ - (['-O2', '-g'], True), - (['-O2', '-g', '-s', 'NO_EXIT_RUNTIME=1'], False), # no-exit-runtime removes the atexits, and then globalgce can work it's magic to remove the global initializer entirely - (['-Os', '-g'], True), - (['-Os', '-g', '-s', 'NO_EXIT_RUNTIME=1'], False), - (['-O2', '-g', '--llvm-lto', '1'], True), - (['-O2', '-g', '-s', 'NO_EXIT_RUNTIME=1', '--llvm-lto', '1'], False), + (['-O2', '-g', '-s', 'NO_EXIT_RUNTIME=0'], True), + (['-O2', '-g'], False), # no-exit-runtime removes the atexits, and then globalgce can work it's magic to remove the global initializer entirely + (['-Os', '-g', '-s', 'NO_EXIT_RUNTIME=0'], True), + (['-Os', '-g'], False), + (['-O2', '-g', '--llvm-lto', '1', '-s', 'NO_EXIT_RUNTIME=0'], True), + (['-O2', '-g', '--llvm-lto', '1'], False), ]: print(opts, has_global) Popen([PYTHON, EMCC, 'main.cpp', '-c'] + opts).communicate() @@ -3783,16 +3837,45 @@ def test_argv0_node(self): def test_returncode(self): open('src.cpp', 'w').write(r''' #include + #include int main() { - return 123; + #if CALL_EXIT + exit(CODE); + #else + return CODE; + #endif } ''') - Popen([PYTHON, EMCC, 'src.cpp']).communicate() - for engine in JS_ENGINES: - print(engine) - process = Popen(engine + ['a.out.js'], stdout=PIPE, stderr=PIPE) - output = process.communicate() - assert process.returncode == 123, process.returncode + for code in [0, 123]: + for no_exit in [0, 1]: + for call_exit in [0, 1]: + subprocess.check_call([PYTHON, EMCC, 'src.cpp', '-DCODE=%d' % code, '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-DCALL_EXIT=%d' % call_exit]) + for engine in JS_ENGINES: + print(code, no_exit, call_exit, engine) + process = Popen(engine + ['a.out.js'], stdout=PIPE, stderr=PIPE) + out, err = process.communicate() + # we always emit the right exit code, whether we exit the runtime or not + assert process.returncode == code, [process.returncode, out, err] + assert not out, out + if not call_exit: + assert not err, err + assert ('but NO_EXIT_RUNTIME is set, so halting execution but not exiting the runtime or preventing further async execution (build with NO_EXIT_RUNTIME=0, if you want a true shutdown)' in err) == (no_exit and call_exit), err + + def test_emscripten_force_exit_NO_EXIT_RUNTIME(self): + open('src.cpp', 'w').write(r''' + #include + int main() { + #if CALL_EXIT + emscripten_force_exit(0); + #endif + } + ''') + for no_exit in [0, 1]: + for call_exit in [0, 1]: + subprocess.check_call([PYTHON, EMCC, 'src.cpp', '-s', 'NO_EXIT_RUNTIME=%d' % no_exit, '-DCALL_EXIT=%d' % call_exit]) + print(no_exit, call_exit) + out = run_js('a.out.js', stdout=PIPE, stderr=PIPE, full_output=True) + assert ('emscripten_force_exit cannot actually shut down the runtime, as the build has NO_EXIT_RUNTIME set' in out) == (no_exit and call_exit), out def test_mkdir_silly(self): open('src.cpp', 'w').write(r''' @@ -4779,7 +4862,7 @@ def test_locale_wrong(self): } } ''') - Popen([PYTHON, EMCC, 'src.cpp']).communicate() + Popen([PYTHON, EMCC, 'src.cpp', '-s', 'NO_EXIT_RUNTIME=0']).communicate() self.assertContained('Constructed locale "C"\nThis locale is the global locale.\nThis locale is the C locale.', run_js('a.out.js', args=['C'])) self.assertContained('''Can't construct locale "waka": collate_byname::collate_byname failed to construct for waka''', run_js('a.out.js', args=['waka'], assert_returncode=1)) @@ -5327,7 +5410,7 @@ def test_link_with_a_static(self): Popen([PYTHON, EMAR, 'rc', 'libtest.a', 'y.o']).communicate() Popen([PYTHON, EMAR, 'rc', 'libtest.a', 'x.o']).communicate() Popen([PYTHON, EMRANLIB, 'libtest.a']).communicate() - Popen([PYTHON, EMCC, 'z.o', 'libtest.a'] + args).communicate() + Popen([PYTHON, EMCC, 'z.o', 'libtest.a', '-s', 'NO_EXIT_RUNTIME=0'] + args).communicate() out = run_js('a.out.js', assert_returncode=161) def test_link_with_bad_o_in_a(self): @@ -5344,22 +5427,22 @@ def test_link_with_bad_o_in_a(self): def test_require(self): inname = path_from_root('tests', 'hello_world.c') - Building.emcc(inname, output_filename='a.out.js') + Building.emcc(inname, args=['-s', 'ASSERTIONS=0'], output_filename='a.out.js') output = Popen(NODE_JS + ['-e', 'require("./a.out.js")'], stdout=PIPE, stderr=PIPE).communicate() assert output == ('hello, world!\n', ''), 'expected no output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % output def test_require_modularize(self): - Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1', '-s', 'ASSERTIONS=0']).communicate() src = open('a.out.js').read() assert "module['exports'] = Module;" in src output = Popen(NODE_JS + ['-e', 'var m = require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE).communicate() assert output == ('hello, world!\n', ''), 'expected output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % output - Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1', '-s', 'EXPORT_NAME="NotModule"']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1', '-s', 'EXPORT_NAME="NotModule"', '-s', 'ASSERTIONS=0']).communicate() src = open('a.out.js').read() assert "module['exports'] = NotModule;" in src output = Popen(NODE_JS + ['-e', 'var m = require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE).communicate() assert output == ('hello, world!\n', ''), 'expected output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % output - Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1', '-s', 'NO_EXIT_RUNTIME=1']).communicate() + Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-s', 'MODULARIZE=1']).communicate() # We call require() twice to ensure it returns wrapper function each time output = Popen(NODE_JS + ['-e', 'require("./a.out.js")();var m = require("./a.out.js"); m();'], stdout=PIPE, stderr=PIPE).communicate() assert output[0] == 'hello, world!\nhello, world!\n', 'expected output, got\n===\nSTDOUT\n%s\n===\nSTDERR\n%s\n===\n' % output @@ -5581,13 +5664,13 @@ def test_module_onexit(self): #include int main() { EM_ASM({ - Module.onExit = function(status) { Module.print('exiting now, status ' + status) }; + Module['onExit'] = function(status) { Module.print('exiting now, status ' + status) }; }); return 14; } ''') try_delete('a.out.js') - Popen([PYTHON, EMCC, 'src.cpp']).communicate() + Popen([PYTHON, EMCC, 'src.cpp', '-s', 'NO_EXIT_RUNTIME=0']).communicate() self.assertContained('exiting now, status 14', run_js('a.out.js', assert_returncode=14)) def test_underscore_exit(self): @@ -5850,7 +5933,7 @@ def test(main_args=[], library_args=[], expected='hello from main\nhello from li #include void library_func() { #ifdef USE_PRINTF - printf("hello from library: %p", (int)&library_func); + printf("hello from library: %p\n", (int)&library_func); #else puts("hello from library"); #endif @@ -6263,7 +6346,7 @@ def test_realpath(self): perror("Resolve failed"); return 1; } else { - printf("Resolved: %s", t_realpath_buf); + printf("Resolved: %s\n", t_realpath_buf); free(t_realpath_buf); return 0; } @@ -6297,7 +6380,7 @@ def test_realpath_nodefs(self): perror("Resolve failed"); return 1; } else { - printf("Resolved: %s", t_realpath_buf); + printf("Resolved: %s\n", t_realpath_buf); free(t_realpath_buf); return 0; } @@ -7419,7 +7502,7 @@ def test_binaryen_names(self): try_delete('a.out.js') subprocess.check_call([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')] + args + ['-s', 'BINARYEN=1']) code = open('a.out.wasm', 'rb').read() - assert (b'__fflush_unlocked' in code) == expect_names, 'an internal function not exported nor imported must only appear in the binary if we have a names section' + assert (code.count(b'malloc') == 2) == expect_names, 'name section adds the name of malloc (there is also another one for the export' sizes[str(args)] = os.stat('a.out.wasm').st_size print(sizes) assert sizes["['-O2']"] < sizes["['-O2', '--profiling-funcs']"], 'when -profiling-funcs, the size increases due to function names' @@ -7672,14 +7755,14 @@ def test_binaryen_metadce(self): sizes = {} # in -Os, -Oz, we remove imports wasm doesn't need for args, expected_len, expected_exists, expected_not_exists in [ - ([], 24, ['abort', 'tempDoublePtr'], ['waka']), - (['-O1'], 21, ['abort', 'tempDoublePtr'], ['waka']), - (['-O2'], 21, ['abort', 'tempDoublePtr'], ['waka']), - (['-O3'], 16, ['abort'], ['tempDoublePtr', 'waka']), # in -O3, -Os and -Oz we metadce - (['-Os'], 16, ['abort'], ['tempDoublePtr', 'waka']), - (['-Oz'], 16, ['abort'], ['tempDoublePtr', 'waka']), + ([], 25, ['abort', 'tempDoublePtr'], ['waka']), + (['-O1'], 20, ['abort', 'tempDoublePtr'], ['waka']), + (['-O2'], 20, ['abort', 'tempDoublePtr'], ['waka']), + (['-O3'], 14, ['abort'], ['tempDoublePtr', 'waka']), # in -O3, -Os and -Oz we metadce + (['-Os'], 14, ['abort'], ['tempDoublePtr', 'waka']), + (['-Oz'], 14, ['abort'], ['tempDoublePtr', 'waka']), # finally, check what happens when we export pretty much nothing. wasm should be almost empty - (['-Os', '-s', 'EXPORTED_FUNCTIONS=[]', '-s', 'EXPORTED_RUNTIME_METHODS=[]'], 9, ['abort'], ['tempDoublePtr', 'waka']), + (['-Os', '-s', 'EXPORTED_FUNCTIONS=[]', '-s', 'EXPORTED_RUNTIME_METHODS=[]'], 2, ['STACKTOP'], ['tempDoublePtr', 'waka']), ]: print(args, expected_len, expected_exists, expected_not_exists) subprocess.check_call([PYTHON, EMCC, path_from_root('tests', 'hello_world.cpp')] + args + ['-s', 'WASM=1', '-g2']) @@ -7691,7 +7774,8 @@ def test_binaryen_metadce(self): relevant = js[start+2:end-2] relevant = relevant.replace(' ', '').replace('"', '').replace("'", '').split(',') sent = [x.split(':')[0].strip() for x in relevant] - assert len(sent) == expected_len, (len(sent), expected_len) + print(' seen: ' + str(sent)) + assert len(sent) == expected_len, (expected_len, len(sent)) for exists in expected_exists: assert exists in sent, [exists, sent] for not_exists in expected_not_exists: @@ -7833,13 +7917,6 @@ def test_native_link_error_message(self): out, err = Popen([PYTHON, EMCC, 'hello_world.a', '-o', 'hello_world.js'], stdout=PIPE, stderr=PIPE).communicate() assert 'exists but was not an LLVM bitcode file suitable for Emscripten. Perhaps accidentally mixing native built object files with Emscripten?' in err - # Tests that the warning message about pairing WASM with EVAL_CTORS appropriately triggers the warning about NO_EXIT_RUNTIME. - def test_binaryen_no_exit_runtime_warn_message(self): - out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', 'hello_world.js', '-s', 'WASM=1', '-Oz'], stdout=PIPE, stderr=PIPE).communicate() - assert 'you should enable -s NO_EXIT_RUNTIME=1' in err - out, err = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world.c'), '-o', 'hello_world.bc', '-s', 'WASM=1', '-Oz'], stdout=PIPE, stderr=PIPE).communicate() - assert 'you should enable -s NO_EXIT_RUNTIME=1' not in err - def test_o_level_clamp(self): for level in [3, 4, 20]: out, err = Popen([PYTHON, EMCC, '-O' + str(level), path_from_root('tests', 'hello_world.c')], stdout=PIPE, stderr=PIPE).communicate()