From 1629d2ca56014beb2d46c42cc199a43ac97e3b97 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Thu, 21 Nov 2024 05:10:46 +0100 Subject: [PATCH] gh-126898: Emscripten support: Use es6 modules (#126903) Modify Emscripten support to use ES6 modules. --- ...-11-20-17-12-40.gh-issue-126898.I2zILt.rst | 1 + Tools/wasm/emscripten/__main__.py | 36 +++++++++++++------ Tools/wasm/emscripten/node_entry.mjs | 30 ++++++++++++++++ Tools/wasm/emscripten/node_pre.js | 15 -------- configure | 4 +-- configure.ac | 4 +-- 6 files changed, 60 insertions(+), 30 deletions(-) create mode 100644 Misc/NEWS.d/next/Build/2024-11-20-17-12-40.gh-issue-126898.I2zILt.rst create mode 100644 Tools/wasm/emscripten/node_entry.mjs delete mode 100644 Tools/wasm/emscripten/node_pre.js diff --git a/Misc/NEWS.d/next/Build/2024-11-20-17-12-40.gh-issue-126898.I2zILt.rst b/Misc/NEWS.d/next/Build/2024-11-20-17-12-40.gh-issue-126898.I2zILt.rst new file mode 100644 index 00000000000..37783c4e890 --- /dev/null +++ b/Misc/NEWS.d/next/Build/2024-11-20-17-12-40.gh-issue-126898.I2zILt.rst @@ -0,0 +1 @@ +The Emscripten build of Python is now based on ES6 modules. diff --git a/Tools/wasm/emscripten/__main__.py b/Tools/wasm/emscripten/__main__.py index 2015a3764ea..9ce8dd6a364 100644 --- a/Tools/wasm/emscripten/__main__.py +++ b/Tools/wasm/emscripten/__main__.py @@ -4,20 +4,22 @@ import argparse import contextlib import functools import os - -try: - from os import process_cpu_count as cpu_count -except ImportError: - from os import cpu_count -from pathlib import Path import shutil import subprocess import sys import sysconfig import tempfile +from pathlib import Path +from textwrap import dedent -WASM_DIR = Path(__file__).parent.parent -CHECKOUT = WASM_DIR.parent.parent +try: + from os import process_cpu_count as cpu_count +except ImportError: + from os import cpu_count + + +EMSCRIPTEN_DIR = Path(__file__).parent +CHECKOUT = EMSCRIPTEN_DIR.parent.parent.parent CROSS_BUILD_DIR = CHECKOUT / "cross-build" BUILD_DIR = CROSS_BUILD_DIR / "build" @@ -72,7 +74,7 @@ def subdir(working_dir, *, clean_ok=False): print("⎯" * terminal_width) print("📁", working_dir) if clean_ok and getattr(context, "clean", False) and working_dir.exists(): - print(f"🚮 Deleting directory (--clean)...") + print("🚮 Deleting directory (--clean)...") shutil.rmtree(working_dir) working_dir.mkdir(parents=True, exist_ok=True) @@ -207,9 +209,21 @@ def configure_emscripten_python(context, working_dir): quiet=context.quiet, ) - python_js = working_dir / "python.js" + shutil.copy(EMSCRIPTEN_DIR / "node_entry.mjs", working_dir / "node_entry.mjs") + + node_entry = working_dir / "node_entry.mjs" exec_script = working_dir / "python.sh" - exec_script.write_text(f'#!/bin/sh\nexec {host_runner} {python_js} "$@"\n') + exec_script.write_text( + dedent( + f"""\ + #!/bin/sh + + # We compute our own path, not following symlinks and pass it in so that + # node_entry.mjs can set sys.executable correctly. + exec {host_runner} {node_entry} "$(realpath -s $0)" "$@" + """ + ) + ) exec_script.chmod(0o755) print(f"🏃‍♀️ Created {exec_script} ... ") sys.stdout.flush() diff --git a/Tools/wasm/emscripten/node_entry.mjs b/Tools/wasm/emscripten/node_entry.mjs new file mode 100644 index 00000000000..cb1c6ff3cba --- /dev/null +++ b/Tools/wasm/emscripten/node_entry.mjs @@ -0,0 +1,30 @@ +import EmscriptenModule from "./python.mjs"; +import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +if (process?.versions?.node) { + const nodeVersion = Number(process.versions.node.split(".", 1)[0]); + if (nodeVersion < 18) { + process.stderr.write( + `Node version must be >= 18, got version ${process.version}\n`, + ); + process.exit(1); + } +} + +const settings = { + preRun(Module) { + const __dirname = dirname(fileURLToPath(import.meta.url)); + Module.FS.mkdirTree("/lib/"); + Module.FS.mount(Module.FS.filesystems.NODEFS, { root: __dirname + "/lib/" }, "/lib/"); + }, + // The first three arguments are: "node", path to this file, path to + // python.sh. After that come the arguments the user passed to python.sh. + arguments: process.argv.slice(3), + // Ensure that sys.executable, sys._base_executable, etc point to python.sh + // not to this file. To properly handle symlinks, python.sh needs to compute + // its own path. + thisProgram: process.argv[2], +}; + +await EmscriptenModule(settings); diff --git a/Tools/wasm/emscripten/node_pre.js b/Tools/wasm/emscripten/node_pre.js deleted file mode 100644 index 54b09dc0823..00000000000 --- a/Tools/wasm/emscripten/node_pre.js +++ /dev/null @@ -1,15 +0,0 @@ -// If process is undefined, we're not running in the node runtime let it go I -// guess? -if (typeof process !== "undefined") { - const nodeVersion = Number(process.versions.node.split(".", 1)[0]); - if (nodeVersion < 18) { - process.stderr.write( - `Node version must be >= 18, got version ${process.version}\n`, - ); - process.exit(1); - } - Module.preRun = () => { - FS.mkdirTree("/lib/"); - FS.mount(NODEFS, { root: __dirname + "/lib/" }, "/lib/"); - }; -} diff --git a/configure b/configure index 5b44a3d6992..84b74ac3584 100755 --- a/configure +++ b/configure @@ -7291,7 +7291,7 @@ else $as_nop case $ac_sys_system in #( Emscripten) : - EXEEXT=.js ;; #( + EXEEXT=.mjs ;; #( WASI) : EXEEXT=.wasm ;; #( *) : @@ -9432,6 +9432,7 @@ fi as_fn_append LDFLAGS_NODIST " -sWASM_BIGINT" as_fn_append LDFLAGS_NODIST " -sFORCE_FILESYSTEM -lidbfs.js -lnodefs.js -lproxyfs.js -lworkerfs.js" + as_fn_append LDFLAGS_NODIST " -sEXPORTED_RUNTIME_METHODS=FS" if test "x$enable_wasm_dynamic_linking" = xyes then : @@ -9450,7 +9451,6 @@ then : fi as_fn_append LDFLAGS_NODIST " -sALLOW_MEMORY_GROWTH" as_fn_append LDFLAGS_NODIST " -sEXIT_RUNTIME" - as_fn_append LDFLAGS_NODIST " --pre-js=\$(srcdir)/Tools/wasm/emscripten/node_pre.js" WASM_LINKFORSHARED_DEBUG="-gseparate-dwarf --emit-symbol-map" if test "x$wasm_debug" = xyes diff --git a/configure.ac b/configure.ac index 7904f8990c4..8fa6cb60900 100644 --- a/configure.ac +++ b/configure.ac @@ -1327,7 +1327,7 @@ AC_ARG_WITH([suffix], ) ], [ AS_CASE([$ac_sys_system], - [Emscripten], [EXEEXT=.js], + [Emscripten], [EXEEXT=.mjs], [WASI], [EXEEXT=.wasm], [EXEEXT=] ) @@ -2328,6 +2328,7 @@ AS_CASE([$ac_sys_system], dnl Include file system support AS_VAR_APPEND([LDFLAGS_NODIST], [" -sFORCE_FILESYSTEM -lidbfs.js -lnodefs.js -lproxyfs.js -lworkerfs.js"]) + AS_VAR_APPEND([LDFLAGS_NODIST], [" -sEXPORTED_RUNTIME_METHODS=FS"]) AS_VAR_IF([enable_wasm_dynamic_linking], [yes], [ AS_VAR_APPEND([LINKFORSHARED], [" -sMAIN_MODULE"]) @@ -2341,7 +2342,6 @@ AS_CASE([$ac_sys_system], AS_VAR_APPEND([LDFLAGS_NODIST], [" -sALLOW_MEMORY_GROWTH"]) dnl not completely sure whether or not we want -sEXIT_RUNTIME, keeping it for now. AS_VAR_APPEND([LDFLAGS_NODIST], [" -sEXIT_RUNTIME"]) - AS_VAR_APPEND([LDFLAGS_NODIST], [" --pre-js=\$(srcdir)/Tools/wasm/emscripten/node_pre.js"]) WASM_LINKFORSHARED_DEBUG="-gseparate-dwarf --emit-symbol-map" AS_VAR_IF([wasm_debug], [yes], [