From 41add82f1750e4c477b941cd076ad12e3a150a99 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Wed, 4 Feb 2026 02:32:33 +0000 Subject: [PATCH 01/21] WIP test for new build system design --- Cargo.lock | 15 ++++++ Cargo.toml | 2 +- Makefile.pre.in | 3 +- Modules/_base64/Cargo.toml | 6 ++- Modules/_base64/build.rs | 5 ++ Modules/cpython-build-helper/Cargo.toml | 7 +++ Modules/cpython-build-helper/README.md | 4 ++ Modules/cpython-build-helper/src/lib.rs | 17 +++++++ Modules/cpython-rust-staticlib/Cargo.toml | 11 +++++ Modules/cpython-rust-staticlib/src/lib.rs | 1 + Modules/makesetup | 60 +++++++++++------------ configure | 43 +++++++++++----- configure.ac | 40 ++++++++++----- 13 files changed, 153 insertions(+), 61 deletions(-) create mode 100644 Modules/_base64/build.rs create mode 100644 Modules/cpython-build-helper/Cargo.toml create mode 100644 Modules/cpython-build-helper/README.md create mode 100644 Modules/cpython-build-helper/src/lib.rs create mode 100644 Modules/cpython-rust-staticlib/Cargo.toml create mode 100644 Modules/cpython-rust-staticlib/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index afc3744abd1cb6..b2080d8d0da80b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,6 +6,7 @@ version = 4 name = "_base64" version = "0.1.0" dependencies = [ + "cpython-build-helper", "cpython-sys", ] @@ -70,6 +71,20 @@ dependencies = [ "libloading", ] +[[package]] +name = "cpython-build-helper" +version = "0.1.0" +dependencies = [ + "shlex", +] + +[[package]] +name = "cpython-rust-staticlib" +version = "0.1.0" +dependencies = [ + "_base64", +] + [[package]] name = "cpython-sys" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 8b75182b7a596a..768d289fd77653 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "3" members = [ - "Modules/_base64", + "Modules/_base64", "Modules/cpython-build-helper", "Modules/cpython-rust-staticlib", "Modules/cpython-sys" ] diff --git a/Makefile.pre.in b/Makefile.pre.in index 10f299819cd40f..0f2553f1fcf737 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -178,7 +178,8 @@ CONFINCLUDEPY= $(CONFINCLUDEDIR)/python$(LDVERSION) SHLIB_SUFFIX= @SHLIB_SUFFIX@ EXT_SUFFIX= @EXT_SUFFIX@ LDSHARED= @LDSHARED@ $(PY_LDFLAGS) -BLDSHARED= @BLDSHARED@ $(PY_CORE_LDFLAGS) +BLDSHARED_EXE= @BLDSHARED_EXE@ +BLDSHARED_ARGS= @BLDSHARED_ARGS@ $(PY_CORE_LDFLAGS) LDCXXSHARED= @LDCXXSHARED@ $(PY_LDFLAGS) DESTSHARED= $(BINLIBDEST)/lib-dynload diff --git a/Modules/_base64/Cargo.toml b/Modules/_base64/Cargo.toml index 0810b787ab2773..f90350624729d6 100644 --- a/Modules/_base64/Cargo.toml +++ b/Modules/_base64/Cargo.toml @@ -6,6 +6,10 @@ edition = "2024" [dependencies] cpython-sys ={ path = "../cpython-sys" } +[build-dependencies] +cpython-build-helper = { path = "../cpython-build-helper" } + [lib] name = "_base64" -crate-type = ["staticlib"] +# For shared builds, we generate a c dynamic library. Static builds use the rlib +crate-type = ["cdylib", "rlib"] diff --git a/Modules/_base64/build.rs b/Modules/_base64/build.rs new file mode 100644 index 00000000000000..8cca32f8f8c3df --- /dev/null +++ b/Modules/_base64/build.rs @@ -0,0 +1,5 @@ +use cpython_build_helper::print_linker_args; + +fn main() { + print_linker_args(); +} diff --git a/Modules/cpython-build-helper/Cargo.toml b/Modules/cpython-build-helper/Cargo.toml new file mode 100644 index 00000000000000..1369020965012a --- /dev/null +++ b/Modules/cpython-build-helper/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cpython-build-helper" +version = "0.1.0" +edition = "2024" + +[dependencies] +shlex = "1.3" diff --git a/Modules/cpython-build-helper/README.md b/Modules/cpython-build-helper/README.md new file mode 100644 index 00000000000000..f42946cfefc31c --- /dev/null +++ b/Modules/cpython-build-helper/README.md @@ -0,0 +1,4 @@ +# cpython-build-helper + +This crate is used in Rust modules `build.rs` files to help do common tasks such +as passing necessary link arguments. \ No newline at end of file diff --git a/Modules/cpython-build-helper/src/lib.rs b/Modules/cpython-build-helper/src/lib.rs new file mode 100644 index 00000000000000..f9fc5b2e77fc46 --- /dev/null +++ b/Modules/cpython-build-helper/src/lib.rs @@ -0,0 +1,17 @@ +use std::env; + +/// Print necessary link arguments for the library depending on the build +/// configuration (static or shared) +pub fn print_linker_args() { + let shared_build = env::var("RUST_SHARED_BUILD").expect("RUST_SHARED_BUILD not set in Makefile?"); + if shared_build == "1" { + let build_shared_args = + env::var("BLDSHARED_ARGS").expect("BLDSHARED_ARGS not set in Makefile?"); + // TODO(emmatyping): Ideally, we would not need to split the args here and take shlex + // as a dependency. + for arg in shlex::split(&build_shared_args).expect("Invalid BUILDSHARED_ARGS") { + println!("cargo:rustc-link-arg={}", arg); + } + } + // Static linker configuration is in cpython-rust-staticlib +} diff --git a/Modules/cpython-rust-staticlib/Cargo.toml b/Modules/cpython-rust-staticlib/Cargo.toml new file mode 100644 index 00000000000000..d7f52b0f3e9602 --- /dev/null +++ b/Modules/cpython-rust-staticlib/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "cpython-rust-staticlib" +version = "0.1.0" +edition = "2024" + +[dependencies] +_base64 ={ path = "../_base64" } + +[lib] +name = "cpython_rust_staticlib" +crate-type = ["staticlib"] diff --git a/Modules/cpython-rust-staticlib/src/lib.rs b/Modules/cpython-rust-staticlib/src/lib.rs new file mode 100644 index 00000000000000..a440707833f65d --- /dev/null +++ b/Modules/cpython-rust-staticlib/src/lib.rs @@ -0,0 +1 @@ +pub use _base64::PyInit__base64; diff --git a/Modules/makesetup b/Modules/makesetup index bd33c8fb2804c7..25b091cde689c1 100755 --- a/Modules/makesetup +++ b/Modules/makesetup @@ -232,7 +232,6 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | yes) continue;; esac objs='' - custom_ldflags='' if test "x$rust" = "x"; then for src in $srcs do @@ -272,6 +271,22 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | esac echo "$rule" >>$rulesf done + case $doconfig in + yes) OBJS="$OBJS $objs";; + esac + for mod in $mods + do + file="$srcdir/$mod\$(EXT_SUFFIX)" + case $doconfig in + no) + SHAREDMODS="$SHAREDMODS $file" + BUILT_SHARED="$BUILT_SHARED $mod" + ;; + esac + rule="$file: $objs \$(MODULE_${mods_upper}_LDEPS)" + rule="$rule; \$(BLDSHARED_EXE) \$(BLDSHARED_ARGS) $objs $libs \$(LIBPYTHON) -o $file" + echo "$rule" >>$rulesf + done else prefixed_srcs= for src in $srcs @@ -279,44 +294,25 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | prefixed_srcs="$prefixed_srcs $srcdir/$src" done objs= - # there's actually only one obj, so just set it to the lib - for lib in $libs - do - objs="target/\$(if \$(CARGO_TARGET),\$(CARGO_TARGET)/\$(CARGO_TARGET_DIR),\$(CARGO_TARGET_DIR))/$lib" - done libs= - # depends on the headers through cpython-sys - rule="$objs: cpython-sys \$(srcdir)/Cargo.toml \$(srcdir)/Cargo.lock \$(srcdir)/$srcdir/$manifest $prefixed_srcs \$(PYTHON_HEADERS)" - rule="$rule; CARGO_TARGET_DIR=\$(abs_builddir)/target PYTHON_BUILD_DIR=\$(abs_builddir) \$(CARGO_HOME)/bin/cargo build --lib --locked --package ${mods} --profile \$(CARGO_PROFILE) \$(if \$(CARGO_TARGET),--target=\$(CARGO_TARGET)) --manifest-path \$(srcdir)/Cargo.toml" - echo "$rule" >>$rulesf + for mod in $mods do - case $UNAME_SYSTEM in - Darwin*) - custom_ldflags="$custom_ldflags -Wl,-u,_PyInit_$mod" - ;; - *) - custom_ldflags="$custom_ldflags -Wl,--defsym=PyInit_$mod=PyInit_$mod" + rust_shared="target/\$(if \$(CARGO_TARGET),\$(CARGO_TARGET)/\$(CARGO_TARGET_DIR),\$(CARGO_TARGET_DIR))/lib$mod\$(SHLIB_SUFFIX)" + file="$srcdir/$mod\$(EXT_SUFFIX)" + case $doconfig in + no) + SHAREDMODS="$SHAREDMODS $file" + BUILT_SHARED="$BUILT_SHARED $mod" ;; esac + # depends on the headers through cpython-sys + rule="$rust_shared: cpython-sys \$(srcdir)/Cargo.toml \$(srcdir)/Cargo.lock \$(srcdir)/$srcdir/$manifest $prefixed_srcs \$(PYTHON_HEADERS) \$(MODULE_${mods_upper}_LDEPS) \$(LIBRARY)" + rule="$rule; CARGO_TARGET_DIR=\$(abs_builddir)/target PYTHON_BUILD_DIR=\$(abs_builddir) RUST_SHARED_BUILD=\$(PY_ENABLE_SHARED) \$(CARGO_HOME)/bin/cargo build -vvv --lib --locked --package ${mod} --profile \$(CARGO_PROFILE) \$(if \$(CARGO_TARGET),--target=\$(CARGO_TARGET)) --manifest-path \$(srcdir)/Cargo.toml" + echo "$rule" >>$rulesf + echo "$file: $rust_shared; mv $rust_shared $file" >>$rulesf done fi - case $doconfig in - yes) OBJS="$OBJS $objs";; - esac - for mod in $mods - do - file="$srcdir/$mod\$(EXT_SUFFIX)" - case $doconfig in - no) - SHAREDMODS="$SHAREDMODS $file" - BUILT_SHARED="$BUILT_SHARED $mod" - ;; - esac - rule="$file: $objs \$(MODULE_${mods_upper}_LDEPS)" - rule="$rule; \$(BLDSHARED) $custom_ldflags $objs $libs \$(LIBPYTHON) -o $file" - echo "$rule" >>$rulesf - done done case $SHAREDMODS in diff --git a/configure b/configure index 5b09de36f9a0b8..911577c444b79c 100755 --- a/configure +++ b/configure @@ -908,7 +908,8 @@ SHLIBS CFLAGSFORSHARED LINKFORSHARED CCSHARED -BLDSHARED +BLDSHARED_ARGS +BLDSHARED_EXE LDCXXSHARED LDSHARED SHLIB_SUFFIX @@ -13503,6 +13504,7 @@ fi + # SHLIB_SUFFIX is the extension of shared libraries `(including the dot!) # -- usually .so, .sl on HP-UX, .dll on Cygwin { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking the extension of shared libraries" >&5 @@ -13532,7 +13534,8 @@ if test -z "$LDSHARED" then case $ac_sys_system/$ac_sys_release in AIX*) - BLDSHARED="Modules/ld_so_aix \$(CC) -bI:Modules/python.exp" + BLDSHARED_EXE="Modules/ld_so_aix" + BLDSHARED_ARGS="\$(CC) -bI:Modules/python.exp" LDSHARED="\$(LIBPL)/ld_so_aix \$(CC) -bI:\$(LIBPL)/python.exp" ;; SunOS/5*) @@ -13556,7 +13559,8 @@ then LDCXXSHARED='$(CXX) -bundle' if test "$enable_framework" ; then # Link against the framework. All externals should be defined. - BLDSHARED="$LDSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' + BLDSHARED_EXE="$LDSHARED" + BLDSHARED_ARGS='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' LDSHARED="$LDSHARED "'$(PYTHONFRAMEWORKPREFIX)/$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' LDCXXSHARED="$LDCXXSHARED "'$(PYTHONFRAMEWORKPREFIX)/$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' else @@ -13567,14 +13571,15 @@ then Darwin/1.4*|Darwin/5.*|Darwin/6.*) LDSHARED='$(CC) -bundle' LDCXXSHARED='$(CXX) -bundle' + BLDSHARED_EXE="$LDSHARED" if test "$enable_framework" ; then # Link against the framework. All externals should be defined. - BLDSHARED="$LDSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' + BLDSHARED_ARGS='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' LDSHARED="$LDSHARED "'$(PYTHONFRAMEWORKPREFIX)/$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' LDCXXSHARED="$LDCXXSHARED "'$(PYTHONFRAMEWORKPREFIX)/$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' else # No framework, use the Python app as bundle-loader - BLDSHARED="$LDSHARED "'-bundle_loader $(BUILDPYTHON)' + BLDSHARED_ARGS='-bundle_loader $(BUILDPYTHON)' LDSHARED="$LDSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' LDCXXSHARED="$LDCXXSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' fi ;; @@ -13595,13 +13600,15 @@ then # building for OS X 10.3 and later LDSHARED='$(CC) -bundle -undefined dynamic_lookup' LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' - BLDSHARED="$LDSHARED" + BLDSHARED_EXE="$LDSHARED" + BLDSHARED_ARGS="" fi ;; iOS/*) LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' - BLDSHARED="$LDSHARED" + BLDSHARED_EXE="$LDSHARED" + BLDSHARED_ARGS="" ;; Emscripten*|WASI*) LDSHARED='$(CC) -shared' @@ -13661,18 +13668,25 @@ fi;; fi if test "$enable_wasm_dynamic_linking" = "yes" -a "$ac_sys_system" = "Emscripten"; then - BLDSHARED='$(CC) -shared -sSIDE_MODULE=1' + BLDSHARED_EXE='$(CC) -shared' + BLDSHARED_ARGS='-sSIDE_MODULE=1' fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LDSHARED" >&5 printf "%s\n" "$LDSHARED" >&6; } LDCXXSHARED=${LDCXXSHARED-$LDSHARED} -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking BLDSHARED flags" >&5 -printf %s "checking BLDSHARED flags... " >&6; } -BLDSHARED=${BLDSHARED-$LDSHARED} -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BLDSHARED" >&5 -printf "%s\n" "$BLDSHARED" >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking BLDSHARED_EXE executable" >&5 +printf %s "checking BLDSHARED_EXE executable... " >&6; } +BLDSHARED_EXE=${BLDSHARED_EXE-$LDSHARED} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BLDSHARED_EXE" >&5 +printf "%s\n" "$BLDSHARED_EXE" >&6; } + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking BLDSHARED_ARGS flags" >&5 +printf %s "checking BLDSHARED_ARGS flags... " >&6; } +BLDSHARED_ARGS=${BLDSHARED_ARGS-} +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $BLDSHARED_ARGS" >&5 +printf "%s\n" "$BLDSHARED_ARGS" >&6; } # CCSHARED are the C *flags* used to create objects to go into a shared # library (module) -- this is only needed for a few systems @@ -16106,6 +16120,8 @@ else CARGO_PROFILE='dev' fi # Set CARGO_TARGET for cross-compilation + # TODO(emmatyping): derive this in platform-triplet.c and set the Rust triple + # higher up case "$host" in aarch64-apple-ios-simulator) CARGO_TARGET="aarch64-apple-ios-sim" @@ -36187,3 +36203,4 @@ if test "$ac_cv_header_stdatomic_h" != "yes"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: Your compiler or platform does have a working C11 stdatomic.h. A future version of Python may require stdatomic.h." >&5 printf "%s\n" "$as_me: Your compiler or platform does have a working C11 stdatomic.h. A future version of Python may require stdatomic.h." >&6;} fi + diff --git a/configure.ac b/configure.ac index 1561c6f9b2e99f..852d7c83718d21 100644 --- a/configure.ac +++ b/configure.ac @@ -3379,7 +3379,8 @@ with_tsan="no" AC_SUBST([SHLIB_SUFFIX]) AC_SUBST([LDSHARED]) AC_SUBST([LDCXXSHARED]) -AC_SUBST([BLDSHARED]) +AC_SUBST([BLDSHARED_EXE]) +AC_SUBST([BLDSHARED_ARGS]) AC_SUBST([CCSHARED]) AC_SUBST([LINKFORSHARED]) @@ -3409,7 +3410,8 @@ if test -z "$LDSHARED" then case $ac_sys_system/$ac_sys_release in AIX*) - BLDSHARED="Modules/ld_so_aix \$(CC) -bI:Modules/python.exp" + BLDSHARED_EXE="Modules/ld_so_aix" + BLDSHARED_ARGS="\$(CC) -bI:Modules/python.exp" LDSHARED="\$(LIBPL)/ld_so_aix \$(CC) -bI:\$(LIBPL)/python.exp" ;; SunOS/5*) @@ -3433,7 +3435,8 @@ then LDCXXSHARED='$(CXX) -bundle' if test "$enable_framework" ; then # Link against the framework. All externals should be defined. - BLDSHARED="$LDSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' + BLDSHARED_EXE="$LDSHARED" + BLDSHARED_ARGS='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' LDSHARED="$LDSHARED "'$(PYTHONFRAMEWORKPREFIX)/$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' LDCXXSHARED="$LDCXXSHARED "'$(PYTHONFRAMEWORKPREFIX)/$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' else @@ -3444,14 +3447,15 @@ then Darwin/1.4*|Darwin/5.*|Darwin/6.*) LDSHARED='$(CC) -bundle' LDCXXSHARED='$(CXX) -bundle' + BLDSHARED_EXE="$LDSHARED" if test "$enable_framework" ; then # Link against the framework. All externals should be defined. - BLDSHARED="$LDSHARED "'$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' + BLDSHARED_ARGS='$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' LDSHARED="$LDSHARED "'$(PYTHONFRAMEWORKPREFIX)/$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' LDCXXSHARED="$LDCXXSHARED "'$(PYTHONFRAMEWORKPREFIX)/$(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK)' else # No framework, use the Python app as bundle-loader - BLDSHARED="$LDSHARED "'-bundle_loader $(BUILDPYTHON)' + BLDSHARED_ARGS='-bundle_loader $(BUILDPYTHON)' LDSHARED="$LDSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' LDCXXSHARED="$LDCXXSHARED "'-bundle_loader $(BINDIR)/python$(VERSION)$(EXE)' fi ;; @@ -3472,13 +3476,15 @@ then # building for OS X 10.3 and later LDSHARED='$(CC) -bundle -undefined dynamic_lookup' LDCXXSHARED='$(CXX) -bundle -undefined dynamic_lookup' - BLDSHARED="$LDSHARED" + BLDSHARED_EXE="$LDSHARED" + BLDSHARED_ARGS="" fi ;; iOS/*) LDSHARED='$(CC) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' LDCXXSHARED='$(CXX) -dynamiclib -F . -framework $(PYTHONFRAMEWORK)' - BLDSHARED="$LDSHARED" + BLDSHARED_EXE="$LDSHARED" + BLDSHARED_ARGS="" ;; Emscripten*|WASI*) LDSHARED='$(CC) -shared' @@ -3535,18 +3541,24 @@ then esac fi -dnl Emscripten's emconfigure sets LDSHARED. Set BLDSHARED outside the -dnl test -z $LDSHARED block to configure BLDSHARED for side module support. +dnl Emscripten's emconfigure sets LDSHARED. Set BLDSHARED_EXE and BLDSHARED_ARGS +dnl outside the test -z $LDSHARED block to configure BLDSHARED for side module +dnl support. if test "$enable_wasm_dynamic_linking" = "yes" -a "$ac_sys_system" = "Emscripten"; then - BLDSHARED='$(CC) -shared -sSIDE_MODULE=1' + BLDSHARED_EXE='$(CC) -shared' + BLDSHARED_ARGS='-sSIDE_MODULE=1' fi AC_MSG_RESULT([$LDSHARED]) LDCXXSHARED=${LDCXXSHARED-$LDSHARED} -AC_MSG_CHECKING([BLDSHARED flags]) -BLDSHARED=${BLDSHARED-$LDSHARED} -AC_MSG_RESULT([$BLDSHARED]) +AC_MSG_CHECKING([BLDSHARED_EXE executable]) +BLDSHARED_EXE=${BLDSHARED_EXE-$LDSHARED} +AC_MSG_RESULT([$BLDSHARED_EXE]) + +AC_MSG_CHECKING([BLDSHARED_ARGS flags]) +BLDSHARED_ARGS=${BLDSHARED_ARGS-} +AC_MSG_RESULT([$BLDSHARED_ARGS]) # CCSHARED are the C *flags* used to create objects to go into a shared # library (module) -- this is only needed for a few systems @@ -4329,6 +4341,8 @@ else CARGO_PROFILE='dev' fi # Set CARGO_TARGET for cross-compilation + # TODO(emmatyping): derive this in platform-triplet.c and set the Rust triple + # higher up case "$host" in aarch64-apple-ios-simulator) CARGO_TARGET="aarch64-apple-ios-sim" From 363f9acf35461ae5e265608188b149e864eec875 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Wed, 4 Feb 2026 05:15:23 +0000 Subject: [PATCH 02/21] Also pass whether or not the build is shared to cpython-sys --- Cargo.lock | 1 + Makefile.pre.in | 2 +- Modules/cpython-sys/Cargo.toml | 3 ++- Modules/cpython-sys/build.rs | 3 +++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2080d8d0da80b..f6a41e2ba1b555 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,7 @@ name = "cpython-sys" version = "0.1.0" dependencies = [ "bindgen", + "cpython-build-helper", ] [[package]] diff --git a/Makefile.pre.in b/Makefile.pre.in index 0f2553f1fcf737..d41e5c1c320aa6 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -3375,7 +3375,7 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h # Module dependencies and platform-specific files cpython-sys: Modules/cpython-sys/Cargo.toml Modules/cpython-sys/build.rs Modules/cpython-sys/wrapper.h Modules/cpython-sys/parser.h - CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) \$(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml + CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) RUST_SHARED_BUILD=\$(PY_ENABLE_SHARED) \$(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml # force rebuild when header file or module build flavor (static/shared) is changed MODULE_DEPS_STATIC=Modules/config.c diff --git a/Modules/cpython-sys/Cargo.toml b/Modules/cpython-sys/Cargo.toml index de0b88d5d5b723..f0e9b5bc888b79 100644 --- a/Modules/cpython-sys/Cargo.toml +++ b/Modules/cpython-sys/Cargo.toml @@ -6,4 +6,5 @@ edition = "2024" [dependencies] [build-dependencies] -bindgen = "0.72.1" \ No newline at end of file +bindgen = "0.72.1" +cpython-build-helper = { path = "../cpython-build-helper" } \ No newline at end of file diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index a681e498171675..ae71f9c0aebe4b 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -1,7 +1,10 @@ use std::env; use std::path::{Path, PathBuf}; +use cpython_build_helper::print_linker_args; + fn main() { + print_linker_args(); let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let srcdir = manifest_dir .parent() From e40d3aa4114c7b10dff5ae45632bde70e694a521 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Sun, 1 Mar 2026 21:51:39 +0000 Subject: [PATCH 03/21] Properly pass linker arguments and set up static library building --- Cargo.lock | 1 - Makefile.pre.in | 11 +++++++++-- Modules/_base64/src/lib.rs | 3 --- Modules/cpython-sys/Cargo.toml | 3 +-- Modules/cpython-sys/build.rs | 3 --- Modules/makesetup | 36 +++++++++++++++++++++------------- configure | 15 ++++++++++++++ configure.ac | 17 ++++++++++++++++ 8 files changed, 64 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f6a41e2ba1b555..b2080d8d0da80b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,7 +90,6 @@ name = "cpython-sys" version = "0.1.0" dependencies = [ "bindgen", - "cpython-build-helper", ] [[package]] diff --git a/Makefile.pre.in b/Makefile.pre.in index d41e5c1c320aa6..a26bff47da7ef9 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -62,6 +62,8 @@ CARGO_HOME=@CARGO_HOME@ CARGO_TARGET_DIR=@CARGO_TARGET_DIR@ CARGO_PROFILE=@CARGO_PROFILE@ CARGO_TARGET=@CARGO_TARGET@ +CARGO_TARGET_LINKER_ENV=@CARGO_TARGET_LINKER_ENV@ +RUST_STATICLIB_DEP=@RUST_STATICLIB_DEP@ GNULD= @GNULD@ @@ -989,7 +991,7 @@ clinic-tests: check-clean-src $(srcdir)/Lib/test/clinic.test.c $(PYTHON_FOR_REGEN) $(srcdir)/Tools/clinic/clinic.py -f $(srcdir)/Lib/test/clinic.test.c # Build the interpreter -$(BUILDPYTHON): Programs/python.o $(LINK_PYTHON_DEPS) +$(BUILDPYTHON): Programs/python.o $(LINK_PYTHON_DEPS) $(RUST_STATICLIB_DEP) $(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/python.o $(LINK_PYTHON_OBJS) $(LIBS) $(MODLIBS) $(SYSLIBS) platform: $(PYTHON_FOR_BUILD_DEPS) pybuilddir.txt @@ -3375,7 +3377,12 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h # Module dependencies and platform-specific files cpython-sys: Modules/cpython-sys/Cargo.toml Modules/cpython-sys/build.rs Modules/cpython-sys/wrapper.h Modules/cpython-sys/parser.h - CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) RUST_SHARED_BUILD=\$(PY_ENABLE_SHARED) \$(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml + CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) $(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml + +RUST_STATICLIB_A= target/$(if $(CARGO_TARGET),$(CARGO_TARGET)/$(CARGO_TARGET_DIR),$(CARGO_TARGET_DIR))/libcpython_rust_staticlib.a + +cpython-rust-staticlib: cpython-sys + CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) $(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-rust-staticlib --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml # force rebuild when header file or module build flavor (static/shared) is changed MODULE_DEPS_STATIC=Modules/config.c diff --git a/Modules/_base64/src/lib.rs b/Modules/_base64/src/lib.rs index 7e13438ca21c6f..64e4d718789372 100644 --- a/Modules/_base64/src/lib.rs +++ b/Modules/_base64/src/lib.rs @@ -115,7 +115,6 @@ impl Drop for BorrowedBuffer { /// # Safety /// `module` must be a valid pointer of PyObject representing the module. /// `args` must be a valid pointer to an array of valid PyObject pointers with length `nargs`. -#[unsafe(no_mangle)] pub unsafe extern "C" fn standard_b64encode( _module: *mut PyObject, args: *mut *mut PyObject, @@ -193,13 +192,11 @@ fn standard_b64encode_impl(source: &PyObject) -> Result<*mut PyObject, ()> { Ok(result) } -#[unsafe(no_mangle)] pub extern "C" fn _base64_clear(_obj: *mut PyObject) -> c_int { //TODO 0 } -#[unsafe(no_mangle)] pub extern "C" fn _base64_free(_o: *mut c_void) { //TODO } diff --git a/Modules/cpython-sys/Cargo.toml b/Modules/cpython-sys/Cargo.toml index f0e9b5bc888b79..de0b88d5d5b723 100644 --- a/Modules/cpython-sys/Cargo.toml +++ b/Modules/cpython-sys/Cargo.toml @@ -6,5 +6,4 @@ edition = "2024" [dependencies] [build-dependencies] -bindgen = "0.72.1" -cpython-build-helper = { path = "../cpython-build-helper" } \ No newline at end of file +bindgen = "0.72.1" \ No newline at end of file diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index ae71f9c0aebe4b..a681e498171675 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -1,10 +1,7 @@ use std::env; use std::path::{Path, PathBuf}; -use cpython_build_helper::print_linker_args; - fn main() { - print_linker_args(); let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let srcdir = manifest_dir .parent() diff --git a/Modules/makesetup b/Modules/makesetup index 25b091cde689c1..c8e68e96c34880 100755 --- a/Modules/makesetup +++ b/Modules/makesetup @@ -296,22 +296,30 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | objs= libs= - for mod in $mods - do - rust_shared="target/\$(if \$(CARGO_TARGET),\$(CARGO_TARGET)/\$(CARGO_TARGET_DIR),\$(CARGO_TARGET_DIR))/lib$mod\$(SHLIB_SUFFIX)" - file="$srcdir/$mod\$(EXT_SUFFIX)" - case $doconfig in - no) + case $doconfig in + no) + # Shared build: build each Rust module as a cdylib (.so) + for mod in $mods + do + rust_shared="target/\$(if \$(CARGO_TARGET),\$(CARGO_TARGET)/\$(CARGO_TARGET_DIR),\$(CARGO_TARGET_DIR))/lib$mod\$(SHLIB_SUFFIX)" + file="$srcdir/$mod\$(EXT_SUFFIX)" SHAREDMODS="$SHAREDMODS $file" BUILT_SHARED="$BUILT_SHARED $mod" - ;; - esac - # depends on the headers through cpython-sys - rule="$rust_shared: cpython-sys \$(srcdir)/Cargo.toml \$(srcdir)/Cargo.lock \$(srcdir)/$srcdir/$manifest $prefixed_srcs \$(PYTHON_HEADERS) \$(MODULE_${mods_upper}_LDEPS) \$(LIBRARY)" - rule="$rule; CARGO_TARGET_DIR=\$(abs_builddir)/target PYTHON_BUILD_DIR=\$(abs_builddir) RUST_SHARED_BUILD=\$(PY_ENABLE_SHARED) \$(CARGO_HOME)/bin/cargo build -vvv --lib --locked --package ${mod} --profile \$(CARGO_PROFILE) \$(if \$(CARGO_TARGET),--target=\$(CARGO_TARGET)) --manifest-path \$(srcdir)/Cargo.toml" - echo "$rule" >>$rulesf - echo "$file: $rust_shared; mv $rust_shared $file" >>$rulesf - done + # depends on the headers through cpython-sys + rule="$rust_shared: cpython-sys \$(srcdir)/Cargo.toml \$(srcdir)/Cargo.lock \$(srcdir)/$srcdir/$manifest $prefixed_srcs \$(PYTHON_HEADERS) \$(MODULE_${mods_upper}_LDEPS) \$(LIBRARY)" + rule="$rule; CARGO_TARGET_DIR=\$(abs_builddir)/target PYTHON_BUILD_DIR=\$(abs_builddir) RUST_SHARED_BUILD=\$(PY_ENABLE_SHARED) BLDSHARED_ARGS=\"\$(BLDSHARED_ARGS)\" \$(if \$(CARGO_TARGET_LINKER_ENV),\$(CARGO_TARGET_LINKER_ENV)=\$(CC)) \$(CARGO_HOME)/bin/cargo build -vvv --lib --locked --package ${mod} --profile \$(CARGO_PROFILE) \$(if \$(CARGO_TARGET),--target=\$(CARGO_TARGET)) --manifest-path \$(srcdir)/Cargo.toml" + echo "$rule" >>$rulesf + echo "$file: $rust_shared; mv $rust_shared $file" >>$rulesf + done + ;; + yes) + # Static build: Rust modules are built as part of cpython-rust-staticlib. + # The dependency on cpython-rust-staticlib is in Makefile.pre.in + # (via RUST_STATICLIB_DEP on the BUILDPYTHON rule). + rust_staticlib="target/\$(if \$(CARGO_TARGET),\$(CARGO_TARGET)/\$(CARGO_TARGET_DIR),\$(CARGO_TARGET_DIR))/libcpython_rust_staticlib.a" + LIBS="$LIBS $rust_staticlib" + ;; + esac fi done diff --git a/configure b/configure index 911577c444b79c..944c18f499eecd 100755 --- a/configure +++ b/configure @@ -824,6 +824,7 @@ MODULE_TIME_FALSE MODULE_TIME_TRUE MODULE__IO_FALSE MODULE__IO_TRUE +RUST_STATICLIB_DEP MODULE_BUILDTYPE _PYTHREAD_NAME_MAXLEN TEST_MODULES @@ -887,6 +888,7 @@ TCLTK_LIBS TCLTK_CFLAGS LIBSQLITE3_LIBS LIBSQLITE3_CFLAGS +CARGO_TARGET_LINKER_ENV CARGO_TARGET CARGO_PROFILE CARGO_TARGET_DIR @@ -16143,6 +16145,12 @@ else ;; esac fi +if test -n "$CARGO_TARGET"; then + CARGO_TARGET_LINKER_ENV="CARGO_TARGET_$(echo "$CARGO_TARGET" | tr 'a-z-' 'A-Z_')_LINKER" +else + CARGO_TARGET_LINKER_ENV="" +fi + @@ -31378,6 +31386,13 @@ case $host_cpu in #( esac +if test "$HAVE_CARGO" != "no" -a "$MODULE_BUILDTYPE" = "static"; then + RUST_STATICLIB_DEP=cpython-rust-staticlib +else + RUST_STATICLIB_DEP= +fi + + MODULE_BLOCK= diff --git a/configure.ac b/configure.ac index 852d7c83718d21..2eec993e841cb0 100644 --- a/configure.ac +++ b/configure.ac @@ -4364,10 +4364,18 @@ else ;; esac fi +dnl Compute the CARGO_TARGET__LINKER env var name so we can +dnl tell Cargo which linker to use for the target platform. +if test -n "$CARGO_TARGET"; then + CARGO_TARGET_LINKER_ENV="CARGO_TARGET_$(echo "$CARGO_TARGET" | tr 'a-z-' 'A-Z_')_LINKER" +else + CARGO_TARGET_LINKER_ENV="" +fi AC_SUBST([CARGO_HOME]) AC_SUBST([CARGO_TARGET_DIR]) AC_SUBST([CARGO_PROFILE]) AC_SUBST([CARGO_TARGET]) +AC_SUBST([CARGO_TARGET_LINKER_ENV]) dnl detect sqlite3 from Emscripten emport @@ -7882,6 +7890,15 @@ AS_CASE([$host_cpu], ) AC_SUBST([MODULE_BUILDTYPE]) +dnl Set RUST_STATICLIB_DEP to cpython-rust-staticlib when building Rust +dnl modules statically so that the python binary depends on it. +if test "$HAVE_CARGO" != "no" -a "$MODULE_BUILDTYPE" = "static"; then + RUST_STATICLIB_DEP=cpython-rust-staticlib +else + RUST_STATICLIB_DEP= +fi +AC_SUBST([RUST_STATICLIB_DEP]) + dnl _MODULE_BLOCK_ADD([VAR], [VALUE]) dnl internal: adds $1=quote($2) to MODULE_BLOCK AC_DEFUN([_MODULE_BLOCK_ADD], [AS_VAR_APPEND([MODULE_BLOCK], ["$1=_AS_QUOTE([$2])$as_nl"])]) From e24aa53e5112ed3b7c430c89a015ea1d456eea47 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Sun, 1 Mar 2026 22:04:45 +0000 Subject: [PATCH 04/21] Modify build helper to handle differences in how Python symbols are linked in --- Modules/cpython-build-helper/src/lib.rs | 43 ++++++++++++++++++++----- Modules/makesetup | 2 +- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Modules/cpython-build-helper/src/lib.rs b/Modules/cpython-build-helper/src/lib.rs index f9fc5b2e77fc46..1f96750afa3d81 100644 --- a/Modules/cpython-build-helper/src/lib.rs +++ b/Modules/cpython-build-helper/src/lib.rs @@ -3,15 +3,42 @@ use std::env; /// Print necessary link arguments for the library depending on the build /// configuration (static or shared) pub fn print_linker_args() { - let shared_build = env::var("RUST_SHARED_BUILD").expect("RUST_SHARED_BUILD not set in Makefile?"); - if shared_build == "1" { - let build_shared_args = - env::var("BLDSHARED_ARGS").expect("BLDSHARED_ARGS not set in Makefile?"); - // TODO(emmatyping): Ideally, we would not need to split the args here and take shlex - // as a dependency. - for arg in shlex::split(&build_shared_args).expect("Invalid BUILDSHARED_ARGS") { - println!("cargo:rustc-link-arg={}", arg); + let target = env::var("TARGET").unwrap_or_default(); + + // On Apple platforms (macOS, iOS), Cargo's cdylib produces a Mach-O + // dynamiclib (via -dynamiclib), but CPython's C extensions are built as + // bundles (via -bundle). Unlike bundles, dynamiclibs require all symbols + // to be resolved at link time. Pass -undefined dynamic_lookup so that + // Python C API symbols are resolved at load time by the interpreter. + if target.contains("apple") { + println!("cargo:rustc-cdylib-link-arg=-undefined"); + println!("cargo:rustc-cdylib-link-arg=dynamic_lookup"); + } + + // Pass platform-specific shared link arguments (e.g. PY_CORE_LDFLAGS) + // from the CPython build system. + if let Ok(args) = env::var("BLDSHARED_ARGS") { + let args = shlex::split(&args).expect("Invalid BLDSHARED_ARGS"); + let mut iter = args.iter(); + while let Some(arg) = iter.next() { + // -bundle_loader is incompatible with Cargo's cdylib on macOS + // (it only works with -bundle, not -dynamiclib). Skip it and + // its argument. + if arg == "-bundle_loader" { + iter.next(); // skip the path argument + continue; + } + println!("cargo:rustc-cdylib-link-arg={}", arg); } } + + // On Android (and Cygwin), extension modules must link against libpython. + // LIBPYTHON is set by the CPython build system on these platforms. + if let Ok(libpython) = env::var("LIBPYTHON") { + for arg in shlex::split(&libpython).expect("Invalid LIBPYTHON") { + println!("cargo:rustc-cdylib-link-arg={}", arg); + } + } + // Static linker configuration is in cpython-rust-staticlib } diff --git a/Modules/makesetup b/Modules/makesetup index c8e68e96c34880..27758a133b65eb 100755 --- a/Modules/makesetup +++ b/Modules/makesetup @@ -307,7 +307,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | BUILT_SHARED="$BUILT_SHARED $mod" # depends on the headers through cpython-sys rule="$rust_shared: cpython-sys \$(srcdir)/Cargo.toml \$(srcdir)/Cargo.lock \$(srcdir)/$srcdir/$manifest $prefixed_srcs \$(PYTHON_HEADERS) \$(MODULE_${mods_upper}_LDEPS) \$(LIBRARY)" - rule="$rule; CARGO_TARGET_DIR=\$(abs_builddir)/target PYTHON_BUILD_DIR=\$(abs_builddir) RUST_SHARED_BUILD=\$(PY_ENABLE_SHARED) BLDSHARED_ARGS=\"\$(BLDSHARED_ARGS)\" \$(if \$(CARGO_TARGET_LINKER_ENV),\$(CARGO_TARGET_LINKER_ENV)=\$(CC)) \$(CARGO_HOME)/bin/cargo build -vvv --lib --locked --package ${mod} --profile \$(CARGO_PROFILE) \$(if \$(CARGO_TARGET),--target=\$(CARGO_TARGET)) --manifest-path \$(srcdir)/Cargo.toml" + rule="$rule; CARGO_TARGET_DIR=\$(abs_builddir)/target PYTHON_BUILD_DIR=\$(abs_builddir) BLDSHARED_ARGS=\"\$(BLDSHARED_ARGS)\" LIBPYTHON=\"\$(LIBPYTHON)\" \$(if \$(CARGO_TARGET_LINKER_ENV),\$(CARGO_TARGET_LINKER_ENV)=\$(CC)) \$(CARGO_HOME)/bin/cargo build -vvv --lib --locked --package ${mod} --profile \$(CARGO_PROFILE) \$(if \$(CARGO_TARGET),--target=\$(CARGO_TARGET)) --manifest-path \$(srcdir)/Cargo.toml" echo "$rule" >>$rulesf echo "$file: $rust_shared; mv $rust_shared $file" >>$rulesf done From 025f3fcd99f5b47cc13d50f77dccff2f47a19318 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Sun, 1 Mar 2026 22:18:47 +0000 Subject: [PATCH 05/21] Modify cdylib suffix on macOS --- Makefile.pre.in | 1 + Modules/makesetup | 2 +- configure | 6 ++++++ configure.ac | 8 ++++++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index a26bff47da7ef9..33dc645fb9c230 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -63,6 +63,7 @@ CARGO_TARGET_DIR=@CARGO_TARGET_DIR@ CARGO_PROFILE=@CARGO_PROFILE@ CARGO_TARGET=@CARGO_TARGET@ CARGO_TARGET_LINKER_ENV=@CARGO_TARGET_LINKER_ENV@ +CARGO_DYLIB_SUFFIX=@CARGO_DYLIB_SUFFIX@ RUST_STATICLIB_DEP=@RUST_STATICLIB_DEP@ GNULD= @GNULD@ diff --git a/Modules/makesetup b/Modules/makesetup index 27758a133b65eb..4b5085d843f1e7 100755 --- a/Modules/makesetup +++ b/Modules/makesetup @@ -301,7 +301,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | # Shared build: build each Rust module as a cdylib (.so) for mod in $mods do - rust_shared="target/\$(if \$(CARGO_TARGET),\$(CARGO_TARGET)/\$(CARGO_TARGET_DIR),\$(CARGO_TARGET_DIR))/lib$mod\$(SHLIB_SUFFIX)" + rust_shared="target/\$(if \$(CARGO_TARGET),\$(CARGO_TARGET)/\$(CARGO_TARGET_DIR),\$(CARGO_TARGET_DIR))/lib$mod\$(CARGO_DYLIB_SUFFIX)" file="$srcdir/$mod\$(EXT_SUFFIX)" SHAREDMODS="$SHAREDMODS $file" BUILT_SHARED="$BUILT_SHARED $mod" diff --git a/configure b/configure index 944c18f499eecd..00b93c838a4bb3 100755 --- a/configure +++ b/configure @@ -888,6 +888,7 @@ TCLTK_LIBS TCLTK_CFLAGS LIBSQLITE3_LIBS LIBSQLITE3_CFLAGS +CARGO_DYLIB_SUFFIX CARGO_TARGET_LINKER_ENV CARGO_TARGET CARGO_PROFILE @@ -16150,6 +16151,11 @@ if test -n "$CARGO_TARGET"; then else CARGO_TARGET_LINKER_ENV="" fi +case "$host" in + *-apple-*) CARGO_DYLIB_SUFFIX=".dylib";; + *) CARGO_DYLIB_SUFFIX="$SHLIB_SUFFIX";; +esac + diff --git a/configure.ac b/configure.ac index 2eec993e841cb0..ccdd55802ba2bb 100644 --- a/configure.ac +++ b/configure.ac @@ -4371,11 +4371,19 @@ if test -n "$CARGO_TARGET"; then else CARGO_TARGET_LINKER_ENV="" fi +dnl Cargo's cdylib crate type uses the platform's native shared library +dnl extension, which differs from CPython's SHLIB_SUFFIX on macOS/iOS +dnl (CPython uses .so, Cargo uses .dylib). +case "$host" in + *-apple-*) CARGO_DYLIB_SUFFIX=".dylib";; + *) CARGO_DYLIB_SUFFIX="$SHLIB_SUFFIX";; +esac AC_SUBST([CARGO_HOME]) AC_SUBST([CARGO_TARGET_DIR]) AC_SUBST([CARGO_PROFILE]) AC_SUBST([CARGO_TARGET]) AC_SUBST([CARGO_TARGET_LINKER_ENV]) +AC_SUBST([CARGO_DYLIB_SUFFIX]) dnl detect sqlite3 from Emscripten emport From 5ce8a5fd2e20e88ca36ef0e4d091ffdcfce3f696 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 04:31:55 +0000 Subject: [PATCH 06/21] Calculate Rust target from Misc/platform_triplet.c --- Misc/platform_triplet.c | 90 +++++++++++++++++++++++++++++++++++++++++ configure | 34 +++++----------- configure.ac | 34 +++++----------- 3 files changed, 112 insertions(+), 46 deletions(-) diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c index f5cd73bdea8333..13da020b84f2e3 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c @@ -1,5 +1,8 @@ /* Detect platform triplet from builtin defines * cc -E Misc/platform_triplet.c | grep '^PLATFORM_TRIPLET=' | tr -d ' ' + * + * Also detects the closest Rust target triple for cargo/rustc: + * cc -E Misc/platform_triplet.c | grep '^RUST_TARGET=' | tr -d ' ' */ #undef bfin #undef cris @@ -16,12 +19,16 @@ #if defined(__ANDROID__) # if defined(__x86_64__) PLATFORM_TRIPLET=x86_64-linux-android +RUST_TARGET=x86_64-linux-android # elif defined(__i386__) PLATFORM_TRIPLET=i686-linux-android +RUST_TARGET=i686-linux-android # elif defined(__aarch64__) PLATFORM_TRIPLET=aarch64-linux-android +RUST_TARGET=aarch64-linux-android # elif defined(__arm__) PLATFORM_TRIPLET=arm-linux-androideabi +RUST_TARGET=arm-linux-androideabi # else # error unknown Android platform # endif @@ -39,18 +46,25 @@ PLATFORM_TRIPLET=arm-linux-androideabi # elif defined(__GLIBC__) # define LIBC gnu # define LIBC_X32 gnux32 +# define RUST_LIBC gnu +# define RUST_LIBC_X32 gnux32 # if defined(__ARM_PCS_VFP) # define LIBC_ARM gnueabihf +# define RUST_LIBC_ARM gnueabihf # else # define LIBC_ARM gnueabi +# define RUST_LIBC_ARM gnueabi # endif # if defined(__loongarch__) # if defined(__loongarch_soft_float) # define LIBC_LA gnusf +# define RUST_LIBC_LA unknown # elif defined(__loongarch_single_float) # define LIBC_LA gnuf32 +# define RUST_LIBC_LA unknown # elif defined(__loongarch_double_float) # define LIBC_LA gnu +# define RUST_LIBC_LA gnu # else # error unknown loongarch floating-point base abi # endif @@ -59,20 +73,26 @@ PLATFORM_TRIPLET=arm-linux-androideabi # if defined(__mips_hard_float) # if defined(_ABIO32) && _MIPS_SIM == _ABIO32 # define LIBC_MIPS gnu +# define RUST_LIBC_MIPS gnu # elif defined(_ABIN32) && _MIPS_SIM == _ABIN32 # define LIBC_MIPS gnuabin32 +# define RUST_LIBC_MIPS unknown # elif defined(_ABI64) && _MIPS_SIM == _ABI64 # define LIBC_MIPS gnuabi64 +# define RUST_LIBC_MIPS gnuabi64 # else # error unknown mips sim value # endif # else # if defined(_ABIO32) && _MIPS_SIM == _ABIO32 # define LIBC_MIPS gnusf +# define RUST_LIBC_MIPS unknown # elif defined(_ABIN32) && _MIPS_SIM == _ABIN32 # define LIBC_MIPS gnuabin32sf +# define RUST_LIBC_MIPS unknown # elif defined(_ABI64) && _MIPS_SIM == _ABI64 # define LIBC_MIPS gnuabi64sf +# define RUST_LIBC_MIPS unknown # else # error unknown mips sim value # endif @@ -80,8 +100,10 @@ PLATFORM_TRIPLET=arm-linux-androideabi # endif # if defined(__SPE__) # define LIBC_PPC gnuspe +# define RUST_LIBC_PPC gnuspe # else # define LIBC_PPC gnu +# define RUST_LIBC_PPC gnu # endif # else // Heuristic to detect musl libc @@ -89,18 +111,25 @@ PLATFORM_TRIPLET=arm-linux-androideabi # ifdef __DEFINED_va_list # define LIBC musl # define LIBC_X32 muslx32 +# define RUST_LIBC musl +# define RUST_LIBC_X32 unknown # if defined(__ARM_PCS_VFP) # define LIBC_ARM musleabihf +# define RUST_LIBC_ARM musleabihf # else # define LIBC_ARM musleabi +# define RUST_LIBC_ARM musleabi # endif # if defined(__loongarch__) # if defined(__loongarch_soft_float) # define LIBC_LA muslsf +# define RUST_LIBC_LA unknown # elif defined(__loongarch_single_float) # define LIBC_LA muslf32 +# define RUST_LIBC_LA unknown # elif defined(__loongarch_double_float) # define LIBC_LA musl +# define RUST_LIBC_LA musl # else # error unknown loongarch floating-point base abi # endif @@ -109,20 +138,26 @@ PLATFORM_TRIPLET=arm-linux-androideabi # if defined(__mips_hard_float) # if defined(_ABIO32) && _MIPS_SIM == _ABIO32 # define LIBC_MIPS musl +# define RUST_LIBC_MIPS musl # elif defined(_ABIN32) && _MIPS_SIM == _ABIN32 # define LIBC_MIPS musln32 +# define RUST_LIBC_MIPS unknown # elif defined(_ABI64) && _MIPS_SIM == _ABI64 # define LIBC_MIPS musl +# define RUST_LIBC_MIPS muslabi64 # else # error unknown mips sim value # endif # else # if defined(_ABIO32) && _MIPS_SIM == _ABIO32 # define LIBC_MIPS muslsf +# define RUST_LIBC_MIPS unknown # elif defined(_ABIN32) && _MIPS_SIM == _ABIN32 # define LIBC_MIPS musln32sf +# define RUST_LIBC_MIPS unknown # elif defined(_ABI64) && _MIPS_SIM == _ABI64 # define LIBC_MIPS muslsf +# define RUST_LIBC_MIPS unknown # else # error unknown mips sim value # endif @@ -130,8 +165,10 @@ PLATFORM_TRIPLET=arm-linux-androideabi # endif # if defined(_SOFT_FLOAT) || defined(__NO_FPRS__) # define LIBC_PPC muslsf +# define RUST_LIBC_PPC unknown # else # define LIBC_PPC musl +# define RUST_LIBC_PPC musl # endif # else # error unknown libc @@ -140,85 +177,118 @@ PLATFORM_TRIPLET=arm-linux-androideabi # if defined(__x86_64__) && defined(__LP64__) PLATFORM_TRIPLET=x86_64-linux-LIBC +RUST_TARGET=x86_64-unknown-linux-RUST_LIBC # elif defined(__x86_64__) && defined(__ILP32__) PLATFORM_TRIPLET=x86_64-linux-LIBC_X32 +RUST_TARGET=x86_64-unknown-linux-RUST_LIBC_X32 # elif defined(__i386__) PLATFORM_TRIPLET=i386-linux-LIBC +RUST_TARGET=i686-unknown-linux-RUST_LIBC # elif defined(__aarch64__) && defined(__AARCH64EL__) # if defined(__ILP32__) PLATFORM_TRIPLET=aarch64_ilp32-linux-LIBC +RUST_TARGET=unknown # else PLATFORM_TRIPLET=aarch64-linux-LIBC +RUST_TARGET=aarch64-unknown-linux-RUST_LIBC # endif # elif defined(__aarch64__) && defined(__AARCH64EB__) # if defined(__ILP32__) PLATFORM_TRIPLET=aarch64_be_ilp32-linux-LIBC +RUST_TARGET=unknown # else PLATFORM_TRIPLET=aarch64_be-linux-LIBC +RUST_TARGET=aarch64_be-unknown-linux-RUST_LIBC # endif # elif defined(__alpha__) PLATFORM_TRIPLET=alpha-linux-LIBC +RUST_TARGET=unknown # elif defined(__ARM_EABI__) # if defined(__ARMEL__) PLATFORM_TRIPLET=arm-linux-LIBC_ARM +RUST_TARGET=arm-unknown-linux-RUST_LIBC_ARM # else PLATFORM_TRIPLET=armeb-linux-LIBC_ARM +RUST_TARGET=armeb-unknown-linux-RUST_LIBC_ARM # endif # elif defined(__hppa__) PLATFORM_TRIPLET=hppa-linux-LIBC +RUST_TARGET=unknown # elif defined(__ia64__) PLATFORM_TRIPLET=ia64-linux-LIBC +RUST_TARGET=unknown # elif defined(__loongarch__) && defined(__loongarch_lp64) PLATFORM_TRIPLET=loongarch64-linux-LIBC_LA +RUST_TARGET=loongarch64-unknown-linux-RUST_LIBC_LA # elif defined(__m68k__) && !defined(__mcoldfire__) PLATFORM_TRIPLET=m68k-linux-LIBC +RUST_TARGET=m68k-unknown-linux-RUST_LIBC # elif defined(__mips__) # if defined(__mips_isa_rev) && (__mips_isa_rev >=6) # if defined(_MIPSEL) && defined(__mips64) PLATFORM_TRIPLET=mipsisa64r6el-linux-LIBC_MIPS +RUST_TARGET=mipsisa64r6el-unknown-linux-RUST_LIBC_MIPS # elif defined(_MIPSEL) PLATFORM_TRIPLET=mipsisa32r6el-linux-LIBC_MIPS +RUST_TARGET=mipsisa32r6el-unknown-linux-RUST_LIBC_MIPS # elif defined(__mips64) PLATFORM_TRIPLET=mipsisa64r6-linux-LIBC_MIPS +RUST_TARGET=mipsisa64r6-unknown-linux-RUST_LIBC_MIPS # else PLATFORM_TRIPLET=mipsisa32r6-linux-LIBC_MIPS +RUST_TARGET=mipsisa32r6-unknown-linux-RUST_LIBC_MIPS # endif # else # if defined(_MIPSEL) && defined(__mips64) PLATFORM_TRIPLET=mips64el-linux-LIBC_MIPS +RUST_TARGET=mips64el-unknown-linux-RUST_LIBC_MIPS # elif defined(_MIPSEL) PLATFORM_TRIPLET=mipsel-linux-LIBC_MIPS +RUST_TARGET=mipsel-unknown-linux-RUST_LIBC_MIPS # elif defined(__mips64) PLATFORM_TRIPLET=mips64-linux-LIBC_MIPS +RUST_TARGET=mips64-unknown-linux-RUST_LIBC_MIPS # else PLATFORM_TRIPLET=mips-linux-LIBC_MIPS +RUST_TARGET=mips-unknown-linux-RUST_LIBC_MIPS # endif # endif # elif defined(__or1k__) PLATFORM_TRIPLET=or1k-linux-LIBC +RUST_TARGET=unknown # elif defined(__powerpc64__) # if defined(__LITTLE_ENDIAN__) PLATFORM_TRIPLET=powerpc64le-linux-LIBC +RUST_TARGET=powerpc64le-unknown-linux-RUST_LIBC # else PLATFORM_TRIPLET=powerpc64-linux-LIBC +RUST_TARGET=powerpc64-unknown-linux-RUST_LIBC # endif # elif defined(__powerpc__) PLATFORM_TRIPLET=powerpc-linux-LIBC_PPC +RUST_TARGET=powerpc-unknown-linux-RUST_LIBC_PPC # elif defined(__s390x__) PLATFORM_TRIPLET=s390x-linux-LIBC +RUST_TARGET=s390x-unknown-linux-RUST_LIBC # elif defined(__s390__) PLATFORM_TRIPLET=s390-linux-LIBC +RUST_TARGET=unknown # elif defined(__sh__) && defined(__LITTLE_ENDIAN__) PLATFORM_TRIPLET=sh4-linux-LIBC +RUST_TARGET=unknown # elif defined(__sparc__) && defined(__arch64__) PLATFORM_TRIPLET=sparc64-linux-LIBC +RUST_TARGET=sparc64-unknown-linux-RUST_LIBC # elif defined(__sparc__) PLATFORM_TRIPLET=sparc-linux-LIBC +RUST_TARGET=sparc-unknown-linux-RUST_LIBC # elif defined(__riscv) # if __riscv_xlen == 32 PLATFORM_TRIPLET=riscv32-linux-LIBC +RUST_TARGET=riscv32gc-unknown-linux-RUST_LIBC # elif __riscv_xlen == 64 PLATFORM_TRIPLET=riscv64-linux-LIBC +RUST_TARGET=riscv64gc-unknown-linux-RUST_LIBC # else # error unknown platform triplet # endif @@ -231,16 +301,20 @@ PLATFORM_TRIPLET=riscv64-linux-LIBC #elif defined(__FreeBSD_kernel__) # if defined(__LP64__) PLATFORM_TRIPLET=x86_64-kfreebsd-gnu +RUST_TARGET=unknown # elif defined(__i386__) PLATFORM_TRIPLET=i386-kfreebsd-gnu +RUST_TARGET=unknown # else # error unknown platform triplet # endif #elif defined(__gnu_hurd__) # if defined(__x86_64__) && defined(__LP64__) PLATFORM_TRIPLET=x86_64-gnu +RUST_TARGET=x86_64-unknown-hurd-gnu # elif defined(__i386__) PLATFORM_TRIPLET=i386-gnu +RUST_TARGET=i686-unknown-hurd-gnu # else # error unknown platform triplet # endif @@ -251,28 +325,42 @@ PLATFORM_TRIPLET=i386-gnu # if defined(TARGET_OS_SIMULATOR) && TARGET_OS_SIMULATOR # if __x86_64__ PLATFORM_TRIPLET=x86_64-iphonesimulator +RUST_TARGET=x86_64-apple-ios # else PLATFORM_TRIPLET=arm64-iphonesimulator +RUST_TARGET=aarch64-apple-ios-sim # endif # else PLATFORM_TRIPLET=arm64-iphoneos +RUST_TARGET=aarch64-apple-ios # endif // Older macOS SDKs do not define TARGET_OS_OSX # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin +# if defined(__x86_64__) +RUST_TARGET=x86_64-apple-darwin +# elif defined(__aarch64__) +RUST_TARGET=aarch64-apple-darwin +# else +RUST_TARGET=unknown +# endif # else # error unknown Apple platform # endif #elif defined(__VXWORKS__) PLATFORM_TRIPLET=vxworks +RUST_TARGET=unknown #elif defined(__wasm32__) # if defined(__EMSCRIPTEN__) PLATFORM_TRIPLET=wasm32-emscripten +RUST_TARGET=wasm32-unknown-emscripten # elif defined(__wasi__) # if defined(_REENTRANT) PLATFORM_TRIPLET=wasm32-wasi-threads +RUST_TARGET=wasm32-wasip1-threads # else PLATFORM_TRIPLET=wasm32-wasi +RUST_TARGET=wasm32-wasip1 # endif # else # error unknown wasm32 platform @@ -280,8 +368,10 @@ PLATFORM_TRIPLET=wasm32-wasi #elif defined(__wasm64__) # if defined(__EMSCRIPTEN__) PLATFORM_TRIPLET=wasm64-emscripten +RUST_TARGET=unknown # elif defined(__wasi__) PLATFORM_TRIPLET=wasm64-wasi +RUST_TARGET=unknown # else # error unknown wasm64 platform # endif diff --git a/configure b/configure index 00b93c838a4bb3..a74a40722c39e3 100755 --- a/configure +++ b/configure @@ -7177,6 +7177,8 @@ printf %s "checking for the platform triplet based on compiler characteristics.. if $CPP $CPPFLAGS $srcdir/Misc/platform_triplet.c >conftest.out 2>/dev/null; then PLATFORM_TRIPLET=`grep '^PLATFORM_TRIPLET=' conftest.out | tr -d ' '` PLATFORM_TRIPLET="${PLATFORM_TRIPLET#PLATFORM_TRIPLET=}" + RUST_TARGET=`grep '^RUST_TARGET=' conftest.out | tr -d ' '` + RUST_TARGET="${RUST_TARGET#RUST_TARGET=}" { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PLATFORM_TRIPLET" >&5 printf "%s\n" "$PLATFORM_TRIPLET" >&6; } else @@ -16122,29 +16124,15 @@ else CARGO_TARGET_DIR='debug' CARGO_PROFILE='dev' fi - # Set CARGO_TARGET for cross-compilation - # TODO(emmatyping): derive this in platform-triplet.c and set the Rust triple - # higher up - case "$host" in - aarch64-apple-ios-simulator) - CARGO_TARGET="aarch64-apple-ios-sim" - ;; - *-apple-darwin*) - cargo_host="$host" - case "$cargo_host" in - arm64-apple-*) - cargo_host="aarch64${cargo_host#arm64}" - ;; - esac - CARGO_TARGET="${cargo_host%%-apple-darwin*}-apple-darwin" - ;; - *-pc-linux-*) - CARGO_TARGET=$(echo "$host" | sed 's/-pc-linux-/-unknown-linux-/') - ;; - *) - CARGO_TARGET="$host" - ;; - esac + # RUST_TARGET is detected from Misc/platform_triplet.c using the + # C preprocessor, giving us the correct Rust target triple for the + # platform being compiled for. + CARGO_TARGET="$RUST_TARGET" + if test -z "$CARGO_TARGET" || test "$CARGO_TARGET" = "unknown"; then + as_fn_error $? "Could not determine a Rust target triple for this platform. + Rust may not support this target. Set CARGO_TARGET manually or + disable Rust support." "$LINENO" 5 + fi fi if test -n "$CARGO_TARGET"; then CARGO_TARGET_LINKER_ENV="CARGO_TARGET_$(echo "$CARGO_TARGET" | tr 'a-z-' 'A-Z_')_LINKER" diff --git a/configure.ac b/configure.ac index ccdd55802ba2bb..554a33af76c536 100644 --- a/configure.ac +++ b/configure.ac @@ -1154,6 +1154,8 @@ AC_MSG_CHECKING([for the platform triplet based on compiler characteristics]) if $CPP $CPPFLAGS $srcdir/Misc/platform_triplet.c >conftest.out 2>/dev/null; then PLATFORM_TRIPLET=`grep '^PLATFORM_TRIPLET=' conftest.out | tr -d ' '` PLATFORM_TRIPLET="${PLATFORM_TRIPLET@%:@PLATFORM_TRIPLET=}" + RUST_TARGET=`grep '^RUST_TARGET=' conftest.out | tr -d ' '` + RUST_TARGET="${RUST_TARGET@%:@RUST_TARGET=}" AC_MSG_RESULT([$PLATFORM_TRIPLET]) else AC_MSG_RESULT([none]) @@ -4340,29 +4342,15 @@ else CARGO_TARGET_DIR='debug' CARGO_PROFILE='dev' fi - # Set CARGO_TARGET for cross-compilation - # TODO(emmatyping): derive this in platform-triplet.c and set the Rust triple - # higher up - case "$host" in - aarch64-apple-ios-simulator) - CARGO_TARGET="aarch64-apple-ios-sim" - ;; - *-apple-darwin*) - cargo_host="$host" - case "$cargo_host" in - arm64-apple-*) - cargo_host="aarch64${cargo_host#arm64}" - ;; - esac - CARGO_TARGET="${cargo_host%%-apple-darwin*}-apple-darwin" - ;; - *-pc-linux-*) - CARGO_TARGET=$(echo "$host" | sed 's/-pc-linux-/-unknown-linux-/') - ;; - *) - CARGO_TARGET="$host" - ;; - esac + # RUST_TARGET is detected from Misc/platform_triplet.c using the + # C preprocessor, giving us the correct Rust target triple for the + # platform being compiled for. + CARGO_TARGET="$RUST_TARGET" + if test -z "$CARGO_TARGET" || test "$CARGO_TARGET" = "unknown"; then + AC_MSG_ERROR([Could not determine a Rust target triple for this platform. + Rust may not support this target. Set CARGO_TARGET manually or + disable Rust support.]) + fi fi dnl Compute the CARGO_TARGET__LINKER env var name so we can dnl tell Cargo which linker to use for the target platform. From cb43e5b961aad18e7186bdc0f2b15dbfd6879313 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 04:47:52 +0000 Subject: [PATCH 07/21] Pass CPPFLAGS to cpython-sys and set target --- Cargo.lock | 1 + Makefile.pre.in | 2 +- Modules/cpython-sys/Cargo.toml | 3 ++- Modules/cpython-sys/build.rs | 35 ++++++++++++++++++++++++++++++---- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2080d8d0da80b..132b913b06d5e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,7 @@ name = "cpython-sys" version = "0.1.0" dependencies = [ "bindgen", + "shlex", ] [[package]] diff --git a/Makefile.pre.in b/Makefile.pre.in index 33dc645fb9c230..489b5cf6a45170 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -3378,7 +3378,7 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h # Module dependencies and platform-specific files cpython-sys: Modules/cpython-sys/Cargo.toml Modules/cpython-sys/build.rs Modules/cpython-sys/wrapper.h Modules/cpython-sys/parser.h - CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) $(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml + CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) PY_CPPFLAGS="$(CPPFLAGS)" $(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml RUST_STATICLIB_A= target/$(if $(CARGO_TARGET),$(CARGO_TARGET)/$(CARGO_TARGET_DIR),$(CARGO_TARGET_DIR))/libcpython_rust_staticlib.a diff --git a/Modules/cpython-sys/Cargo.toml b/Modules/cpython-sys/Cargo.toml index de0b88d5d5b723..8919965d031eba 100644 --- a/Modules/cpython-sys/Cargo.toml +++ b/Modules/cpython-sys/Cargo.toml @@ -6,4 +6,5 @@ edition = "2024" [dependencies] [build-dependencies] -bindgen = "0.72.1" \ No newline at end of file +bindgen = "0.72.1" +shlex = "1.3" \ No newline at end of file diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index a681e498171675..84c98dfb263d79 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -12,11 +12,8 @@ fn main() { if gil_disabled(srcdir, builddir.as_deref()) { println!("cargo:rustc-cfg=py_gil_disabled"); } + println!("cargo::rustc-check-cfg=cfg(py_gil_disabled)"); generate_c_api_bindings(srcdir, builddir.as_deref(), out_path.as_path()); - // TODO(emmatyping): generate bindings to the internal parser API - // The parser includes things slightly differently, so we should generate - // it's bindings independently - //generate_parser_bindings(srcdir, &out_path.as_path()); } fn gil_disabled(srcdir: &Path, builddir: Option<&str>) -> bool { @@ -39,6 +36,36 @@ fn gil_disabled(srcdir: &Path, builddir: Option<&str>) -> bool { fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Path) { let mut builder = bindgen::Builder::default().header("wrapper.h"); + // Suppress all clang warnings (deprecation warnings, etc.) + builder = builder.clang_arg("-w"); + + // Tell clang the correct target triple for cross-compilation. + // Without this, bindgen uses the host target which causes errors like + // "thread-local storage is not supported" on iOS or missing headers + // on Android/WASI. + if let Ok(target) = env::var("TARGET") { + builder = builder.clang_arg(format!("--target={}", target)); + } + + // Forward cross-compilation flags (include paths, defines, sysroot) + // from CPython's CPPFLAGS. These are needed so bindgen's clang can + // find system headers (e.g. assert.h) when cross-compiling for + // Android NDK, WASI, etc. + if let Ok(cppflags) = env::var("PY_CPPFLAGS") { + if let Some(flags) = shlex::split(&cppflags) { + for flag in &flags { + if flag.starts_with("-I") + || flag.starts_with("-D") + || flag.starts_with("--sysroot") + || flag.starts_with("-isysroot") + || flag.starts_with("-isystem") + { + builder = builder.clang_arg(flag); + } + } + } + } + // Always search the source dir and the public headers. let mut include_dirs = vec![srcdir.to_path_buf(), srcdir.join("Include")]; // Include the build directory if provided; out-of-tree builds place From 8c593c8864f9501874fd1c14f752ed0a858cb803 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 05:13:25 +0000 Subject: [PATCH 08/21] Detect LLVM triple and handle NDK sysroot --- Makefile.pre.in | 3 +- Misc/platform_triplet.c | 19 +++++++++ Modules/cpython-sys/build.rs | 79 ++++++++++++++++++++++++++++-------- configure | 7 ++++ configure.ac | 7 ++++ 5 files changed, 96 insertions(+), 19 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 489b5cf6a45170..2ca8b45ce792fb 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -64,6 +64,7 @@ CARGO_PROFILE=@CARGO_PROFILE@ CARGO_TARGET=@CARGO_TARGET@ CARGO_TARGET_LINKER_ENV=@CARGO_TARGET_LINKER_ENV@ CARGO_DYLIB_SUFFIX=@CARGO_DYLIB_SUFFIX@ +LLVM_TARGET=@LLVM_TARGET@ RUST_STATICLIB_DEP=@RUST_STATICLIB_DEP@ GNULD= @GNULD@ @@ -3378,7 +3379,7 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h # Module dependencies and platform-specific files cpython-sys: Modules/cpython-sys/Cargo.toml Modules/cpython-sys/build.rs Modules/cpython-sys/wrapper.h Modules/cpython-sys/parser.h - CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) PY_CPPFLAGS="$(CPPFLAGS)" $(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml + CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) PY_CC="$(CC)" PY_CPPFLAGS="$(CPPFLAGS)" LLVM_TARGET="$(LLVM_TARGET)" $(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml RUST_STATICLIB_A= target/$(if $(CARGO_TARGET),$(CARGO_TARGET)/$(CARGO_TARGET_DIR),$(CARGO_TARGET_DIR))/libcpython_rust_staticlib.a diff --git a/Misc/platform_triplet.c b/Misc/platform_triplet.c index 13da020b84f2e3..f6d584189eb4ec 100644 --- a/Misc/platform_triplet.c +++ b/Misc/platform_triplet.c @@ -3,6 +3,10 @@ * * Also detects the closest Rust target triple for cargo/rustc: * cc -E Misc/platform_triplet.c | grep '^RUST_TARGET=' | tr -d ' ' + * + * And the LLVM/clang target triple (for bindgen), only when it differs + * from RUST_TARGET: + * cc -E Misc/platform_triplet.c | grep '^LLVM_TARGET=' | tr -d ' ' */ #undef bfin #undef cris @@ -145,6 +149,7 @@ RUST_TARGET=arm-linux-androideabi # elif defined(_ABI64) && _MIPS_SIM == _ABI64 # define LIBC_MIPS musl # define RUST_LIBC_MIPS muslabi64 +# define LLVM_LIBC_MIPS musl # else # error unknown mips sim value # endif @@ -242,12 +247,18 @@ RUST_TARGET=mipsisa32r6-unknown-linux-RUST_LIBC_MIPS # if defined(_MIPSEL) && defined(__mips64) PLATFORM_TRIPLET=mips64el-linux-LIBC_MIPS RUST_TARGET=mips64el-unknown-linux-RUST_LIBC_MIPS +# ifdef LLVM_LIBC_MIPS +LLVM_TARGET=mips64el-unknown-linux-LLVM_LIBC_MIPS +# endif # elif defined(_MIPSEL) PLATFORM_TRIPLET=mipsel-linux-LIBC_MIPS RUST_TARGET=mipsel-unknown-linux-RUST_LIBC_MIPS # elif defined(__mips64) PLATFORM_TRIPLET=mips64-linux-LIBC_MIPS RUST_TARGET=mips64-unknown-linux-RUST_LIBC_MIPS +# ifdef LLVM_LIBC_MIPS +LLVM_TARGET=mips64-unknown-linux-LLVM_LIBC_MIPS +# endif # else PLATFORM_TRIPLET=mips-linux-LIBC_MIPS RUST_TARGET=mips-unknown-linux-RUST_LIBC_MIPS @@ -286,9 +297,11 @@ RUST_TARGET=sparc-unknown-linux-RUST_LIBC # if __riscv_xlen == 32 PLATFORM_TRIPLET=riscv32-linux-LIBC RUST_TARGET=riscv32gc-unknown-linux-RUST_LIBC +LLVM_TARGET=riscv32-unknown-linux-RUST_LIBC # elif __riscv_xlen == 64 PLATFORM_TRIPLET=riscv64-linux-LIBC RUST_TARGET=riscv64gc-unknown-linux-RUST_LIBC +LLVM_TARGET=riscv64-unknown-linux-RUST_LIBC # else # error unknown platform triplet # endif @@ -326,21 +339,26 @@ RUST_TARGET=i686-unknown-hurd-gnu # if __x86_64__ PLATFORM_TRIPLET=x86_64-iphonesimulator RUST_TARGET=x86_64-apple-ios +LLVM_TARGET=x86_64-apple-ios-simulator # else PLATFORM_TRIPLET=arm64-iphonesimulator RUST_TARGET=aarch64-apple-ios-sim +LLVM_TARGET=arm64-apple-ios-simulator # endif # else PLATFORM_TRIPLET=arm64-iphoneos RUST_TARGET=aarch64-apple-ios +LLVM_TARGET=arm64-apple-ios # endif // Older macOS SDKs do not define TARGET_OS_OSX # elif !defined(TARGET_OS_OSX) || TARGET_OS_OSX PLATFORM_TRIPLET=darwin # if defined(__x86_64__) RUST_TARGET=x86_64-apple-darwin +LLVM_TARGET=x86_64-apple-macosx # elif defined(__aarch64__) RUST_TARGET=aarch64-apple-darwin +LLVM_TARGET=arm64-apple-macosx # else RUST_TARGET=unknown # endif @@ -358,6 +376,7 @@ RUST_TARGET=wasm32-unknown-emscripten # if defined(_REENTRANT) PLATFORM_TRIPLET=wasm32-wasi-threads RUST_TARGET=wasm32-wasip1-threads +LLVM_TARGET=wasm32-wasi # else PLATFORM_TRIPLET=wasm32-wasi RUST_TARGET=wasm32-wasip1 diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index 84c98dfb263d79..cc47b8b8819822 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -40,27 +40,70 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat builder = builder.clang_arg("-w"); // Tell clang the correct target triple for cross-compilation. - // Without this, bindgen uses the host target which causes errors like - // "thread-local storage is not supported" on iOS or missing headers - // on Android/WASI. - if let Ok(target) = env::var("TARGET") { + // LLVM_TARGET is the clang/LLVM triple which may differ from the Rust + // target (e.g. arm64-apple-macosx vs aarch64-apple-darwin, or + // riscv64-unknown-linux-gnu vs riscv64gc-unknown-linux-gnu). + // Falls back to Cargo's TARGET if LLVM_TARGET is not set. + let target = env::var("LLVM_TARGET") + .or_else(|_| env::var("TARGET")) + .unwrap_or_default(); + if !target.is_empty() { builder = builder.clang_arg(format!("--target={}", target)); } - // Forward cross-compilation flags (include paths, defines, sysroot) - // from CPython's CPPFLAGS. These are needed so bindgen's clang can - // find system headers (e.g. assert.h) when cross-compiling for - // Android NDK, WASI, etc. - if let Ok(cppflags) = env::var("PY_CPPFLAGS") { - if let Some(flags) = shlex::split(&cppflags) { - for flag in &flags { - if flag.starts_with("-I") - || flag.starts_with("-D") - || flag.starts_with("--sysroot") - || flag.starts_with("-isysroot") - || flag.starts_with("-isystem") - { - builder = builder.clang_arg(flag); + // Extract cross-compilation flags from the C compiler command (PY_CC) + // and preprocessor flags (PY_CPPFLAGS). These provide the sysroot and + // include paths that bindgen's clang needs to find system headers when + // cross-compiling. + // + // - WASI: the sysroot is embedded in CC ("clang --sysroot=...") + // - iOS: -isysroot in CPPFLAGS points to the SDK + let mut have_sysroot = false; + for env_name in ["PY_CC", "PY_CPPFLAGS"] { + if let Ok(value) = env::var(env_name) { + if let Some(flags) = shlex::split(&value) { + let mut iter = flags.iter().peekable(); + while let Some(flag) = iter.next() { + if flag.starts_with("--sysroot") + || flag.starts_with("-isysroot") + { + builder = builder.clang_arg(flag); + have_sysroot = true; + // Handle "-isysroot " (space-separated) + if flag == "-isysroot" || flag == "--sysroot" { + if let Some(path) = iter.next() { + builder = builder.clang_arg(path); + } + } + } else if flag.starts_with("-I") + || flag.starts_with("-D") + || flag.starts_with("-isystem") + { + builder = builder.clang_arg(flag); + } + } + } + } + } + + // Android NDK: the cross-compiler binary knows its own sysroot + // implicitly, but bindgen's libclang does not. The NDK sysroot is + // at .../toolchains/llvm/prebuilt//sysroot, which is a sibling + // of the bin/ directory containing the compiler. + if !have_sysroot && target.contains("android") { + if let Ok(cc) = env::var("PY_CC") { + if let Some(parts) = shlex::split(&cc) { + if let Some(binary) = parts.first() { + let cc_path = Path::new(binary); + if let Some(bin_dir) = cc_path.parent() { + let sysroot = bin_dir.with_file_name("sysroot"); + if sysroot.is_dir() { + builder = builder.clang_arg(format!( + "--sysroot={}", + sysroot.display() + )); + } + } } } } diff --git a/configure b/configure index a74a40722c39e3..16db4f9593cd47 100755 --- a/configure +++ b/configure @@ -888,6 +888,7 @@ TCLTK_LIBS TCLTK_CFLAGS LIBSQLITE3_LIBS LIBSQLITE3_CFLAGS +LLVM_TARGET CARGO_DYLIB_SUFFIX CARGO_TARGET_LINKER_ENV CARGO_TARGET @@ -7179,6 +7180,11 @@ if $CPP $CPPFLAGS $srcdir/Misc/platform_triplet.c >conftest.out 2>/dev/null; the PLATFORM_TRIPLET="${PLATFORM_TRIPLET#PLATFORM_TRIPLET=}" RUST_TARGET=`grep '^RUST_TARGET=' conftest.out | tr -d ' '` RUST_TARGET="${RUST_TARGET#RUST_TARGET=}" + LLVM_TARGET=`grep '^LLVM_TARGET=' conftest.out | tr -d ' '` + LLVM_TARGET="${LLVM_TARGET#LLVM_TARGET=}" + if test -z "$LLVM_TARGET"; then + LLVM_TARGET="$RUST_TARGET" + fi { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $PLATFORM_TRIPLET" >&5 printf "%s\n" "$PLATFORM_TRIPLET" >&6; } else @@ -16154,6 +16160,7 @@ esac + if test "$ac_sys_system" = "Emscripten" -a -z "$LIBSQLITE3_CFLAGS" -a -z "$LIBSQLITE3_LIBS" then : diff --git a/configure.ac b/configure.ac index 554a33af76c536..dd202d193a6da1 100644 --- a/configure.ac +++ b/configure.ac @@ -1156,6 +1156,12 @@ if $CPP $CPPFLAGS $srcdir/Misc/platform_triplet.c >conftest.out 2>/dev/null; the PLATFORM_TRIPLET="${PLATFORM_TRIPLET@%:@PLATFORM_TRIPLET=}" RUST_TARGET=`grep '^RUST_TARGET=' conftest.out | tr -d ' '` RUST_TARGET="${RUST_TARGET@%:@RUST_TARGET=}" + dnl LLVM_TARGET is only emitted when it differs from RUST_TARGET + LLVM_TARGET=`grep '^LLVM_TARGET=' conftest.out | tr -d ' '` + LLVM_TARGET="${LLVM_TARGET@%:@LLVM_TARGET=}" + if test -z "$LLVM_TARGET"; then + LLVM_TARGET="$RUST_TARGET" + fi AC_MSG_RESULT([$PLATFORM_TRIPLET]) else AC_MSG_RESULT([none]) @@ -4372,6 +4378,7 @@ AC_SUBST([CARGO_PROFILE]) AC_SUBST([CARGO_TARGET]) AC_SUBST([CARGO_TARGET_LINKER_ENV]) AC_SUBST([CARGO_DYLIB_SUFFIX]) +AC_SUBST([LLVM_TARGET]) dnl detect sqlite3 from Emscripten emport From a5fb908e8f2cc64e694ef678c2b585d3a7fb2447 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 07:31:03 +0000 Subject: [PATCH 09/21] Add targets for WASI, Android, and iOS --- .github/workflows/build.yml | 7 ++++++- .github/workflows/reusable-wasi.yml | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a0f60c30ac8a60..42ce10fd340eba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -383,6 +383,9 @@ jobs: - uses: actions/checkout@v4 with: persist-credentials: false + - uses: dtolnay/rust-toolchain@master + with: + targets: ${{ matrix.arch }}-linux-android - name: Build and test run: ./Android/android.py ci --fast-ci ${{ matrix.arch }}-linux-android @@ -396,7 +399,9 @@ jobs: - uses: actions/checkout@v4 with: persist-credentials: false - + - uses: dtolnay/rust-toolchain@master + with: + targets: aarch64-apple-ios-sim # GitHub recommends explicitly selecting the desired Xcode version: # https://github.com/actions/runner-images/issues/12541#issuecomment-3083850140 # This became a necessity as a result of diff --git a/.github/workflows/reusable-wasi.yml b/.github/workflows/reusable-wasi.yml index 8f412288f530bc..dc6366cb106e92 100644 --- a/.github/workflows/reusable-wasi.yml +++ b/.github/workflows/reusable-wasi.yml @@ -48,6 +48,9 @@ jobs: uses: actions/setup-python@v5 with: python-version: '3.x' + - uses: dtolnay/rust-toolchain@master + with: + targets: wasm32-wasip1 - name: "Runner image version" run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV" - name: "Configure build Python" From b170c1b899d52cb008af02ae2b783f5481406ecb Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 07:31:20 +0000 Subject: [PATCH 10/21] Tweak WASI and Android sysroot searching --- Modules/cpython-sys/build.rs | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index cc47b8b8819822..addebd2ed878dd 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -86,16 +86,33 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat } } - // Android NDK: the cross-compiler binary knows its own sysroot - // implicitly, but bindgen's libclang does not. The NDK sysroot is - // at .../toolchains/llvm/prebuilt//sysroot, which is a sibling - // of the bin/ directory containing the compiler. + // WASI SDK: WASI_SDK_PATH is set by Tools/wasm/wasi/__main__.py. + // The sysroot is at $WASI_SDK_PATH/share/wasi-sysroot. + if !have_sysroot && target.contains("wasi") { + if let Ok(sdk_path) = env::var("WASI_SDK_PATH") { + let sysroot = PathBuf::from(&sdk_path) + .join("share") + .join("wasi-sysroot"); + if sysroot.is_dir() { + builder = builder.clang_arg(format!( + "--sysroot={}", + sysroot.display() + )); + have_sysroot = true; + } + } + } + + // Android NDK: ANDROID_HOME is set by the CI/user environment, and + // Android/android-env.sh sets CC to the NDK clang binary at: + // $ANDROID_HOME/ndk//toolchains/llvm/prebuilt//bin/-clang + // The sysroot is a sibling of bin/: + // .../toolchains/llvm/prebuilt//sysroot if !have_sysroot && target.contains("android") { if let Ok(cc) = env::var("PY_CC") { if let Some(parts) = shlex::split(&cc) { if let Some(binary) = parts.first() { - let cc_path = Path::new(binary); - if let Some(bin_dir) = cc_path.parent() { + if let Some(bin_dir) = Path::new(binary).parent() { let sysroot = bin_dir.with_file_name("sysroot"); if sysroot.is_dir() { builder = builder.clang_arg(format!( From b85f67e56847bce0c7aa324bdd00bcb24c22f696 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 07:35:55 +0000 Subject: [PATCH 11/21] Pin to 1.91.1 toolchain for CI --- .github/workflows/build.yml | 4 ++-- .github/workflows/reusable-wasi.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 42ce10fd340eba..ab9a73627ae1df 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -383,7 +383,7 @@ jobs: - uses: actions/checkout@v4 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@master + - uses: dtolnay/rust-toolchain@1.91.1 with: targets: ${{ matrix.arch }}-linux-android - name: Build and test @@ -399,7 +399,7 @@ jobs: - uses: actions/checkout@v4 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@master + - uses: dtolnay/rust-toolchain@1.91.1 with: targets: aarch64-apple-ios-sim # GitHub recommends explicitly selecting the desired Xcode version: diff --git a/.github/workflows/reusable-wasi.yml b/.github/workflows/reusable-wasi.yml index dc6366cb106e92..b0462a82ed34a1 100644 --- a/.github/workflows/reusable-wasi.yml +++ b/.github/workflows/reusable-wasi.yml @@ -48,7 +48,7 @@ jobs: uses: actions/setup-python@v5 with: python-version: '3.x' - - uses: dtolnay/rust-toolchain@master + - uses: dtolnay/rust-toolchain@1.91.1 with: targets: wasm32-wasip1 - name: "Runner image version" From 98ed04788a5acda7282cc5d8ae9af20301dd5c79 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 07:51:20 +0000 Subject: [PATCH 12/21] Fix bindings for 32bit (e.g. WASI) platforms --- Modules/cpython-sys/src/lib.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Modules/cpython-sys/src/lib.rs b/Modules/cpython-sys/src/lib.rs index 5180762fbef6bf..17924f0e5fb00a 100644 --- a/Modules/cpython-sys/src/lib.rs +++ b/Modules/cpython-sys/src/lib.rs @@ -55,7 +55,7 @@ pub const _Py_STATIC_FLAG_BITS: Py_ssize_t = pub const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = (_Py_IMMORTAL_INITIAL_REFCNT as Py_ssize_t) | (_Py_STATIC_FLAG_BITS << 48); #[cfg(not(target_pointer_width = "64"))] -pub const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = 7u32 << 28; +pub const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = (7u32 << 28) as Py_ssize_t; #[repr(transparent)] pub struct PyObject(std::cell::UnsafeCell<_object>); @@ -129,7 +129,7 @@ pub const PyObject_HEAD_INIT: PyObject = { PyObject(std::cell::UnsafeCell::new(obj)) }; -#[cfg(not(py_gil_disabled))] +#[cfg(all(not(py_gil_disabled), target_pointer_width = "64"))] pub const PyObject_HEAD_INIT: PyObject = PyObject(std::cell::UnsafeCell::new(_object { __bindgen_anon_1: _object__bindgen_ty_1 { ob_refcnt_full: _Py_STATIC_IMMORTAL_INITIAL_REFCNT as i64, @@ -137,6 +137,15 @@ pub const PyObject_HEAD_INIT: PyObject = PyObject(std::cell::UnsafeCell::new(_ob ob_type: std::ptr::null_mut(), })); +// On 32-bit platforms, the refcount union only has ob_refcnt (no ob_refcnt_split). +#[cfg(all(not(py_gil_disabled), not(target_pointer_width = "64")))] +pub const PyObject_HEAD_INIT: PyObject = PyObject(std::cell::UnsafeCell::new(_object { + __bindgen_anon_1: _object__bindgen_ty_1 { + ob_refcnt: _Py_STATIC_IMMORTAL_INITIAL_REFCNT, + }, + ob_type: std::ptr::null_mut(), +})); + pub const PyModuleDef_HEAD_INIT: PyModuleDef_Base = PyModuleDef_Base { ob_base: PyObject_HEAD_INIT, m_init: None, From 4979e92b648c34004ade0f7f950d28ea46954531 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 07:51:54 +0000 Subject: [PATCH 13/21] Fix remaining BLDSHARED references --- Makefile.pre.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index 2ca8b45ce792fb..c69a032107ecc6 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1026,16 +1026,16 @@ $(LIBRARY): $(LIBRARY_OBJS) libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS) # AIX Linker don't support "-h" option if test "$(MACHDEP)" != "aix"; then \ - $(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ + $(BLDSHARED_EXE) $(BLDSHARED_ARGS) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ else \ - $(BLDSHARED) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ + $(BLDSHARED_EXE) $(BLDSHARED_ARGS) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ fi if test $(INSTSONAME) != $@; then \ $(LN) -f $(INSTSONAME) $@; \ fi libpython3.so: libpython$(LDVERSION).so - $(BLDSHARED) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ + $(BLDSHARED_EXE) $(BLDSHARED_ARGS) $(NO_AS_NEEDED) -o $@ -Wl,-h$@ $^ libpython$(LDVERSION).dylib: $(LIBRARY_OBJS) $(CC) -dynamiclib $(PY_CORE_LDFLAGS) -undefined dynamic_lookup -Wl,-install_name,$(prefix)/lib/libpython$(LDVERSION).dylib -Wl,-compatibility_version,$(VERSION) -Wl,-current_version,$(VERSION) -o $@ $(LIBRARY_OBJS) $(DTRACE_OBJS) $(SHLIBS) $(LIBC) $(LIBM); \ From 5c40dc8df23232910f9d1da472cf51041c16fdd8 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 08:55:32 +0000 Subject: [PATCH 14/21] Add LIBPYTHON search path for Android --- Modules/cpython-build-helper/src/lib.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Modules/cpython-build-helper/src/lib.rs b/Modules/cpython-build-helper/src/lib.rs index 1f96750afa3d81..1362e383606142 100644 --- a/Modules/cpython-build-helper/src/lib.rs +++ b/Modules/cpython-build-helper/src/lib.rs @@ -33,9 +33,19 @@ pub fn print_linker_args() { } // On Android (and Cygwin), extension modules must link against libpython. - // LIBPYTHON is set by the CPython build system on these platforms. + // LIBPYTHON is set by the CPython build system on these platforms and + // typically contains "-L. -lpython3.X". The "-L." is relative to the + // make build directory, so resolve it to an absolute path using + // PYTHON_BUILD_DIR. if let Ok(libpython) = env::var("LIBPYTHON") { + let builddir = env::var("PYTHON_BUILD_DIR").ok(); for arg in shlex::split(&libpython).expect("Invalid LIBPYTHON") { + if arg == "-L." { + if let Some(ref dir) = builddir { + println!("cargo:rustc-cdylib-link-arg=-L{}", dir); + continue; + } + } println!("cargo:rustc-cdylib-link-arg={}", arg); } } From dfb19036ddfdface9c17dc48e3db24b1f067d83d Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 08:56:44 +0000 Subject: [PATCH 15/21] Get WASI working? --- Makefile.pre.in | 2 +- Modules/Setup.stdlib.in | 2 +- Modules/cpython-sys/build.rs | 24 +++++++++++++----------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index c69a032107ecc6..03c894268fea72 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -3379,7 +3379,7 @@ Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h # Module dependencies and platform-specific files cpython-sys: Modules/cpython-sys/Cargo.toml Modules/cpython-sys/build.rs Modules/cpython-sys/wrapper.h Modules/cpython-sys/parser.h - CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) PY_CC="$(CC)" PY_CPPFLAGS="$(CPPFLAGS)" LLVM_TARGET="$(LLVM_TARGET)" $(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml + CARGO_TARGET_DIR=$(abs_builddir)/target PYTHON_BUILD_DIR=$(abs_builddir) PY_CC="$(CC)" PY_CPPFLAGS="$(CPPFLAGS)" PY_CFLAGS="$(CFLAGS)" LLVM_TARGET="$(LLVM_TARGET)" $(CARGO_HOME)/bin/cargo build --lib --locked --package cpython-sys --profile $(CARGO_PROFILE) $(if $(CARGO_TARGET),--target=$(CARGO_TARGET)) --manifest-path $(srcdir)/Cargo.toml RUST_STATICLIB_A= target/$(if $(CARGO_TARGET),$(CARGO_TARGET)/$(CARGO_TARGET_DIR),$(CARGO_TARGET_DIR))/libcpython_rust_staticlib.a diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in index 635be3cbe8dbc0..6b56dd858ce585 100644 --- a/Modules/Setup.stdlib.in +++ b/Modules/Setup.stdlib.in @@ -120,7 +120,7 @@ ############################################################################ # Rust modules # -@MODULE__BASE64_TRUE@_base64 _base64/Cargo.toml _base64/src/lib.rs lib_base64.a +@MODULE__BASE64_TRUE@_base64 _base64/Cargo.toml _base64/src/lib.rs ############################################################################ # Modules with some UNIX dependencies diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index addebd2ed878dd..d370f40b13d199 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -51,15 +51,15 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat builder = builder.clang_arg(format!("--target={}", target)); } - // Extract cross-compilation flags from the C compiler command (PY_CC) - // and preprocessor flags (PY_CPPFLAGS). These provide the sysroot and - // include paths that bindgen's clang needs to find system headers when - // cross-compiling. + // Extract cross-compilation flags from the C compiler command (PY_CC), + // preprocessor flags (PY_CPPFLAGS), and compiler flags (PY_CFLAGS). + // These provide the sysroot, include paths, and defines that bindgen's + // clang needs when cross-compiling. // - // - WASI: the sysroot is embedded in CC ("clang --sysroot=...") - // - iOS: -isysroot in CPPFLAGS points to the SDK + // - WASI: sysroot in CC, -D_WASI_EMULATED_SIGNAL in CFLAGS + // - iOS: -isysroot in CPPFLAGS let mut have_sysroot = false; - for env_name in ["PY_CC", "PY_CPPFLAGS"] { + for env_name in ["PY_CC", "PY_CPPFLAGS", "PY_CFLAGS"] { if let Ok(value) = env::var(env_name) { if let Some(flags) = shlex::split(&value) { let mut iter = flags.iter().peekable(); @@ -126,13 +126,15 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat } } - // Always search the source dir and the public headers. - let mut include_dirs = vec![srcdir.to_path_buf(), srcdir.join("Include")]; - // Include the build directory if provided; out-of-tree builds place - // the generated pyconfig.h there. + // Include the build directory first so that cross-build pyconfig.h + // takes precedence over any pyconfig.h in the source tree (which may + // be from a native build with different settings like LONG_BIT). + let mut include_dirs = Vec::new(); if let Some(build) = builddir { include_dirs.push(PathBuf::from(build)); } + include_dirs.push(srcdir.to_path_buf()); + include_dirs.push(srcdir.join("Include")); for dir in include_dirs { builder = builder.clang_arg(format!("-I{}", dir.display())); From 2c08857308bab9ac37234ae14ea38a231f38a038 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 09:10:41 +0000 Subject: [PATCH 16/21] Try adding a fallback mapping of Rust->LLVM triples --- Modules/cpython-sys/build.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index d370f40b13d199..8d1f99fe5e0890 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -33,6 +33,32 @@ fn gil_disabled(srcdir: &Path, builddir: Option<&str>) -> bool { false } +/// Map a Rust target triple to the corresponding LLVM/clang target triple. +/// Most are identical, but some platforms differ: +/// - Apple: aarch64 → arm64, darwin → macosx, ios-sim → ios-simulator +/// - RISC-V: riscv64gc → riscv64, riscv32gc → riscv32 +/// - WASI threads: wasip1-threads → wasi +fn rust_target_to_llvm(rust_target: &str) -> String { + match rust_target { + "aarch64-apple-darwin" => "arm64-apple-macosx".into(), + "x86_64-apple-darwin" => "x86_64-apple-macosx".into(), + "aarch64-apple-ios" => "arm64-apple-ios".into(), + "aarch64-apple-ios-sim" => "arm64-apple-ios-simulator".into(), + "x86_64-apple-ios" => "x86_64-apple-ios-simulator".into(), + "wasm32-wasip1-threads" => "wasm32-wasi".into(), + other => { + // riscv64gc-* → riscv64-*, riscv32gc-* → riscv32-* + if let Some(rest) = other.strip_prefix("riscv64gc-") { + return format!("riscv64-{rest}"); + } + if let Some(rest) = other.strip_prefix("riscv32gc-") { + return format!("riscv32-{rest}"); + } + other.into() + } + } +} + fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Path) { let mut builder = bindgen::Builder::default().header("wrapper.h"); @@ -43,9 +69,13 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat // LLVM_TARGET is the clang/LLVM triple which may differ from the Rust // target (e.g. arm64-apple-macosx vs aarch64-apple-darwin, or // riscv64-unknown-linux-gnu vs riscv64gc-unknown-linux-gnu). - // Falls back to Cargo's TARGET if LLVM_TARGET is not set. + // Falls back to Cargo's TARGET with known Rust→LLVM mappings. let target = env::var("LLVM_TARGET") - .or_else(|_| env::var("TARGET")) + .ok() + .filter(|s| !s.is_empty()) + .or_else(|| { + env::var("TARGET").ok().map(|t| rust_target_to_llvm(&t)) + }) .unwrap_or_default(); if !target.is_empty() { builder = builder.clang_arg(format!("--target={}", target)); From caf1f9fa2e91266de7d1a9c999ba5fd6177f89a6 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Mon, 2 Mar 2026 09:25:25 +0000 Subject: [PATCH 17/21] Revert "Try adding a fallback mapping of Rust->LLVM triples" This reverts commit 2c08857308bab9ac37234ae14ea38a231f38a038. --- Modules/cpython-sys/build.rs | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index 8d1f99fe5e0890..d370f40b13d199 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -33,32 +33,6 @@ fn gil_disabled(srcdir: &Path, builddir: Option<&str>) -> bool { false } -/// Map a Rust target triple to the corresponding LLVM/clang target triple. -/// Most are identical, but some platforms differ: -/// - Apple: aarch64 → arm64, darwin → macosx, ios-sim → ios-simulator -/// - RISC-V: riscv64gc → riscv64, riscv32gc → riscv32 -/// - WASI threads: wasip1-threads → wasi -fn rust_target_to_llvm(rust_target: &str) -> String { - match rust_target { - "aarch64-apple-darwin" => "arm64-apple-macosx".into(), - "x86_64-apple-darwin" => "x86_64-apple-macosx".into(), - "aarch64-apple-ios" => "arm64-apple-ios".into(), - "aarch64-apple-ios-sim" => "arm64-apple-ios-simulator".into(), - "x86_64-apple-ios" => "x86_64-apple-ios-simulator".into(), - "wasm32-wasip1-threads" => "wasm32-wasi".into(), - other => { - // riscv64gc-* → riscv64-*, riscv32gc-* → riscv32-* - if let Some(rest) = other.strip_prefix("riscv64gc-") { - return format!("riscv64-{rest}"); - } - if let Some(rest) = other.strip_prefix("riscv32gc-") { - return format!("riscv32-{rest}"); - } - other.into() - } - } -} - fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Path) { let mut builder = bindgen::Builder::default().header("wrapper.h"); @@ -69,13 +43,9 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat // LLVM_TARGET is the clang/LLVM triple which may differ from the Rust // target (e.g. arm64-apple-macosx vs aarch64-apple-darwin, or // riscv64-unknown-linux-gnu vs riscv64gc-unknown-linux-gnu). - // Falls back to Cargo's TARGET with known Rust→LLVM mappings. + // Falls back to Cargo's TARGET if LLVM_TARGET is not set. let target = env::var("LLVM_TARGET") - .ok() - .filter(|s| !s.is_empty()) - .or_else(|| { - env::var("TARGET").ok().map(|t| rust_target_to_llvm(&t)) - }) + .or_else(|_| env::var("TARGET")) .unwrap_or_default(); if !target.is_empty() { builder = builder.clang_arg(format!("--target={}", target)); From e1936b8b62b9716b85ff0518aee9682f9131106e Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Tue, 3 Mar 2026 01:06:39 +0000 Subject: [PATCH 18/21] Ignore Rust build support dirs in generate_stdlib_module_names.py --- Tools/build/generate_stdlib_module_names.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Tools/build/generate_stdlib_module_names.py b/Tools/build/generate_stdlib_module_names.py index 646f31b49761c1..f7a4166415a4a5 100644 --- a/Tools/build/generate_stdlib_module_names.py +++ b/Tools/build/generate_stdlib_module_names.py @@ -22,6 +22,10 @@ '__pycache__', 'site-packages', + # Rust build-system support modules + 'cpython-build-helper', + 'cpython-rust-staticlib', + # Test modules and packages '__hello__', '__phello__', From a59b30007c63867c349c4a1fd278c3c2c5107b84 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Tue, 3 Mar 2026 01:07:07 +0000 Subject: [PATCH 19/21] Run cargo fmt && clippy --- Modules/cpython-build-helper/src/lib.rs | 10 +-- Modules/cpython-sys/build.rs | 81 +++++++++++-------------- 2 files changed, 40 insertions(+), 51 deletions(-) diff --git a/Modules/cpython-build-helper/src/lib.rs b/Modules/cpython-build-helper/src/lib.rs index 1362e383606142..94883aaf4f8d82 100644 --- a/Modules/cpython-build-helper/src/lib.rs +++ b/Modules/cpython-build-helper/src/lib.rs @@ -40,11 +40,11 @@ pub fn print_linker_args() { if let Ok(libpython) = env::var("LIBPYTHON") { let builddir = env::var("PYTHON_BUILD_DIR").ok(); for arg in shlex::split(&libpython).expect("Invalid LIBPYTHON") { - if arg == "-L." { - if let Some(ref dir) = builddir { - println!("cargo:rustc-cdylib-link-arg=-L{}", dir); - continue; - } + if arg == "-L." + && let Some(ref dir) = builddir + { + println!("cargo:rustc-cdylib-link-arg=-L{}", dir); + continue; } println!("cargo:rustc-cdylib-link-arg={}", arg); } diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index d370f40b13d199..5d45d58239709d 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -60,27 +60,25 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat // - iOS: -isysroot in CPPFLAGS let mut have_sysroot = false; for env_name in ["PY_CC", "PY_CPPFLAGS", "PY_CFLAGS"] { - if let Ok(value) = env::var(env_name) { - if let Some(flags) = shlex::split(&value) { - let mut iter = flags.iter().peekable(); - while let Some(flag) = iter.next() { - if flag.starts_with("--sysroot") - || flag.starts_with("-isysroot") - { - builder = builder.clang_arg(flag); - have_sysroot = true; - // Handle "-isysroot " (space-separated) - if flag == "-isysroot" || flag == "--sysroot" { - if let Some(path) = iter.next() { - builder = builder.clang_arg(path); - } - } - } else if flag.starts_with("-I") - || flag.starts_with("-D") - || flag.starts_with("-isystem") + if let Ok(value) = env::var(env_name) + && let Some(flags) = shlex::split(&value) + { + let mut iter = flags.iter().peekable(); + while let Some(flag) = iter.next() { + if flag.starts_with("--sysroot") || flag.starts_with("-isysroot") { + builder = builder.clang_arg(flag); + have_sysroot = true; + // Handle "-isysroot " (space-separated) + if (flag == "-isysroot" || flag == "--sysroot") + && let Some(path) = iter.next() { - builder = builder.clang_arg(flag); + builder = builder.clang_arg(path); } + } else if flag.starts_with("-I") + || flag.starts_with("-D") + || flag.starts_with("-isystem") + { + builder = builder.clang_arg(flag); } } } @@ -88,18 +86,14 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat // WASI SDK: WASI_SDK_PATH is set by Tools/wasm/wasi/__main__.py. // The sysroot is at $WASI_SDK_PATH/share/wasi-sysroot. - if !have_sysroot && target.contains("wasi") { - if let Ok(sdk_path) = env::var("WASI_SDK_PATH") { - let sysroot = PathBuf::from(&sdk_path) - .join("share") - .join("wasi-sysroot"); - if sysroot.is_dir() { - builder = builder.clang_arg(format!( - "--sysroot={}", - sysroot.display() - )); - have_sysroot = true; - } + if !have_sysroot + && target.contains("wasi") + && let Ok(sdk_path) = env::var("WASI_SDK_PATH") + { + let sysroot = PathBuf::from(&sdk_path).join("share").join("wasi-sysroot"); + if sysroot.is_dir() { + builder = builder.clang_arg(format!("--sysroot={}", sysroot.display())); + have_sysroot = true; } } @@ -108,21 +102,16 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat // $ANDROID_HOME/ndk//toolchains/llvm/prebuilt//bin/-clang // The sysroot is a sibling of bin/: // .../toolchains/llvm/prebuilt//sysroot - if !have_sysroot && target.contains("android") { - if let Ok(cc) = env::var("PY_CC") { - if let Some(parts) = shlex::split(&cc) { - if let Some(binary) = parts.first() { - if let Some(bin_dir) = Path::new(binary).parent() { - let sysroot = bin_dir.with_file_name("sysroot"); - if sysroot.is_dir() { - builder = builder.clang_arg(format!( - "--sysroot={}", - sysroot.display() - )); - } - } - } - } + if !have_sysroot + && target.contains("android") + && let Ok(cc) = env::var("PY_CC") + && let Some(parts) = shlex::split(&cc) + && let Some(binary) = parts.first() + && let Some(bin_dir) = Path::new(binary).parent() + { + let sysroot = bin_dir.with_file_name("sysroot"); + if sysroot.is_dir() { + builder = builder.clang_arg(format!("--sysroot={}", sysroot.display())); } } From 9b338488e3ee3290a7e4e3b84bc1f671ea917bd5 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Tue, 3 Mar 2026 01:13:49 +0000 Subject: [PATCH 20/21] Pass through C standard flag to libclang for sanitizers --- Modules/cpython-sys/build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index 5d45d58239709d..701a3be550ae0b 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -76,6 +76,7 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat } } else if flag.starts_with("-I") || flag.starts_with("-D") + || flag.starts_with("-std=") || flag.starts_with("-isystem") { builder = builder.clang_arg(flag); From 74f6e7ecd626fc272b5733addc2ee2a6932f5eb0 Mon Sep 17 00:00:00 2001 From: Emma Harper Smith Date: Tue, 3 Mar 2026 02:21:26 +0000 Subject: [PATCH 21/21] Add newline to end of cpython-sys/Cargo.toml --- Modules/cpython-sys/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/cpython-sys/Cargo.toml b/Modules/cpython-sys/Cargo.toml index 8919965d031eba..a12556c6e305a1 100644 --- a/Modules/cpython-sys/Cargo.toml +++ b/Modules/cpython-sys/Cargo.toml @@ -7,4 +7,4 @@ edition = "2024" [build-dependencies] bindgen = "0.72.1" -shlex = "1.3" \ No newline at end of file +shlex = "1.3"