diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e944eae23bc..eb7d4ed36ba 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,6 @@ jobs: build: continue-on-error: ${{ endsWith(inputs.python-version, '-dev') || contains(fromJSON('["beta", "nightly"]'), inputs.rust) }} runs-on: ${{ inputs.os }} - if: ${{ !(startsWith(inputs.python-version, 'graalpy') && startsWith(inputs.os, 'windows')) }} steps: - uses: actions/checkout@v7.0.1 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52c2e98628c..cea49a9ba93 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -297,7 +297,8 @@ jobs: "3.15-dev", "3.15t-dev", "pypy3.11", - "graalpy25.0", + "graalpy25.2", # 3.12 + "graalpy25.3", # 3.13 ] platform: [ diff --git a/Cargo.toml b/Cargo.toml index 52bc2c414f6..6097df53f75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -255,7 +255,7 @@ check-cfg = [ # # maybe in the future this list will not be necessary, see # internals.rust-lang.org/t/support-renames-with-link-name-kind-raw-dylib/24415 - 'cfg(pyo3_dll, values("python3", "python3_d", "python3t", "python3t_d", "python38", "python38_d", "python39", "python39_d", "python310", "python310_d", "python311", "python311_d", "python312", "python312_d", "python313", "python313_d", "python313t", "python313t_d", "python314", "python314_d", "python314t", "python314t_d", "python315", "python315_d", "python315t", "python315t_d", "python316", "python316_d", "python316t", "python316t_d", "libpypy3.11-c", "libpypy3.12-c"))', + 'cfg(pyo3_dll, values("python3", "python3_d", "python3t", "python3t_d", "python38", "python38_d", "python39", "python39_d", "python310", "python310_d", "python311", "python311_d", "python312", "python312_d", "python313", "python313_d", "python313t", "python313t_d", "python314", "python314_d", "python314t", "python314t_d", "python315", "python315_d", "python315t", "python315t_d", "python316", "python316_d", "python316t", "python316t_d", "libpypy3.11-c", "libpypy3.12-c", "python-native"))', ] diff --git a/newsfragments/6406.packaging.md b/newsfragments/6406.packaging.md new file mode 100644 index 00000000000..e6e1fcbd23a --- /dev/null +++ b/newsfragments/6406.packaging.md @@ -0,0 +1 @@ +Support GraalPy on Windows. diff --git a/noxfile.py b/noxfile.py index a0aaf4505c2..2aa9d573af5 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1494,6 +1494,9 @@ def _check_raw_dylib_macro(session: nox.Session): ): expected_dlls.add(f"libpypy3.{minor}-c") + # GraalPy DLL (python-native.dll) + expected_dlls.add("python-native") + # Parse the DLL name list in the extern_libpython!(@impl ...) invocation lib_rs = (PYO3_DIR / "pyo3-ffi" / "src" / "impl_" / "macros.rs").read_text() found_dlls = set(re.findall(r'"((?:python(?!XY)|libpypy)[^"]+)"', lib_rs)) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 2c63cd724cc..8272c6582e5 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -2396,15 +2396,6 @@ fn default_lib_name_for_target(abi: PythonAbi, target: &Triple) -> String { } fn default_lib_name_windows(abi: PythonAbi, mingw: bool, debug: bool) -> Result { - // mingw formats lib names like unix, and uses a "lib" prefix. We could let the linker - // handle "lib" prefix, but that means the `raw-dylib` name is incorrect (where the - // "lib" prefix is not automatically added). - if mingw { - let mut lib_name = default_lib_name_unix(abi, true, None)?; - lib_name.insert_str(0, "lib"); - return Ok(lib_name); - } - if abi.implementation.is_pypy() { // PyPy on Windows ships `libpypy3.X-c.dll` (e.g. `libpypy3.11-c.dll`), // not CPython's `pythonXY.dll`. With raw-dylib linking we need the real @@ -2413,6 +2404,16 @@ fn default_lib_name_windows(abi: PythonAbi, mingw: bool, debug: bool) -> Result< "libpypy{}.{}-c", abi.version.major, abi.version.minor )) + } else if abi.implementation == PythonImplementation::GraalPy { + // Similar for GraalPy on Windows, which ships `python-native.dll` + Ok("python-native".to_string()) + } else if mingw { + // mingw formats lib names like unix, and uses a "lib" prefix. We could let the linker + // handle "lib" prefix, but that means the `raw-dylib` name is incorrect (where the + // "lib" prefix is not automatically added). + let mut lib_name = default_lib_name_unix(abi, true, None)?; + lib_name.insert_str(0, "lib"); + Ok(lib_name) } else if debug && abi.version < PythonVersion::PY310 { // CPython bug: linking against python3_d.dll raises error // https://github.com/python/cpython/issues/101614 diff --git a/pyo3-ffi-check/definitions/wrapper.h b/pyo3-ffi-check/definitions/wrapper.h index 2c822162b6b..b3128dc8c27 100644 --- a/pyo3-ffi-check/definitions/wrapper.h +++ b/pyo3-ffi-check/definitions/wrapper.h @@ -2,4 +2,8 @@ #include "datetime.h" #include "frameobject.h" #include "structmember.h" + +// no marshal.h in GraalPy +#ifndef GRAALVM_PYTHON #include "marshal.h" +#endif diff --git a/pyo3-ffi-check/macro/src/lib.rs b/pyo3-ffi-check/macro/src/lib.rs index 0d3afcac282..90f19f4b1d3 100644 --- a/pyo3-ffi-check/macro/src/lib.rs +++ b/pyo3-ffi-check/macro/src/lib.rs @@ -56,6 +56,13 @@ pub fn for_all_structs(input: proc_macro::TokenStream) -> proc_macro::TokenStrea continue; } + if pyo3_build_config::get().target_abi().implementation() == PythonImplementation::GraalPy + && (struct_name == "PyFunctionObject" || struct_name == "PySliceObject") + { + // GraalPy wants these types to be opaque, we skip them + continue; + } + let struct_ident = Ident::new(struct_name, Span::call_site()); output.extend(quote!(#macro_name!(#struct_ident);)); } @@ -263,6 +270,17 @@ pub fn for_all_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream // bindgen picked `__bindgen_anon_1` as the field name for the anonymous union containing ob_refcnt, // PyO3 uses ob_refcnt directly all_fields.remove("__bindgen_anon_1"); + } else if struct_name == "PyVarObject" + && pyo3_build_config::get().target_abi().implementation() == PythonImplementation::GraalPy + { + // ob_size not present in GraalPy + all_fields.remove("ob_size"); + } else if pyo3_build_config::get().target_abi().implementation() + == PythonImplementation::GraalPy + && (struct_name == "PyFunctionObject" || struct_name == "PySliceObject") + { + // GraalPy wants these types to be opaque, we skip them + return TokenStream::new().into(); } let mut output = TokenStream::new(); @@ -533,7 +551,7 @@ const MACRO_EXCLUSIONS: &[(&str, &str)] = &[ ("Py_IS_TYPE", "not(Py_3_15)"), // symbol added for stable abi on 3.15 ("Py_None", ""), ("Py_NotImplemented", ""), - ("Py_REFCNT", "not(Py_3_14)"), + ("Py_REFCNT", "all(not(Py_3_14), not(GraalPy))"), ("Py_SIZE", "not(Py_3_15)"), // symbol added for stable abi on 3.15 ("Py_True", ""), ("Py_TYPE", "not(Py_3_14)"), diff --git a/pyo3-ffi/build.rs b/pyo3-ffi/build.rs index 70e193d2cae..860d9055eb1 100644 --- a/pyo3-ffi/build.rs +++ b/pyo3-ffi/build.rs @@ -195,10 +195,12 @@ fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result /// (see https://internals.rust-lang.org/t/support-renames-with-link-name-kind-raw-dylib/24415) /// so if the lib name is not one of the known subset, we must fall back to full linking. fn lib_name_is_known_for_raw_dylib(lib_name: &str) -> bool { - // pyo3_dll cfg for raw-dylib linking on Windows if matches!( lib_name, + // pyo3_dll cfg for raw-dylib linking on Windows "python3" | "python3_d" | "python3t" | "python3t_d" + // GraalPy DLL name + | "python-native" ) { return true; } diff --git a/pyo3-ffi/src/cpython/mod.rs b/pyo3-ffi/src/cpython/mod.rs index 5303a57f59d..f44de934614 100644 --- a/pyo3-ffi/src/cpython/mod.rs +++ b/pyo3-ffi/src/cpython/mod.rs @@ -26,6 +26,7 @@ pub(crate) mod listobject; #[cfg(Py_3_13)] pub(crate) mod lock; pub(crate) mod longobject; +#[cfg(not(GraalPy))] pub(crate) mod marshal; #[cfg(not(PyPy))] pub(crate) mod methodobject; @@ -77,6 +78,7 @@ pub use self::listobject::*; #[cfg(Py_3_13)] pub use self::lock::*; pub use self::longobject::*; +#[cfg(not(GraalPy))] pub use self::marshal::*; #[cfg(not(PyPy))] pub use self::methodobject::*; diff --git a/pyo3-ffi/src/datetime.rs b/pyo3-ffi/src/datetime.rs index d2022138a87..f4b6a9bd81b 100644 --- a/pyo3-ffi/src/datetime.rs +++ b/pyo3-ffi/src/datetime.rs @@ -350,7 +350,7 @@ pub unsafe fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int { // but copying them seems suboptimal #[inline] #[cfg(GraalPy)] -pub unsafe fn _get_attr(obj: *mut PyObject, field: &core::ffi::CStr) -> c_int { +unsafe fn _get_attr(obj: *mut PyObject, field: &core::ffi::CStr) -> c_int { let result = PyObject_GetAttrString(obj, field.as_ptr()); Py_DecRef(result); // the original macros are borrowing if PyLong_Check(result) == 1 { diff --git a/pyo3-ffi/src/impl_/macros.rs b/pyo3-ffi/src/impl_/macros.rs index 5ca4a4ac3bc..49099f9e147 100644 --- a/pyo3-ffi/src/impl_/macros.rs +++ b/pyo3-ffi/src/impl_/macros.rs @@ -289,6 +289,8 @@ macro_rules! extern_libpython { // PyPy (DLL is libpypy3.X-c.dll, not pythonXY.dll) "libpypy3.11-c", "libpypy3.12-c", + // GraalPy DLL + "python-native", ); }; // Internal: generate cfg_attr for each DLL name. One of these will be selected diff --git a/pyo3-ffi/src/object.rs b/pyo3-ffi/src/object.rs index 40578b244d1..91a146c537f 100644 --- a/pyo3-ffi/src/object.rs +++ b/pyo3-ffi/src/object.rs @@ -169,7 +169,7 @@ pub struct PyVarObject { pub ob_size: Py_ssize_t, // On GraalPy the field is physically there, but not always populated. We hide it to prevent accidental misuse #[cfg(GraalPy)] - pub _ob_size_graalpy: Py_ssize_t, + _ob_size_graalpy: Py_ssize_t, } #[inline] diff --git a/src/marshal.rs b/src/marshal.rs index 56249492a06..4ee15fa9a34 100644 --- a/src/marshal.rs +++ b/src/marshal.rs @@ -1,4 +1,4 @@ -#![cfg(not(Py_LIMITED_API))] +#![cfg(not(any(Py_LIMITED_API, GraalPy)))] //! Support for the Python `marshal` format.