diff --git a/dtool/src/dtoolutil/console_preamble.js b/dtool/src/dtoolutil/console_preamble.js new file mode 100644 index 0000000000..151f5c1e03 --- /dev/null +++ b/dtool/src/dtoolutil/console_preamble.js @@ -0,0 +1,50 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file console_preamble.js + * @author rdb + * @date 2025-02-03 + */ + +if (ENVIRONMENT_IS_NODE) { + Module["preInit"] = Module["preInit"] || []; + Module["preInit"].push(function() { + if (typeof process === "object" && typeof process.env === "object") { + // These are made up by emscripten if we don't set them to undefined + ENV['USER'] = undefined; + ENV['LOGNAME'] = undefined; + ENV['PATH'] = undefined; + ENV['PWD'] = undefined; + ENV['HOME'] = undefined; + ENV['LANG'] = undefined; + ENV['_'] = undefined; + for (var variable in process.env) { + ENV[variable] = process.env[variable]; + } + } + + addOnPreMain(function preloadNodeEnv() { + var sp = stackSave(); + var set_binary_name = wasmExports["_set_binary_name"]; + if (set_binary_name && typeof __filename === "string") { + set_binary_name(stringToUTF8OnStack(__filename)); + } + + var set_env_var = wasmExports["_set_env_var"]; + if (set_env_var) { + for (var variable in ENV) { + var value = ENV[variable]; + if (value !== undefined) { + set_env_var(stringToUTF8OnStack(variable), stringToUTF8OnStack(value)); + } + } + } + stackRestore(sp); + }); + }); +} diff --git a/dtool/src/dtoolutil/executionEnvironment.cxx b/dtool/src/dtoolutil/executionEnvironment.cxx index 71083fa7ab..371a211612 100644 --- a/dtool/src/dtoolutil/executionEnvironment.cxx +++ b/dtool/src/dtoolutil/executionEnvironment.cxx @@ -125,12 +125,18 @@ static const char *const libp3dtool_filenames[] = { #if defined(__EMSCRIPTEN__) && !defined(CPPPARSER) extern "C" void EMSCRIPTEN_KEEPALIVE -_set_env_var(ExecutionEnvironment *ptr, const char *var, const char *value) { +_set_env_var(const char *var, const char *value) { + ExecutionEnvironment *ptr = ExecutionEnvironment::get_ptr(); ptr->_variables[std::string(var)] = std::string(value); } + +extern "C" void EMSCRIPTEN_KEEPALIVE +_set_binary_name(const char *path) { + ExecutionEnvironment::set_binary_name(std::string(path)); +} #endif -// Linux with GNU libc does have global argvargc variables, but we can't +// Linux with GNU libc does have global argv/argc variables, but we can't // safely access them at stat init time--at least, not in libc5. (It does seem // to work with glibc2, however.) @@ -584,17 +590,10 @@ read_environment_variables() { } } #elif defined(__EMSCRIPTEN__) - // We only have environment variables if we're running in node.js. -#ifndef CPPPARSER - EM_ASM({ - if (typeof process === 'object' && typeof process.env === 'object') { - for (var variable in process.env) { - __set_env_var($0, stringToUTF8OnStack(variable), - stringToUTF8OnStack(process.env[variable])); - } - } - }, this); -#endif + // The environment variables get loaded in by the .js file before main() + // using the _set_env_var exported function, defined above. Trying to load + // env vars at static init time otherwise makes some optimizations more + // difficult, notably wasm-ctor-eval/wizer. #elif defined(HAVE_PROC_SELF_ENVIRON) // In some cases, we may have a file called procselfenviron that may be read diff --git a/dtool/src/dtoolutil/executionEnvironment.h b/dtool/src/dtoolutil/executionEnvironment.h index e54f87c40f..ddb83fcecc 100644 --- a/dtool/src/dtoolutil/executionEnvironment.h +++ b/dtool/src/dtoolutil/executionEnvironment.h @@ -22,10 +22,10 @@ #include #if defined(__EMSCRIPTEN__) && !defined(CPPPARSER) -class ExecutionEnvironment; - extern "C" void EMSCRIPTEN_KEEPALIVE -_set_env_var(ExecutionEnvironment *ptr, const char *var, const char *value); +_set_env_var(const char *var, const char *value); +extern "C" void EMSCRIPTEN_KEEPALIVE +_set_binary_name(const char *path); #endif /** @@ -98,7 +98,7 @@ private: static ExecutionEnvironment *_global_ptr; #ifdef __EMSCRIPTEN__ - friend void ::_set_env_var(ExecutionEnvironment *ptr, const char *var, const char *value); + friend void ::_set_env_var(const char *var, const char *value); #endif }; diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 7e4588051a..614b2cbb45 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1913,6 +1913,9 @@ def CompileLink(dll, obj, opts): if GetOrigExt(dll) == ".exe": cmd += " -s EXIT_RUNTIME=1" + if dll.endswith(".js") and "SUBSYSTEM:WINDOWS" not in opts: + cmd += " --pre-js dtool/src/dtoolutil/console_preamble.js" + else: cmd += " -pthread" if "SYSROOT" in SDK: @@ -5984,7 +5987,7 @@ if GetLinkAllStatic(): if not PkgSkip('BULLET'): DefSymbol('RUN_TESTS_FLAGS', 'HAVE_BULLET') - OPTS=['DIR:tests', 'PYTHON', 'RUN_TESTS_FLAGS'] + OPTS=['DIR:tests', 'PYTHON', 'RUN_TESTS_FLAGS', 'SUBSYSTEM:CONSOLE'] PyTargetAdd('run_tests-main.obj', opts=OPTS, input='main.c') PyTargetAdd('run_tests.exe', input='run_tests-main.obj') PyTargetAdd('run_tests.exe', input='core.pyd') diff --git a/tests/main.c b/tests/main.c index acf26dd10e..8fce2d6f92 100644 --- a/tests/main.c +++ b/tests/main.c @@ -19,11 +19,6 @@ #include -#ifdef __EMSCRIPTEN__ -#include -#include -#endif - #include "pandabase.h" #ifdef LINK_ALL_STATIC @@ -55,43 +50,6 @@ int main(int argc, char **argv) { PyConfig config; PyConfig_InitPythonConfig(&config); -#ifdef __EMSCRIPTEN__ - // getenv does not work with emscripten, instead read the PYTHONPATH and - // PYTHONHOME from the process.env variable if we're running in node.js. - char path[4096], home[4096]; - path[0] = 0; - home[0] = 0; - - EM_ASM({ - if (typeof process === 'object' && typeof process.env === 'object') { - var path = process.env.PYTHONPATH; - var home = process.env.PYTHONHOME; - if (path) { - if (process.platform === 'win32') { - path = path.replace(/;/g, ':'); - } - stringToUTF8(path, $0, $1); - } - if (home) { - stringToUTF8(home, $2, $3); - } - } - }, path, sizeof(path), home, sizeof(home)); - - if (path[0] != 0) { - status = PyConfig_SetBytesString(&config, &config.pythonpath_env, path); - if (PyStatus_Exception(status)) { - goto exception; - } - } - if (home[0] != 0) { - status = PyConfig_SetBytesString(&config, &config.home, home); - if (PyStatus_Exception(status)) { - goto exception; - } - } -#endif - PyConfig_SetBytesString(&config, &config.run_module, "pytest"); config.parse_argv = 0;