Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ Some of the functionality of `pyo3-build-config`:
cfg with the target DLL name, and the `extern_libpython!` macro expands to the appropriate
`#[link(name = "...", kind = "raw-dylib")]` attribute. This enables cross compiling Python
extensions for Windows without having to install any Windows Python libraries.
This can be opted out by setting `PYO3_USE_RAW_DYLIB` to anything other than "1".

<!-- External Links -->

Expand Down
12 changes: 11 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,17 @@ unsafe_op_in_unsafe_fn = "warn"
level = "warn"
check-cfg = [
'cfg(pyo3_disable_reference_pool)',
'cfg(pyo3_leak_on_drop_without_reference_pool)'
'cfg(pyo3_leak_on_drop_without_reference_pool)',
'cfg(pyo3_use_raw_dylib)',
# this horrible hard-coded list should be kept in sync with the list of possible
# cfgs in pyo3-ffi/src/impl_/macros.rs
#
# for library names outside of this set we don't support raw-dylib and require a
# proper import library
#
# 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"))',
]


Expand Down
22 changes: 11 additions & 11 deletions guide/src/building-and-distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ Caused by:
cargo:rustc-check-cfg=cfg(Py_3_14)
cargo:rustc-check-cfg=cfg(Py_3_15)
cargo:rustc-check-cfg=cfg(Py_3_16)
cargo:rustc-check-cfg=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"))
cargo:rerun-if-env-changed=PYO3_CONFIG_FILE
cargo:rerun-if-env-changed=PYO3_CROSS
cargo:rerun-if-env-changed=PYO3_CROSS_LIB_DIR
Expand Down Expand Up @@ -239,6 +238,16 @@ This should only be set when building a library for distribution.
>
> Projects are encouraged to migrate off the feature, as it caused [major development pain](faq.md#i-cant-run-cargo-test-or-i-cant-build-in-a-cargo-workspace-im-having-linker-issues-like-symbol-not-found-or-undefined-reference-to-_pyexc_systemerror) due to the lack of linking.

### The `PYO3_USE_RAW_DYLIB` environment variable

When targeting Windows, PyO3 will attempt to use [`raw-dylib` linking](https://doc.rust-lang.org/reference/items/external-blocks.html#dylib-versus-raw-dylib) to avoid the need for users to provide an actual import library to link against.

On occasion the full Python import library may be needed (e.g. mixed C/Rust projects where the C code uses symbols not defined by `pyo3-ffi`).
In these cases, setting `PYO3_USE_RAW_DYLIB=0` can be used to disable `raw-dylib` linking.

> [!NOTE]
> Historically PyO3 used a `generate-import-lib` feature which needed external machinery to achieve the same result of `raw-dylib` linking.

### `Py_LIMITED_API`/`abi3`/`abi3t`

By default, Python extension modules can only be used with the same Python version they were compiled against.
Expand Down Expand Up @@ -398,8 +407,7 @@ When cross-compiling, PyO3's build script cannot execute the target Python inter

- `PYO3_CROSS`: If present this variable forces PyO3 to configure as a cross-compilation.
- `PYO3_CROSS_LIB_DIR`: This variable can be set to the directory containing the target's libpython DSO and the associated `_sysconfigdata*.py` file for Unix-like targets.
This variable is only needed when the output binary must link to libpython explicitly (e.g. when targeting Android or embedding a Python interpreter), or when it is absolutely required to get the interpreter configuration from `_sysconfigdata*.py`.
On Windows, this variable is not needed because PyO3 uses `raw-dylib` linking.
This variable is only needed when the output binary must link to libpython explicitly (e.g. when targeting Android, Windows when `raw-dylib` linking is unavailable, or embedding a Python interpreter), or when it is absolutely required to get the interpreter configuration from `_sysconfigdata*.py`.
- `PYO3_CROSS_PYTHON_VERSION`: Major and minor version (e.g. 3.9) of the target Python installation.
This variable is only needed if PyO3 cannot determine the version to target from `abi3-py3*` features, or if `PYO3_CROSS_LIB_DIR` is not set, or if there are multiple versions of Python present in `PYO3_CROSS_LIB_DIR`.
- `PYO3_CROSS_PYTHON_IMPLEMENTATION`: Python implementation name ("CPython" or "PyPy") of the target Python installation.
Expand All @@ -422,14 +430,6 @@ export PYO3_CROSS_LIB_DIR="/home/pyo3/cross/sysroot/usr/lib"
cargo build --target armv7-unknown-linux-gnueabihf
```

Or another example building for Windows (no `PYO3_CROSS_LIB_DIR` needed thanks to `raw-dylib`):

```sh
export PYO3_CROSS_PYTHON_VERSION=3.9

cargo build --target x86_64-pc-windows-gnu
```

Any of the `abi3-py3*` features can be enabled instead of setting `PYO3_CROSS_PYTHON_VERSION` in the above examples.

`PYO3_CROSS_LIB_DIR` can often be omitted when cross compiling extension modules for Unix, macOS, and Windows targets.
Expand Down
2 changes: 0 additions & 2 deletions guide/src/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,5 +323,3 @@ See the [building and distribution](building-and-distribution.md#the-extension-m
### `generate-import-lib`

This feature is deprecated and has no effect.
PyO3 now uses Rust's `raw-dylib` linking feature to link against the Python DLL on Windows, eliminating the need for import library (`.lib`) files entirely.
Cross-compiling for Windows targets works without any additional setup.
1 change: 1 addition & 0 deletions newsfragments/6185.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix PyO3 0.29 regression with failure to link under Cygwin / MSYS2.
1 change: 1 addition & 0 deletions newsfragments/6185.packaging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `PYO3_USE_RAW_DYLIB=0` opt-out of `raw-dylib` linking for Windows.
4 changes: 2 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1433,7 +1433,7 @@ def _check_raw_dylib_macro(session: nox.Session):

# Build the set of DLL names that default_lib_name_windows can produce
expected_dlls = {"python3", "python3_d"}
for minor in range(min_minor, max_minor + 1):
for minor in range(min_minor, max_minor + 2): # allow prerelease of next version
expected_dlls.add(f"python3{minor}")
expected_dlls.add(f"python3{minor}_d")
if minor >= 13:
Expand All @@ -1452,7 +1452,7 @@ def _check_raw_dylib_macro(session: nox.Session):

# 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|libpypy)[^"]+)"', lib_rs))
found_dlls = set(re.findall(r'"((?:python(?!XY)|libpypy)[^"]+)"', lib_rs))

missing = expected_dlls - found_dlls
extra = found_dlls - expected_dlls
Expand Down
4 changes: 0 additions & 4 deletions pyo3-build-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,5 @@ default = []

# deprecated
resolve-config = []

# deprecated
extension-module = []

# deprecated: no longer needed, raw-dylib is used instead
generate-import-lib = []
77 changes: 34 additions & 43 deletions pyo3-build-config/src/impl_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,6 @@ use crate::{
/// Minimum Python version PyO3 supports.
pub(crate) const MINIMUM_SUPPORTED_VERSION: PythonVersion = PythonVersion { major: 3, minor: 9 };

pub(crate) const MINIMUM_SUPPORTED_VERSION_PYPY: PythonVersion = PythonVersion {
major: 3,
minor: 11,
};
pub(crate) const MAXIMUM_SUPPORTED_VERSION_PYPY: PythonVersion = PythonVersion {
major: 3,
minor: 11,
};

pub(crate) const MINIMUM_SUPPORTED_VERSION_ABI3T: PythonVersion = PythonVersion {
major: 3,
minor: 15,
Expand Down Expand Up @@ -2314,6 +2305,15 @@ fn default_lib_name_for_target(abi: PythonAbi, target: &Triple) -> String {
}

fn default_lib_name_windows(abi: PythonAbi, mingw: bool, debug: bool) -> Result<String> {
// 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).)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- // "lib" prefix is not automatically added).)
+ // "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
Expand Down Expand Up @@ -2341,13 +2341,6 @@ fn default_lib_name_windows(abi: PythonAbi, mingw: bool, debug: bool) -> Result<
lib_name = lib_name.replace("python3", "python3t");
}
Ok(lib_name)
} else if mingw {
ensure!(
!abi.kind.is_free_threaded(),
"MinGW free-threaded builds are not currently tested or supported"
);
// https://packages.msys2.org/base/mingw-w64-python
Ok(format!("python{}.{}", abi.version.major, abi.version.minor))
} else if abi.kind().is_free_threaded() {
#[expect(deprecated, reason = "using constant internally")]
{
Expand All @@ -2371,28 +2364,36 @@ fn default_lib_name_windows(abi: PythonAbi, mingw: bool, debug: bool) -> Result<
}
}

fn default_lib_name_unix(abi: PythonAbi, cygwin: bool, ld_version: Option<&str>) -> Result<String> {
fn default_lib_name_unix(
abi: PythonAbi,
use_stable_abi_lib: bool,
ld_version: Option<&str>,
) -> Result<String> {
match abi.implementation {
PythonImplementation::CPython => match ld_version {
Some(ld_version) => Ok(format!("python{ld_version}")),
None => {
if cygwin && matches!(abi.kind, PythonAbiKind::Stable(StableAbi::Abi3)) {
None => match abi.kind {
PythonAbiKind::Stable(StableAbi::Abi3) if use_stable_abi_lib => {
Ok("python3".to_string())
} else if cygwin && matches!(abi.kind, PythonAbiKind::Stable(StableAbi::Abi3t)) {
}
PythonAbiKind::Stable(StableAbi::Abi3t) if use_stable_abi_lib => {
Ok("python3t".to_string())
} else if abi.kind.is_free_threaded() {
#[expect(deprecated, reason = "using constant internally")]
{
ensure!(abi.version >= PythonVersion::PY313, "Cannot compile extensions for the free-threaded build on Python versions earlier than 3.13, found {}.{}", abi.version.major, abi.version.minor);
}
_ => {
if abi.kind.is_free_threaded() {
#[expect(deprecated, reason = "using constant internally")]
{
ensure!(abi.version >= PythonVersion::PY313, "Cannot compile extensions for the free-threaded build on Python versions earlier than 3.13, found {}.{}", abi.version.major, abi.version.minor);
}
Ok(format!(
"python{}.{}t",
abi.version.major, abi.version.minor
))
} else {
Ok(format!("python{}.{}", abi.version.major, abi.version.minor))
}
Ok(format!(
"python{}.{}t",
abi.version.major, abi.version.minor
))
} else {
Ok(format!("python{}.{}", abi.version.major, abi.version.minor))
}
}
},
},
PythonImplementation::PyPy => match ld_version {
Some(ld_version) => Ok(format!("pypy{ld_version}-c")),
Expand Down Expand Up @@ -3290,7 +3291,7 @@ mod tests {
false,
)
.unwrap(),
"python3.9",
"libpython3.9",
);
assert_eq!(
super::default_lib_name_windows(
Expand All @@ -3302,7 +3303,7 @@ mod tests {
false,
)
.unwrap(),
"python3",
"libpython3",
);
assert_eq!(
super::default_lib_name_windows(
Expand Down Expand Up @@ -3366,16 +3367,6 @@ mod tests {
.unwrap(),
"python3_d",
);
// mingw and free-threading are incompatible (until someone adds support)
assert!(super::default_lib_name_windows(
PythonAbiBuilder::new(PythonImplementation::CPython, PythonVersion::PY313)
.free_threaded()
.finalize()
.unwrap(),
true,
false,
)
.is_err());
assert_eq!(
super::default_lib_name_windows(
PythonAbiBuilder::new(PythonImplementation::CPython, PythonVersion::PY313)
Expand Down
28 changes: 0 additions & 28 deletions pyo3-build-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,34 +164,6 @@ pub fn print_expected_cfgs() {
for i in impl_::MINIMUM_SUPPORTED_VERSION.minor..=impl_::STABLE_ABI_MAX_MINOR + 1 {
println!("cargo:rustc-check-cfg=cfg(Py_3_{i})");
}

// pyo3_dll cfg for raw-dylib linking on Windows
let mut dll_names = vec![
"python3".to_string(),
"python3_d".to_string(),
"python3t".to_string(),
"python3t_d".to_string(),
];
for i in impl_::MINIMUM_SUPPORTED_VERSION.minor..=impl_::STABLE_ABI_MAX_MINOR + 1 {
dll_names.push(format!("python3{i}"));
dll_names.push(format!("python3{i}_d"));
if i >= 13 {
dll_names.push(format!("python3{i}t"));
dll_names.push(format!("python3{i}t_d"));
}
}
// PyPy DLL names (libpypy3.X-c.dll)
for i in
impl_::MINIMUM_SUPPORTED_VERSION_PYPY.minor..=impl_::MAXIMUM_SUPPORTED_VERSION_PYPY.minor
{
dll_names.push(format!("libpypy3.{i}-c"));
}
let values = dll_names
.iter()
.map(|n| format!("\"{n}\""))
.collect::<Vec<_>>()
.join(", ");
println!("cargo:rustc-check-cfg=cfg(pyo3_dll, values({values}))");
}

/// Private exports used in PyO3's build.rs
Expand Down
6 changes: 2 additions & 4 deletions pyo3-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ libc = "0.2.62"

default = []

# deprecated
extension-module = ["pyo3-build-config/extension-module"]

# Use the Python limited API. See https://www.python.org/dev/peps/pep-0384/ for more.
abi3 = []

Expand All @@ -39,7 +36,8 @@ abi3-py315 = ["abi3"]

abi3t-py315 = ["abi3t"]

# deprecated: no longer needed, raw-dylib is used instead
# deprecated
extension-module = ["pyo3-build-config/extension-module"]
generate-import-lib = ["pyo3-build-config/generate-import-lib"]

[dev-dependencies]
Expand Down
Loading
Loading