Skip to content

Commit 089ad42

Browse files
committed
Fix cargo test with extension-module feature.
1 parent 7eb3aa3 commit 089ad42

File tree

3 files changed

+49
-45
lines changed

3 files changed

+49
-45
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
4242
- Remove `PYO3_CROSS_INCLUDE_DIR` environment variable and the associated C header parsing functionality.
4343

4444
### Fixed
45+
- Fix `cargo test` with `extension-module` feature. (Requires `cargo +nightly -Zextra-link-arg test` for now.) #[1123](https://github.com/PyO3/pyo3/pull/1123)
4546
- Remove FFI definition `PyCFunction_ClearFreeList` for Python 3.9 and later. [#1425](https://github.com/PyO3/pyo3/pull/1425)
4647
- `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)
4748
- Fix FFI definition `_PyEval_RequestCodeExtraIndex` which took an argument of the wrong type. [#1429](https://github.com/PyO3/pyo3/pull/1429)

build.rs

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ struct InterpreterConfig {
3737
libdir: Option<String>,
3838
shared: bool,
3939
ld_version: String,
40-
/// Prefix used for determining the directory of libpython
41-
base_prefix: String,
4240
executable: PathBuf,
4341
calcsize_pointer: Option<u32>,
4442
implementation: PythonInterpreterKind,
@@ -468,7 +466,6 @@ fn load_cross_compile_from_sysconfigdata(
468466
libdir: cross_compile_config.lib_dir.to_str().map(String::from),
469467
shared: sysconfig_data.get_bool("Py_ENABLE_SHARED")?,
470468
ld_version,
471-
base_prefix: "".to_string(),
472469
executable: PathBuf::new(),
473470
calcsize_pointer,
474471
implementation: PythonInterpreterKind::CPython,
@@ -508,7 +505,6 @@ fn windows_hardcoded_cross_compile(
508505
libdir: cross_compile_config.lib_dir.to_str().map(String::from),
509506
shared: true,
510507
ld_version: format!("{}.{}", major, minor),
511-
base_prefix: "".to_string(),
512508
executable: PathBuf::new(),
513509
calcsize_pointer: None,
514510
implementation: PythonInterpreterKind::CPython,
@@ -574,8 +570,15 @@ fn run_python_script(interpreter: &Path, script: &str) -> Result<String> {
574570
}
575571
}
576572

577-
fn get_rustc_link_lib(config: &InterpreterConfig) -> String {
578-
let link_name = if env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "windows" {
573+
fn get_library_link_name_unix(config: &InterpreterConfig) -> String {
574+
match config.implementation {
575+
PythonInterpreterKind::CPython => format!("python{}", config.ld_version),
576+
PythonInterpreterKind::PyPy => format!("pypy{}-c", config.version.major),
577+
}
578+
}
579+
580+
fn get_library_link_name(config: &InterpreterConfig) -> String {
581+
if env::var("CARGO_CFG_TARGET_OS").unwrap().as_str() == "windows" {
579582
if is_abi3() {
580583
// Link against python3.lib for the stable ABI on Windows.
581584
// See https://www.python.org/dev/peps/pep-0384/#linkage
@@ -595,17 +598,8 @@ fn get_rustc_link_lib(config: &InterpreterConfig) -> String {
595598
)
596599
}
597600
} else {
598-
match config.implementation {
599-
PythonInterpreterKind::CPython => format!("python{}", config.ld_version),
600-
PythonInterpreterKind::PyPy => format!("pypy{}-c", config.version.major),
601-
}
602-
};
603-
604-
format!(
605-
"cargo:rustc-link-lib={link_model}{link_name}",
606-
link_model = if config.shared { "" } else { "static=" },
607-
link_name = link_name
608-
)
601+
get_library_link_name_unix(config)
602+
}
609603
}
610604

611605
fn find_interpreter() -> Result<PathBuf> {
@@ -670,15 +664,17 @@ PYPY = platform.python_implementation() == "PyPy"
670664
# LTO the static library (and failing with newer gcc's, because it is old).
671665
ANACONDA = os.path.exists(os.path.join(sys.base_prefix, "conda-meta"))
672666
673-
libdir = get_config_var("LIBDIR")
667+
if platform.system() == "Windows":
668+
libdir = sys.base_prefix + "\\libs"
669+
else:
670+
libdir = get_config_var("LIBDIR")
674671
675672
print("version_major", sys.version_info[0])
676673
print("version_minor", sys.version_info[1])
677674
print("implementation", platform.python_implementation())
678675
if libdir is not None:
679676
print("libdir", libdir)
680677
print("ld_version", get_config_var("LDVERSION") or get_config_var("py_version_short"))
681-
print("base_prefix", sys.base_prefix)
682678
print("framework", bool(get_config_var("PYTHONFRAMEWORK")))
683679
print("shared", PYPY or ANACONDA or bool(get_config_var("Py_ENABLE_SHARED")))
684680
print("executable", sys.executable)
@@ -708,7 +704,6 @@ print("calcsize_pointer", struct.calcsize("P"))
708704
libdir: map.get("libdir").cloned(),
709705
shared,
710706
ld_version: map["ld_version"].clone(),
711-
base_prefix: map["base_prefix"].clone(),
712707
executable: map["executable"].clone().into(),
713708
calcsize_pointer: Some(map["calcsize_pointer"].parse()?),
714709
})
@@ -733,28 +728,35 @@ fn configure(interpreter_config: &InterpreterConfig) -> Result<()> {
733728
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
734729
let is_extension_module = env::var_os("CARGO_FEATURE_EXTENSION_MODULE").is_some();
735730
match (is_extension_module, target_os.as_str()) {
736-
(_, "windows") => {
737-
// always link on windows, even with extension module
738-
println!("{}", get_rustc_link_lib(&interpreter_config));
731+
(_, "windows") | (_, "android") | (false, _) => {
732+
// windows or android - always link to libpython
733+
// other systems - only link libs if not extension module
734+
735+
if let Some(libdir) = &interpreter_config.libdir {
736+
println!("cargo:rustc-link-search=native={}", libdir);
737+
}
739738
println!(
740-
"cargo:rustc-link-search=native={}\\libs",
741-
interpreter_config.base_prefix
739+
"cargo:rustc-link-lib={link_model}{link_name}",
740+
link_model = if interpreter_config.shared {
741+
""
742+
} else {
743+
"static="
744+
},
745+
link_name = get_library_link_name(&interpreter_config)
742746
);
743747
}
744-
(true, "macos") => {
745-
// with extension module on macos some extra linker arguments are needed
746-
println!("cargo:rustc-cdylib-link-arg=-undefined");
747-
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
748-
}
749-
(false, _) | (_, "android") => {
750-
// other systems, only link libs if not extension module
751-
// android always link.
752-
println!("{}", get_rustc_link_lib(&interpreter_config));
753-
if let Some(libdir) = &interpreter_config.libdir {
754-
println!("cargo:rustc-link-search=native={}", libdir);
748+
(true, _) => {
749+
// Extension module on unix system - only link non-lib targets
750+
if target_os == "macos" {
751+
// with extension module on macos some extra linker arguments are needed
752+
println!("cargo:rustc-cdylib-link-arg=-undefined");
753+
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
755754
}
755+
756+
// Extension module on unix - only link non-lib targets
757+
let lib_name = get_library_link_name_unix(&interpreter_config);
758+
println!("cargo:rustc-link-arg-bins=-l{}", lib_name);
756759
}
757-
_ => {}
758760
}
759761

760762
if interpreter_config.shared {

guide/src/faq.md

Lines changed: 9 additions & 8 deletions
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)