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 6, 2021
1 parent 7eb3aa3 commit 089ad42
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 45 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,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
76 changes: 39 additions & 37 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ struct InterpreterConfig {
libdir: Option<String>,
shared: bool,
ld_version: String,
/// Prefix used for determining the directory of libpython
base_prefix: String,
executable: PathBuf,
calcsize_pointer: Option<u32>,
implementation: PythonInterpreterKind,
Expand Down Expand Up @@ -468,7 +466,6 @@ fn load_cross_compile_from_sysconfigdata(
libdir: cross_compile_config.lib_dir.to_str().map(String::from),
shared: sysconfig_data.get_bool("Py_ENABLE_SHARED")?,
ld_version,
base_prefix: "".to_string(),
executable: PathBuf::new(),
calcsize_pointer,
implementation: PythonInterpreterKind::CPython,
Expand Down Expand Up @@ -508,7 +505,6 @@ fn windows_hardcoded_cross_compile(
libdir: cross_compile_config.lib_dir.to_str().map(String::from),
shared: true,
ld_version: format!("{}.{}", major, minor),
base_prefix: "".to_string(),
executable: PathBuf::new(),
calcsize_pointer: None,
implementation: PythonInterpreterKind::CPython,
Expand Down Expand Up @@ -574,8 +570,15 @@ fn run_python_script(interpreter: &Path, script: &str) -> Result<String> {
}
}

fn get_rustc_link_lib(config: &InterpreterConfig) -> String {
let link_name = if env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "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 env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "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 @@ -595,17 +598,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 find_interpreter() -> Result<PathBuf> {
Expand Down Expand Up @@ -670,15 +664,17 @@ PYPY = platform.python_implementation() == "PyPy"
# LTO the static library (and failing with newer gcc's, because it is old).
ANACONDA = os.path.exists(os.path.join(sys.base_prefix, "conda-meta"))
libdir = get_config_var("LIBDIR")
if platform.system() == "Windows":
libdir = sys.base_prefix + "\\libs"
else:
libdir = get_config_var("LIBDIR")
print("version_major", sys.version_info[0])
print("version_minor", sys.version_info[1])
print("implementation", platform.python_implementation())
if libdir is not None:
print("libdir", libdir)
print("ld_version", get_config_var("LDVERSION") or get_config_var("py_version_short"))
print("base_prefix", sys.base_prefix)
print("framework", bool(get_config_var("PYTHONFRAMEWORK")))
print("shared", PYPY or ANACONDA or bool(get_config_var("Py_ENABLE_SHARED")))
print("executable", sys.executable)
Expand Down Expand Up @@ -708,7 +704,6 @@ print("calcsize_pointer", struct.calcsize("P"))
libdir: map.get("libdir").cloned(),
shared,
ld_version: map["ld_version"].clone(),
base_prefix: map["base_prefix"].clone(),
executable: map["executable"].clone().into(),
calcsize_pointer: Some(map["calcsize_pointer"].parse()?),
})
Expand All @@ -733,28 +728,35 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result<()> {
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let is_extension_module = env::var_os("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);
}
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");
}

// Extension module on unix - only link non-lib targets
let lib_name = get_library_link_name_unix(&interpreter_config);
println!("cargo:rustc-link-arg-bins=-l{}", lib_name);
}
_ => {}
}

if interpreter_config.shared {
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 089ad42

Please sign in to comment.