gh-156780: Emscripten: support building CPython with static linking - #156781
gh-156780: Emscripten: support building CPython with static linking#156781clementperon wants to merge 4 commits into
Conversation
|
@hoodmane 👀 maybe ? |
|
Maybe let's start with a PR that adds the I think rather than using #ifdef __EMSCRIPTEN__
__attribute__((constructor)) void _PyEmscripten_Constructor(void) {
_PyEmscripten_BeforeMain();
}
And of course as traditional we need much less prose since Claude is as rambly as usual. Please rewrite all the comments and the commit message by hand. I find that helps cut them down to a more reasonable length. Of course, it's likely that any of this will bit rot without test coverage. cc @freakboy3742 WDYT? |
e0433ec to
c89eb2f
Compare
|
@hoodmane as you suggest move to a dedicated object file + reword commit + add a test |
c89eb2f to
2e46f9a
Compare
+1 to this as an approach to landing the changes.
I agree that linking into the mainline is a preferable approach to a runtime flag. As for where to put it - I can definitely see the argument for using
+1 to this as well. Claude's predilection for extraneous verbositude is usually contrary to the underlying goals of effective communication :-) |
fa3261b to
ee90cfa
Compare
|
@hoodmane EM_JS_DEPS has been merged thanks to @freakboy3742. I will rework a bit this PR + clean commit message & comment. |
5417e3e to
7c1f523
Compare
7c1f523 to
18d7011
Compare
|
@hoodmane new version after discussion with AI
Tested with the CI configuration, a |
|
The changes to |
The trampoline exports _PyRuntime's address itself and reuses a placeholder's table slot, so libpython no longer needs -sEXPORTED_FUNCTIONS=__PyRuntime or a growable function table. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Running main() under WebAssembly.promising only makes sense when Python is the program. Move the wrapper into Programs/emscripten_beforemain.c, linked into the interpreter but not into libpython, and let the runtime shut down normally afterwards so atexit handlers run and buffered output is flushed. FS.createAsyncInputDevice() stays in libpython. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…() wrapper Suspending in fd_read or poll needs a promising entry point, which libpython no longer sets up itself. Check Module.Py_EmscriptenStackSwitching, set by the interpreter's wrapper, instead of WebAssembly.promising alone. An embedder with its own promising entry point sets the flag. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Link libpython.a into a program whose main() is not Python's, without -sMAIN_MODULE or the interpreter's link flags, and run a script that imports from the stdlib, calls through the trampoline and polls. Platforms/emscripten/web_embed_test/run_test.sh builds and runs it in CI. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
18d7011 to
1eaadc6
Compare
|
Rebase on top of #157238 |
Fixes #156780.
CPython's Emscripten glue was written for the
-sMAIN_MODULEconfiguration it is built and tested in. Linkinglibpython.ainto a program without that flag failed at startup because the glue relied on things only that configuration provides:resolveGlobalSymbol()fromlibdylink.js, a growable function table foraddFunction(), and_PyRuntimebeing exported through CPython's own-sEXPORTED_FUNCTIONS.Changes
main()wrapping out of libpython. Runningmain()underWebAssembly.promisingonly makes sense when Python is the program. The wrapper moves fromPython/emscripten_syscalls.ctoPrograms/emscripten_beforemain.c, which is linked into the interpreter (and installed next topython.o) but not intolibpython. It rebinds_main, andwasmImports.mainunder-sMAIN_MODULE, so one code path covers both configurations. Whenmain()completes it exits throughexitJS(), soatexithandlers run and buffered output is flushed, and the exit status is preserved.FS.createAsyncInputDevice()stays in libpython.fd_readandpollonly suspend whenModule.Py_EmscriptenStackSwitchingis set, which the interpreter's wrapper does on entry tomain(). An embedder running its own promising entry point sets the flag itself; otherwise the calls keep their synchronous behaviour. The flag is documented inemscripten_syscalls.c._PyRuntime's address itself and takes over a placeholder's function table slot instead of callingaddFunction(), so it needs neither-sEXPORTED_FUNCTIONSnor-sALLOW_TABLE_GROWTH.__PyRuntimeis dropped from the interpreter's export list since nothing reads it any more.Platforms/emscripten/web_embed_testlinkslibpython.ainto a program whosemain()is not Python's, without-sMAIN_MODULEor the interpreter's link flags, and runs a script that imports from the stdlib, calls through the trampoline and polls.run_test.shbuilds and runs it and is wired into the Emscripten CI job.Testing
configurewas regenerated withmake regen-configure.test_capi.test_emscripten,test_select,test_builtin,test_json,test_sys,test_functoolspass.--disable-wasm-dynamic-linking, not covered by CI): builds and behaves the same, including stack switching on Node 24 with JSPI.libpython.a; the syscall-override object is pulled into the link.make Programs/_testembedandmake libainstallwork.