diff --git a/ChangeLog.md b/ChangeLog.md index ed064b7895ea7..baa7b8adca665 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -26,6 +26,10 @@ See docs/process.md for more on how version tagging works. use case. (#25645, #25440) - The fetch library now supports streaming data requests when `-sFETCH_STREAMING` is enabled. +- A new `NODE_HOST_ENV` setting was added which exposes the host environment + variables to the generated program, when running under Node. This setting is + enabled by default when `-sNODERAWFS` is used but can also be controlled + separately. (#18820) 4.0.20 - 11/18/25 ----------------- diff --git a/site/source/docs/tools_reference/settings_reference.rst b/site/source/docs/tools_reference/settings_reference.rst index 79d6f7501093e..21943519e801b 100644 --- a/site/source/docs/tools_reference/settings_reference.rst +++ b/site/source/docs/tools_reference/settings_reference.rst @@ -1494,7 +1494,7 @@ Default value: false NODERAWFS ========= -Enables support for the NODERAWFS filesystem backend. This is a special +Enables support for the ``NODERAWFS`` filesystem backend. This is a special backend as it replaces all normal filesystem access with direct Node.js operations, without the need to do ``FS.mount()``, and this backend only works with Node.js. The initial working directory will be same as @@ -1504,6 +1504,20 @@ necessarily be portable between OSes - it will be as portable as a Node.js program would be, which means that differences in how the underlying OS handles permissions and errors and so forth may be noticeable. +Enabling this setting will also enable :ref:`NODE_HOST_ENV` by default. + +Default value: false + +.. _node_host_env: + +NODE_HOST_ENV +============= + +When running under Node, expose the underlying OS environment variables. +This is similar to how ``NODERAWFS`` exposes the underlying FS. +This setting gets enabled by default when ``NODERAWFS`` is enabled, but can +also be controlled separately. + Default value: false .. _node_code_caching: diff --git a/src/lib/libwasi.js b/src/lib/libwasi.js index 64abbf0659cfa..4b6f422586051 100644 --- a/src/lib/libwasi.js +++ b/src/lib/libwasi.js @@ -67,6 +67,12 @@ var WasiLibrary = { '_': getExecutableName() #endif }; +#if ENVIRONMENT_MAY_BE_NODE && NODE_HOST_ENV + if (ENVIRONMENT_IS_NODE) { + // When NODE_HOST_ENV is enabled we mirror then entire host environment. + env = process.env; + } +#endif // Apply the user-provided values, if any. for (var x in ENV) { // x is a key in ENV; if ENV[x] is undefined, that means it was diff --git a/src/settings.js b/src/settings.js index 39b5d637b5dd1..9048096cc47d2 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1023,7 +1023,7 @@ var FILESYSTEM = true; // [link] var FORCE_FILESYSTEM = false; -// Enables support for the NODERAWFS filesystem backend. This is a special +// Enables support for the ``NODERAWFS`` filesystem backend. This is a special // backend as it replaces all normal filesystem access with direct Node.js // operations, without the need to do ``FS.mount()``, and this backend only // works with Node.js. The initial working directory will be same as @@ -1032,9 +1032,17 @@ var FORCE_FILESYSTEM = false; // necessarily be portable between OSes - it will be as portable as a Node.js // program would be, which means that differences in how the underlying OS // handles permissions and errors and so forth may be noticeable. +// +// Enabling this setting will also enable :ref:`NODE_HOST_ENV` by default. // [link] var NODERAWFS = false; +// When running under Node, expose the underlying OS environment variables. +// This is similar to how ``NODERAWFS`` exposes the underlying FS. +// This setting gets enabled by default when ``NODERAWFS`` is enabled, but can +// also be controlled separately. +var NODE_HOST_ENV = false; + // This saves the compiled wasm module in a file with name // ``$WASM_BINARY_NAME.$V8_VERSION.cached`` // and loads it on subsequent runs. This caches the compiled wasm code from diff --git a/test/other/test_std_filesystem_tempdir.cpp b/test/other/test_std_filesystem_tempdir.cpp new file mode 100644 index 0000000000000..72c52c0e69c94 --- /dev/null +++ b/test/other/test_std_filesystem_tempdir.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +namespace fs = std::filesystem; + +int main() { + // libc++'s temp_directory_path depends on one of these env vars + // being set, and falls back to /tmp as a default. + // Log these values in case this test ever fails: + const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; + for (int i = 0; i < sizeof(env_paths)/sizeof(char*); i++) { + const char* p = getenv(env_paths[i]); + std::cout << env_paths[i] << " -> " << (p ? p : "(NULL)") << "\n"; + if (p) { + std::cout << env_paths[i] << " exists: " << fs::exists(env_paths[i]) << "\n"; + } + } + std::cout << "default /tmp exists: " << fs::exists("/tmp") << "\n"; + + fs::path tmp{fs::temp_directory_path()}; + std::cout << "temp_directory_path: " << tmp << "\n"; + assert(fs::exists(tmp)); + std::cout << "exists ok!" << "\n"; + return 0; +} diff --git a/test/other/test_std_filesystem_tempdir.out b/test/other/test_std_filesystem_tempdir.out new file mode 100644 index 0000000000000..3498cf1ddaf12 --- /dev/null +++ b/test/other/test_std_filesystem_tempdir.out @@ -0,0 +1 @@ +exists ok! diff --git a/test/test_other.py b/test/test_other.py index 4677051fcc7dd..db5299e953427 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -7840,6 +7840,24 @@ def test_override_js_execution_environment(self): seen = self.run_js('test.js', engine=engine, assert_returncode=NON_ZERO) self.assertContained('Module.ENVIRONMENT has been deprecated. To force the environment, use the ENVIRONMENT compile-time option (for example, -sENVIRONMENT=web or -sENVIRONMENT=node', seen) + @requires_node + @with_env_modify({'FOO': 'bar'}) + @parameterized({ + '': ([], '|(null)|\n'), + 'rawfs': (['-sNODERAWFS'], '|bar|\n'), + 'rawfs_no_env': (['-sNODERAWFS', '-sNODE_HOST_ENV=0'], '|(null)|\n'), + 'enabled': (['-sNODE_HOST_ENV'], '|bar|\n'), + }) + def test_node_host_env(self, args, expected): + create_file('src.c', r''' + #include + #include + int main() { + printf("|%s|\n", getenv("FOO")); + } + ''') + self.do_runf('src.c', expected, cflags=args) + def test_override_c_environ(self): create_file('pre.js', r''' Module.preRun = () => { ENV.hello = '💩 world'; ENV.LANG = undefined; } @@ -13644,12 +13662,20 @@ def test_fs_icase(self): # c++20 for ends_with(). self.do_other_test('test_fs_icase.cpp', cflags=['-sCASE_INSENSITIVE_FS', '-std=c++20']) + @crossplatform @with_all_fs def test_std_filesystem(self): if (WINDOWS or MACOS) and self.get_setting('NODERAWFS'): self.skipTest('Rawfs directory removal works only on Linux') self.do_other_test('test_std_filesystem.cpp') + @crossplatform + @with_all_fs + def test_std_filesystem_tempdir(self): + if (WINDOWS or MACOS) and self.get_setting('NODERAWFS') and self.get_setting('WASMFS'): + self.skipTest('NODERAWFS + WASMFS is does not allow access to arbitrary paths') + self.do_other_test('test_std_filesystem_tempdir.cpp', cflags=['-g']) + def test_strict_js_closure(self): self.do_runf('hello_world.c', cflags=['-sSTRICT_JS', '-Werror=closure', '--closure=1', '-O3']) diff --git a/tools/link.py b/tools/link.py index b27d557b684e0..e779c761b6d15 100644 --- a/tools/link.py +++ b/tools/link.py @@ -1405,7 +1405,6 @@ def limit_incoming_module_api(): # see https://github.com/emscripten-core/emscripten/issues/18723#issuecomment-1429236996 if settings.MODULARIZE: default_setting('NODEJS_CATCH_REJECTION', 0) - default_setting('NODEJS_CATCH_EXIT', 0) if settings.POLYFILL: # Emscripten requires certain ES6+ constructs by default in library code @@ -1884,6 +1883,20 @@ def get_full_import_name(name): if settings.PTHREADS: settings.REQUIRED_EXPORTS.append('_emscripten_tls_init') + if settings.NODERAWFS: + default_setting('NODE_HOST_ENV', 1) + + if not settings.ENVIRONMENT_MAY_BE_NODE: + # Node-specific settings only make sense if ENVIRONMENT_MAY_BE_NODE + if settings.NODERAWFS: + diagnostics.warning('unused-command-line-argument', 'NODERAWFS ignored since `node` not in `ENVIRONMENT`') + if settings.NODE_CODE_CACHING: + diagnostics.warning('unused-command-line-argument', 'NODE_CODE_CACHING ignored since `node` not in `ENVIRONMENT`') + if settings.NODEJS_CATCH_EXIT: + diagnostics.warning('unused-command-line-argument', 'NODEJS_CATCH_EXIT ignored since `node` not in `ENVIRONMENT`') + if settings.NODEJS_CATCH_REJECTION and 'NODEJS_CATCH_REJECTION' in user_settings: + diagnostics.warning('unused-command-line-argument', 'NODEJS_CATCH_REJECTION ignored since `node` not in `ENVIRONMENT`') + settings.PRE_JS_FILES = options.pre_js settings.POST_JS_FILES = options.post_js