diff --git a/newsfragments/5624.added.md b/newsfragments/5624.added.md new file mode 100644 index 00000000000..670a23039d9 --- /dev/null +++ b/newsfragments/5624.added.md @@ -0,0 +1 @@ +Add `pyo3_build_config::add_libpython_rpath_link_args`. diff --git a/pyo3-build-config/src/lib.rs b/pyo3-build-config/src/lib.rs index 0544376e3b4..5011470bfa9 100644 --- a/pyo3-build-config/src/lib.rs +++ b/pyo3-build-config/src/lib.rs @@ -73,6 +73,44 @@ fn _add_extension_module_link_args(triple: &Triple, mut writer: impl std::io::Wr } } +/// Adds linker arguments to set rpath when embedding Python within a Rust binary. +/// +/// When running tests or binaries built with PyO3, the Python dynamic library needs +/// to be found at runtime. +/// +/// This can be done by setting environment variables like `DYLD_LIBRARY_PATH` on macOS, +/// `LD_LIBRARY_PATH` on Linux, or `PATH` on Windows. +/// +/// Altrnatively (as per this function) rpath can be set at link time to point to the +/// directory containing the Python dynamic library. This avoids the need to set environment +/// variables, so can be convenient, however may not be appropriate for binaries packaged +/// for distribution. +/// +#[doc = concat!("[See PyO3's guide](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/building-and-distribution#dynamically-embedding-the-python-interpreter)")] +/// for more details. +#[cfg(feature = "resolve-config")] +pub fn add_libpython_rpath_link_args() { + let target = impl_::target_triple_from_env(); + _add_libpython_rpath_link_args( + get(), + impl_::is_linking_libpython_for_target(&target), + std::io::stdout(), + ) +} + +#[cfg(feature = "resolve-config")] +fn _add_libpython_rpath_link_args( + interpreter_config: &InterpreterConfig, + is_linking_libpython: bool, + mut writer: impl std::io::Write, +) { + if is_linking_libpython { + if let Some(lib_dir) = interpreter_config.lib_dir.as_ref() { + writeln!(writer, "cargo:rustc-link-arg=-Wl,-rpath,{lib_dir}").unwrap(); + } + } +} + /// Adds linker arguments suitable for linking against the Python framework on macOS. /// /// This should be called from a build script. diff --git a/pyo3-ffi-check/Cargo.toml b/pyo3-ffi-check/Cargo.toml index cdf38c9f9bf..6c861139098 100644 --- a/pyo3-ffi-check/Cargo.toml +++ b/pyo3-ffi-check/Cargo.toml @@ -6,10 +6,7 @@ publish = false [dependencies] pyo3-ffi-check-macro = { path = "./macro" } - -[dependencies.pyo3-ffi] -path = "../pyo3-ffi" -features = ["extension-module"] # A lazy way of skipping linking in most cases (as we don't use any runtime symbols) +pyo3-ffi = { path = "../pyo3-ffi" } [build-dependencies] bindgen = "0.69.4" diff --git a/pyo3-ffi-check/build.rs b/pyo3-ffi-check/build.rs index e7cfbe40df3..d6c4fb765b6 100644 --- a/pyo3-ffi-check/build.rs +++ b/pyo3-ffi-check/build.rs @@ -20,7 +20,10 @@ impl bindgen::callbacks::ParseCallbacks for ParseCallbacks { } fn main() { + pyo3_build_config::add_libpython_rpath_link_args(); + let config = pyo3_build_config::get(); + let python_include_dir = config .run_python_script( "import sysconfig; print(sysconfig.get_config_var('INCLUDEPY'), end='');",