Make output scripts executable by default when targeting node This is something we already did autoconf mode.
diff --git a/ChangeLog.md b/ChangeLog.md index cd0025f..e6a8c73 100644 --- a/ChangeLog.md +++ b/ChangeLog.md
@@ -20,6 +20,9 @@ 3.1.33 (in development) ----------------------- +- When targetting `node` (i.e. when node is included in `ENVIRONMENT`) the + output file is now marked as executable and includes a !# line by default. + This can be disabled explictly via `-sEXECUTABLE_OUTPUT=0`. - Removed `sys/sysctl.h` compatibility header. We don't implement the function it defines. (#18863) - Update SDL2_ttf port to 2.20.2 (#18804)
diff --git a/emcc.py b/emcc.py index 165e0c2..21b0f9c 100755 --- a/emcc.py +++ b/emcc.py
@@ -253,7 +253,7 @@ def __init__(self): self.output_file = None self.post_link = False - self.executable = False + self.autoconf = False self.compiler_wrapper = None self.oformat = None self.requested_debug = '' @@ -749,20 +749,23 @@ return passes -def make_js_executable(script): +def make_js_executable(options, script): src = read_file(script) - cmd = config.NODE_JS - if settings.MEMORY64 == 1: - cmd += shared.node_memory64_flags() - elif settings.WASM_BIGINT: - cmd += shared.node_bigint_flags() - if len(cmd) > 1 or not os.path.isabs(cmd[0]): - # Using -S (--split-string) here means that arguments to the executable are - # correctly parsed. We don't do this by default because old versions of env - # don't support -S. - cmd = '/usr/bin/env -S ' + shared.shlex_join(cmd) + if options.autoconf: + cmd = config.NODE_JS + if settings.MEMORY64 == 1: + cmd += shared.node_memory64_flags() + elif settings.WASM_BIGINT: + cmd += shared.node_bigint_flags() + if len(cmd) > 1 or not os.path.isabs(cmd[0]): + # Using -S (--split-string) here means that arguments to the executable are + # correctly parsed. We don't do this by default because old versions of env + # don't support -S. + cmd = '/usr/bin/env -S ' + shared.shlex_join(cmd) + else: + cmd = shared.shlex_join(cmd) else: - cmd = shared.shlex_join(cmd) + cmd = '/usr/bin/env node' logger.debug('adding `#!` to JavaScript file: %s' % cmd) # add shebang with open(script, 'w') as f: @@ -1760,14 +1763,14 @@ @ToolchainProfiler.profile_block('linker_setup') def phase_linker_setup(options, state, newargs): - autoconf = os.environ.get('EMMAKEN_JUST_CONFIGURE') or 'conftest.c' in state.orig_args or 'conftest.cpp' in state.orig_args - if autoconf: + options.autoconf = os.environ.get('EMMAKEN_JUST_CONFIGURE') or 'conftest.c' in state.orig_args or 'conftest.cpp' in state.orig_args + if options.autoconf: # configure tests want a more shell-like style, where we emit return codes on exit() settings.EXIT_RUNTIME = 1 # use node.js raw filesystem access, to behave just like a native executable settings.NODERAWFS = 1 # Add `#!` line to output JS and make it executable. - options.executable = True + settings.EXECUTABLE_OUTPUT = True system_libpath = '-L' + str(cache.get_lib_dir(absolute=True)) add_link_flag(state, sys.maxsize, system_libpath) @@ -1775,6 +1778,9 @@ if settings.OPT_LEVEL >= 1: default_setting('ASSERTIONS', 0) + if settings.ENVIRONMENT_MAY_BE_NODE and not settings.MODULARIZE: + default_setting('EXECUTABLE_OUTPUT', 1) + if options.emrun: options.pre_js.append(utils.path_from_root('src/emrun_prejs.js')) options.post_js.append(utils.path_from_root('src/emrun_postjs.js')) @@ -1826,7 +1832,7 @@ dirname = os.path.dirname(target) if dirname and not os.path.isdir(dirname): exit_with_error("specified output file (%s) is in a directory that does not exist" % target) - elif autoconf: + elif options.autoconf: # Autoconf expects the executable output file to be called `a.out` target = 'a.out' elif settings.SIDE_MODULE: @@ -3291,8 +3297,8 @@ for f in generated_text_files_with_native_eols: tools.line_endings.convert_line_endings_in_file(f, os.linesep, options.output_eol) - if options.executable: - make_js_executable(js_target) + if settings.EXECUTABLE_OUTPUT and settings.ENVIRONMENT_MAY_BE_NODE: + make_js_executable(options, js_target) def version_string():
diff --git a/src/settings.js b/src/settings.js index 704d987..46819fb 100644 --- a/src/settings.js +++ b/src/settings.js
@@ -2101,6 +2101,10 @@ // library symbol. var LEGACY_RUNTIME = false; +// Mark JS output file as executable and include #! line at the top. +// This defaults to true when node is included in ENVIRONMENT. +var EXECUTABLE_OUTPUT = false; + //=========================================== // Internal, used for testing only, from here //===========================================
diff --git a/test/test_core.py b/test/test_core.py index f52fe17..bf46fc1 100644 --- a/test/test_core.py +++ b/test/test_core.py
@@ -429,9 +429,7 @@ self.assertEqual(prefix, output[:len(prefix)]) def verify_in_strict_mode(self, filename): - js = read_file(filename) - filename += '.strict.js' - write_file(filename, '"use strict";\n' + js) + self.node_args.append('--use_strict') self.run_js(filename) def do_core_test(self, testname, **kwargs):
diff --git a/test/test_other.py b/test/test_other.py index bd157ba..8a6c71a 100644 --- a/test/test_other.py +++ b/test/test_other.py
@@ -11309,6 +11309,12 @@ output = self.run_process([os.path.abspath('a.out')], stdout=PIPE).stdout self.assertContained('hello, world!', output) + @no_windows('windows does not support shbang syntax') + def test_executable_output(self): + self.run_process([EMCC, test_file('hello_world.c')]) + output = self.run_process([os.path.abspath('a.out.js')], stdout=PIPE).stdout + self.assertContained('hello, world!', output) + def test_standalone_export_main(self): # Tests that explicitly exported `_main` does not fail, even though `_start` is the entry # point.