Skip to content

Commit 972b827

Browse files
committed
Fix cargo test with extension-module feature.
1 parent 28ccef2 commit 972b827

File tree

3 files changed

+52
-38
lines changed

3 files changed

+52
-38
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
5151
- Remove `PYO3_CROSS_INCLUDE_DIR` environment variable and the associated C header parsing functionality.
5252

5353
### Fixed
54+
- Fix `cargo test` with `extension-module` feature. (Requires `cargo +nightly -Zextra-link-arg test` for now.) #[1123](https://github.com/PyO3/pyo3/pull/1123)
5455
- Remove FFI definition `PyCFunction_ClearFreeList` for Python 3.9 and later. [#1425](https://github.com/PyO3/pyo3/pull/1425)
5556
- `PYO3_CROSS_LIB_DIR` enviroment variable no long required when compiling for x86-64 Python from macOS arm64 and reverse. [#1428](https://github.com/PyO3/pyo3/pull/1428)
5657
- Fix FFI definition `_PyEval_RequestCodeExtraIndex` which took an argument of the wrong type. [#1429](https://github.com/PyO3/pyo3/pull/1429)

build.rs

+42-30
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,15 @@ fn run_python_script(interpreter: &Path, script: &str) -> Result<String> {
587587
}
588588
}
589589

590-
fn get_rustc_link_lib(config: &InterpreterConfig) -> String {
591-
let link_name = if cargo_env_var("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
590+
fn get_library_link_name_unix(config: &InterpreterConfig) -> String {
591+
match config.implementation {
592+
PythonInterpreterKind::CPython => format!("python{}", config.ld_version),
593+
PythonInterpreterKind::PyPy => format!("pypy{}-c", config.version.major),
594+
}
595+
}
596+
597+
fn get_library_link_name(config: &InterpreterConfig) -> String {
598+
if cargo_env_var("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
592599
if is_abi3() {
593600
// Link against python3.lib for the stable ABI on Windows.
594601
// See https://www.python.org/dev/peps/pep-0384/#linkage
@@ -608,17 +615,8 @@ fn get_rustc_link_lib(config: &InterpreterConfig) -> String {
608615
)
609616
}
610617
} else {
611-
match config.implementation {
612-
PythonInterpreterKind::CPython => format!("python{}", config.ld_version),
613-
PythonInterpreterKind::PyPy => format!("pypy{}-c", config.version.major),
614-
}
615-
};
616-
617-
format!(
618-
"cargo:rustc-link-lib={link_model}{link_name}",
619-
link_model = if config.shared { "" } else { "static=" },
620-
link_name = link_name
621-
)
618+
get_library_link_name_unix(config)
619+
}
622620
}
623621

624622
fn get_venv_path() -> Option<PathBuf> {
@@ -764,26 +762,35 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result<()> {
764762
let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap();
765763
let is_extension_module = cargo_env_var("CARGO_FEATURE_EXTENSION_MODULE").is_some();
766764
match (is_extension_module, target_os.as_str()) {
767-
(_, "windows") => {
768-
// always link on windows, even with extension module
769-
println!("{}", get_rustc_link_lib(&interpreter_config));
765+
(_, "windows") | (_, "android") | (false, _) => {
766+
// windows or android - always link to libpython
767+
// other systems - only link libs if not extension module
768+
769+
if let Some(libdir) = &interpreter_config.libdir {
770+
println!("cargo:rustc-link-search=native={}", libdir);
771+
}
772+
if target_os == "windows" {
773+
println!("cargo:rustc-link-search=native={}\\libs", interpreter_config.base_prefix);
774+
}
775+
770776
println!(
771-
"cargo:rustc-link-search=native={}\\libs",
772-
interpreter_config.base_prefix
777+
"cargo:rustc-link-lib={link_model}{link_name}",
778+
link_model = if interpreter_config.shared {
779+
""
780+
} else {
781+
"static="
782+
},
783+
link_name = get_library_link_name(&interpreter_config)
773784
);
774785
}
775-
(true, "macos") => {
776-
// with extension module on macos some extra linker arguments are needed
777-
println!("cargo:rustc-cdylib-link-arg=-undefined");
778-
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
779-
}
780-
(false, _) | (_, "android") => {
781-
// other systems, only link libs if not extension module
782-
// android always link.
783-
println!("{}", get_rustc_link_lib(&interpreter_config));
784-
if let Some(libdir) = &interpreter_config.libdir {
785-
println!("cargo:rustc-link-search=native={}", libdir);
786+
(true, _) => {
787+
// Extension module on unix system - only link non-lib targets
788+
if target_os == "macos" {
789+
// with extension module on macos some extra linker arguments are needed
790+
println!("cargo:rustc-cdylib-link-arg=-undefined");
791+
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
786792
}
793+
787794
if interpreter_config.implementation == PythonInterpreterKind::PyPy {
788795
// PyPy 3.7 changed LIBDIR to point to base_prefix/lib, so need to hard-code /bin
789796
// search path too: https://foss.heptapod.net/pypy/pypy/-/issues/3442
@@ -792,8 +799,13 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result<()> {
792799
interpreter_config.base_prefix
793800
);
794801
}
802+
803+
let lib_name = get_library_link_name_unix(&interpreter_config);
804+
println!("cargo:rustc-link-arg-bins=-l{}", lib_name);
805+
println!("cargo:rustc-link-arg-tests=-l{}", lib_name);
806+
println!("cargo:rustc-link-arg-benches=-l{}", lib_name);
807+
println!("cargo:rustc-link-arg-examples=-l{}", lib_name);
795808
}
796-
_ => {}
797809
}
798810

799811
if env::var_os("CARGO_FEATURE_AUTO_INITIALIZE").is_some() {

guide/src/faq.md

+9-8
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@ PyO3 provides a struct [`GILOnceCell`] which works equivalently to `OnceCell` bu
1515

1616
[`GILOnceCell`]: {{#PYO3_DOCS_URL}}/pyo3/once_cell/struct.GILOnceCell.html
1717

18-
## I can't run `cargo test`: I'm having linker issues like "Symbol not found" or "Undefined reference to _PyExc_SystemError"!
18+
## I can't run `cargo test` or `cargo run`: I'm having linker issues like "Symbol not found" or "Undefined reference to _PyExc_SystemError"!
1919

20-
Currently, [#341](https://github.com/PyO3/pyo3/issues/341) causes `cargo test` to fail with linking errors when the `extension-module` feature is activated. For now you can work around this by making the `extension-module` feature optional and running the tests with `cargo test --no-default-features`:
20+
On unix operating systems the `extension-module` feature is required to disable linking against libpython to meet criteria of how Python extension modules should be built.
2121

22-
```toml
23-
[dependencies.pyo3]
24-
version = "{{#PYO3_VERSION}}"
22+
PyO3 is able to re-enable linking for binaries and tests in the project, but it requires a nightly cargo feature. To use this feature, you must opt into it, e.g.:
2523

26-
[features]
27-
extension-module = ["pyo3/extension-module"]
28-
default = ["extension-module"]
24+
```
25+
# For cargo test
26+
cargo +nightly -Zextra-link-arg test
27+
28+
# For cargo run
29+
cargo +nightly -Zextra-link-arg run
2930
```
3031

3132
## I can't run `cargo test`: my crate cannot be found for tests in `tests/` directory!

0 commit comments

Comments
 (0)