Skip to content

Commit

Permalink
Fix cargo test with extension-module feature.
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Apr 26, 2021
1 parent 28ccef2 commit 972b827
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Remove `PYO3_CROSS_INCLUDE_DIR` environment variable and the associated C header parsing functionality.

### Fixed
- Fix `cargo test` with `extension-module` feature. (Requires `cargo +nightly -Zextra-link-arg test` for now.) #[1123](https://github.com/PyO3/pyo3/pull/1123)
- Remove FFI definition `PyCFunction_ClearFreeList` for Python 3.9 and later. [#1425](https://github.com/PyO3/pyo3/pull/1425)
- `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)
- Fix FFI definition `_PyEval_RequestCodeExtraIndex` which took an argument of the wrong type. [#1429](https://github.com/PyO3/pyo3/pull/1429)
Expand Down
72 changes: 42 additions & 30 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -587,8 +587,15 @@ fn run_python_script(interpreter: &Path, script: &str) -> Result<String> {
}
}

fn get_rustc_link_lib(config: &InterpreterConfig) -> String {
let link_name = if cargo_env_var("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
fn get_library_link_name_unix(config: &InterpreterConfig) -> String {
match config.implementation {
PythonInterpreterKind::CPython => format!("python{}", config.ld_version),
PythonInterpreterKind::PyPy => format!("pypy{}-c", config.version.major),
}
}

fn get_library_link_name(config: &InterpreterConfig) -> String {
if cargo_env_var("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
if is_abi3() {
// Link against python3.lib for the stable ABI on Windows.
// See https://www.python.org/dev/peps/pep-0384/#linkage
Expand All @@ -608,17 +615,8 @@ fn get_rustc_link_lib(config: &InterpreterConfig) -> String {
)
}
} else {
match config.implementation {
PythonInterpreterKind::CPython => format!("python{}", config.ld_version),
PythonInterpreterKind::PyPy => format!("pypy{}-c", config.version.major),
}
};

format!(
"cargo:rustc-link-lib={link_model}{link_name}",
link_model = if config.shared { "" } else { "static=" },
link_name = link_name
)
get_library_link_name_unix(config)
}
}

fn get_venv_path() -> Option<PathBuf> {
Expand Down Expand Up @@ -764,26 +762,35 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result<()> {
let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap();
let is_extension_module = cargo_env_var("CARGO_FEATURE_EXTENSION_MODULE").is_some();
match (is_extension_module, target_os.as_str()) {
(_, "windows") => {
// always link on windows, even with extension module
println!("{}", get_rustc_link_lib(&interpreter_config));
(_, "windows") | (_, "android") | (false, _) => {
// windows or android - always link to libpython
// other systems - only link libs if not extension module

if let Some(libdir) = &interpreter_config.libdir {
println!("cargo:rustc-link-search=native={}", libdir);
}
if target_os == "windows" {
println!("cargo:rustc-link-search=native={}\\libs", interpreter_config.base_prefix);
}

println!(
"cargo:rustc-link-search=native={}\\libs",
interpreter_config.base_prefix
"cargo:rustc-link-lib={link_model}{link_name}",
link_model = if interpreter_config.shared {
""
} else {
"static="
},
link_name = get_library_link_name(&interpreter_config)
);
}
(true, "macos") => {
// with extension module on macos some extra linker arguments are needed
println!("cargo:rustc-cdylib-link-arg=-undefined");
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
}
(false, _) | (_, "android") => {
// other systems, only link libs if not extension module
// android always link.
println!("{}", get_rustc_link_lib(&interpreter_config));
if let Some(libdir) = &interpreter_config.libdir {
println!("cargo:rustc-link-search=native={}", libdir);
(true, _) => {
// Extension module on unix system - only link non-lib targets
if target_os == "macos" {
// with extension module on macos some extra linker arguments are needed
println!("cargo:rustc-cdylib-link-arg=-undefined");
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
}

if interpreter_config.implementation == PythonInterpreterKind::PyPy {
// PyPy 3.7 changed LIBDIR to point to base_prefix/lib, so need to hard-code /bin
// search path too: https://foss.heptapod.net/pypy/pypy/-/issues/3442
Expand All @@ -792,8 +799,13 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result<()> {
interpreter_config.base_prefix
);
}

let lib_name = get_library_link_name_unix(&interpreter_config);
println!("cargo:rustc-link-arg-bins=-l{}", lib_name);
println!("cargo:rustc-link-arg-tests=-l{}", lib_name);
println!("cargo:rustc-link-arg-benches=-l{}", lib_name);
println!("cargo:rustc-link-arg-examples=-l{}", lib_name);
}
_ => {}
}

if env::var_os("CARGO_FEATURE_AUTO_INITIALIZE").is_some() {
Expand Down
17 changes: 9 additions & 8 deletions guide/src/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@ PyO3 provides a struct [`GILOnceCell`] which works equivalently to `OnceCell` bu

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

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

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`:
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.

```toml
[dependencies.pyo3]
version = "{{#PYO3_VERSION}}"
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.:

[features]
extension-module = ["pyo3/extension-module"]
default = ["extension-module"]
```
# For cargo test
cargo +nightly -Zextra-link-arg test
# For cargo run
cargo +nightly -Zextra-link-arg run
```

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

0 comments on commit 972b827

Please sign in to comment.