diff --git a/CHANGELOG.md b/CHANGELOG.md index 60bba62ff9c..fda04d1dd12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add new public `pyo3-build-config` API using the types from `target_lexicon` crate. Deprecate `cross_compiling()`. [#2253](https://github.com/PyO3/pyo3/pull/2253) - Allow dependent crates to access config values from `pyo3-build-config` via cargo link dep env vars. [#2092](https://github.com/PyO3/pyo3/pull/2092) - Added methods on `InterpreterConfig` to run Python scripts using the configured executable. [#2092](https://github.com/PyO3/pyo3/pull/2092) - Added FFI definitions for `PyType_FromModuleAndSpec`, `PyType_GetModule`, `PyType_GetModuleState` and `PyModule_AddType`. [#2250](https://github.com/PyO3/pyo3/pull/2250) diff --git a/pyo3-build-config/Cargo.toml b/pyo3-build-config/Cargo.toml index 2233dd78a4e..96d9c3717ee 100644 --- a/pyo3-build-config/Cargo.toml +++ b/pyo3-build-config/Cargo.toml @@ -12,6 +12,10 @@ edition = "2018" [dependencies] once_cell = "1" +target-lexicon = "0.12" + +[build-dependencies] +target-lexicon = "0.12" [features] default = [] diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index c3dcff32d97..0439951df4c 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -12,6 +12,10 @@ use std::{ str::FromStr, }; +pub use target_lexicon::Triple; + +use target_lexicon::{Architecture, BinaryFormat, Environment, OperatingSystem, Vendor}; + use crate::{ bail, ensure, errors::{Context, Error, Result}, @@ -40,6 +44,16 @@ pub fn env_var(var: &str) -> Option { env::var_os(var) } +/// Gets the compilation target triple from environment variables set by Cargo. +/// +/// Must be called from a crate build script. +pub fn target_triple_from_env() -> Triple { + env::var("TARGET") + .expect("target_triple_from_env() must be called from a build script") + .parse() + .expect("Unrecognized TARGET environment variable value") +} + /// Configuration needed by PyO3 to build for the correct Python implementation. /// /// Usually this is queried directly from the Python interpreter, or overridden using the @@ -524,6 +538,29 @@ print("mingw", get_platform().startswith("mingw")) envs, ) } + + /// Lowers the configured version to the abi3 version, if set. + fn fixup_for_abi3_version(&mut self, abi3_version: Option) -> Result<()> { + // PyPy doesn't support abi3; don't adjust the version + if self.implementation.is_pypy() { + return Ok(()); + } + + if let Some(version) = abi3_version { + ensure!( + version <= self.version, + "cannot set a minimum Python version {} higher than the interpreter version {} \ + (the minimum Python version is implied by the abi3-py3{} feature)", + version, + self.version, + version.minor, + ); + + self.version = version; + } + + Ok(()) + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -608,52 +645,37 @@ fn is_abi3() -> bool { cargo_env_var("CARGO_FEATURE_ABI3").is_some() } -#[derive(Debug, PartialEq)] -struct TargetInfo { - /// The `arch` component of the compilation target triple. - /// - /// e.g. x86_64, i386, arm, thumb, mips, etc. - arch: String, - - /// The `vendor` component of the compilation target triple. - /// - /// e.g. apple, pc, unknown, etc. - vendor: String, +/// Gets the minimum supported Python version from PyO3 `abi3-py*` features. +/// +/// Must be called from a PyO3 crate build script. +pub fn get_abi3_version() -> Option { + let minor_version = (MINIMUM_SUPPORTED_VERSION.minor..=ABI3_MAX_MINOR) + .find(|i| cargo_env_var(&format!("CARGO_FEATURE_ABI3_PY3{}", i)).is_some()); + minor_version.map(|minor| PythonVersion { major: 3, minor }) +} - /// The `os` component of the compilation target triple. - /// - /// e.g. darwin, freebsd, linux, windows, etc. - os: String, +/// Checks if the `extension-module` feature is enabled for the PyO3 crate. +/// +/// Must be called from a PyO3 crate build script. +pub fn is_extension_module() -> bool { + cargo_env_var("CARGO_FEATURE_EXTENSION_MODULE").is_some() } -impl TargetInfo { - fn from_cargo_env() -> Result { - Ok(Self { - arch: cargo_env_var("CARGO_CFG_TARGET_ARCH") - .ok_or("expected CARGO_CFG_TARGET_ARCH env var")?, - vendor: cargo_env_var("CARGO_CFG_TARGET_VENDOR") - .ok_or("expected CARGO_CFG_TARGET_VENDOR env var")?, - os: cargo_env_var("CARGO_CFG_TARGET_OS") - .ok_or("expected CARGO_CFG_TARGET_OS env var")?, - }) - } +/// Checks if we need to link to `libpython` for the current build target. +/// +/// Must be called from a crate PyO3 build script. +pub fn is_linking_libpython() -> bool { + is_linking_libpython_for_target(&target_triple_from_env()) +} - fn to_target_triple(&self) -> String { - format!( - "{}-{}-{}", - if self.arch == "x86" { - "i686" - } else { - &self.arch - }, - self.vendor, - if self.os == "macos" { - "darwin" - } else { - &self.os - } - ) - } +/// Checks if we need to link to `libpython` for the target. +/// +/// Must be called from a crate PyO3 build script. +fn is_linking_libpython_for_target(target: &Triple) -> bool { + target.operating_system == OperatingSystem::Windows + || target.environment == Environment::Android + || target.environment == Environment::Androideabi + || !is_extension_module() } /// Configuration needed by PyO3 to cross-compile for a target platform. @@ -668,32 +690,63 @@ pub struct CrossCompileConfig { /// The version of the Python library to link against. version: Option, - /// The target information - target_info: TargetInfo, + /// The compile target triple (e.g. aarch64-unknown-linux-gnu) + target: Triple, } impl CrossCompileConfig { - fn from_env_vars(env_vars: CrossCompileEnvVars, target_info: TargetInfo) -> Result { - Ok(CrossCompileConfig { - lib_dir: env_vars - .pyo3_cross_lib_dir - .ok_or( - "The PYO3_CROSS_LIB_DIR environment variable must be set when cross-compiling", - )? - .into(), - target_info, - version: env_vars - .pyo3_cross_python_version - .map(|os_string| { - let utf8_str = os_string - .to_str() - .ok_or("PYO3_CROSS_PYTHON_VERSION is not valid utf-8.")?; - utf8_str - .parse() - .context("failed to parse PYO3_CROSS_PYTHON_VERSION") - }) - .transpose()?, - }) + /// Creates a new cross compile config struct from PyO3 environment variables + /// and the build environment when cross compilation mode is detected. + /// + /// Returns `None` when not cross compiling. + fn try_from_env_vars_host_target( + env_vars: CrossCompileEnvVars, + host: &Triple, + target: &Triple, + ) -> Result> { + if env_vars.any() || Self::is_cross_compiling_from_to(host, target) { + let lib_dir = env_vars.lib_dir_path()?; + let version = env_vars.parse_version()?; + let target = target.clone(); + + Ok(Some(CrossCompileConfig { + lib_dir, + target, + version, + })) + } else { + Ok(None) + } + } + + /// Checks if compiling on `host` for `target` required "real" cross compilation. + /// + /// Returns `false` if the target Python interpreter can run on the host. + fn is_cross_compiling_from_to(host: &Triple, target: &Triple) -> bool { + // Not cross-compiling if arch-vendor-os is all the same + // e.g. x86_64-unknown-linux-musl on x86_64-unknown-linux-gnu host + // x86_64-pc-windows-gnu on x86_64-pc-windows-msvc host + let mut compatible = host.architecture == target.architecture + && host.vendor == target.vendor + && host.operating_system == target.operating_system; + + // Not cross-compiling to compile for 32-bit Python from windows 64-bit + compatible |= target.operating_system == OperatingSystem::Windows + && host.operating_system == OperatingSystem::Windows; + + // Not cross-compiling to compile for x86-64 Python from macOS arm64 and vice versa + compatible |= target.operating_system == OperatingSystem::Darwin + && host.operating_system == OperatingSystem::Darwin; + + !compatible + } + + /// Converts `lib_dir` member field to an UTF-8 string. + /// + /// The conversion can not fail because `PYO3_CROSS_LIB_DIR` variable + /// is ensured contain a valid UTF-8 string. + fn lib_dir_string(&self) -> String { + self.lib_dir.to_str().unwrap().to_owned() } } @@ -709,6 +762,44 @@ impl CrossCompileEnvVars { || self.pyo3_cross_lib_dir.is_some() || self.pyo3_cross_python_version.is_some() } + + /// Parses `PYO3_CROSS_PYTHON_VERSION` environment variable value + /// into `PythonVersion`. + fn parse_version(&self) -> Result> { + let version = self + .pyo3_cross_python_version + .as_ref() + .map(|os_string| { + let utf8_str = os_string + .to_str() + .ok_or("PYO3_CROSS_PYTHON_VERSION is not valid a UTF-8 string")?; + utf8_str + .parse() + .context("failed to parse PYO3_CROSS_PYTHON_VERSION") + }) + .transpose()?; + + Ok(version) + } + + /// Converts the stored `PYO3_CROSS_LIB_DIR` variable value (if any) + /// into a `PathBuf` instance. + /// + /// Ensures that the path is a valid UTF-8 string. + fn lib_dir_path(&self) -> Result { + let lib_dir = self.pyo3_cross_lib_dir.as_ref().map(PathBuf::from); + + if let Some(dir) = lib_dir.as_ref() { + ensure!( + dir.to_str().is_some(), + "PYO3_CROSS_LIB_DIR variable value is not a valid UTF-8 string" + ); + Ok(dir.clone()) + } else { + // FIXME: Relax this restriction in the future. + bail!("The PYO3_CROSS_LIB_DIR environment variable must be set when cross-compiling") + } + } } pub(crate) fn cross_compile_env_vars() -> CrossCompileEnvVars { @@ -733,39 +824,76 @@ pub(crate) fn cross_compile_env_vars() -> CrossCompileEnvVars { /// `PYO3_CROSS_LIB_DIR`. /// /// See the [PyO3 User Guide](https://pyo3.rs/) for more info on cross-compiling. +#[deprecated( + since = "0.17.0", + note = "please use cross_compiling_from_to() instead" +)] pub fn cross_compiling( host: &str, target_arch: &str, target_vendor: &str, target_os: &str, ) -> Result> { - let env_vars = cross_compile_env_vars(); + let host: Triple = host.parse().map_err(|_| "bad host triple")?; + + let architecture: Architecture = target_arch.parse().map_err(|_| "bad target arch")?; + let vendor: Vendor = target_vendor.parse().map_err(|_| "bad target vendor")?; + let operating_system: OperatingSystem = target_os.parse().map_err(|_| "bad target os")?; - let target_info = TargetInfo { - arch: target_arch.to_owned(), - vendor: target_vendor.to_owned(), - os: target_os.to_owned(), + // FIXME: This is a very bad approximation that only works + // for the current `CrossCompileConfig` implementation. + let environment = match operating_system { + OperatingSystem::Windows => Environment::Msvc, + _ => Environment::Gnu, }; - if !env_vars.any() && is_not_cross_compiling(host, &target_info) { - return Ok(None); - } + // FIXME: This field is currently unused. + let binary_format = BinaryFormat::Elf; + + let target = Triple { + architecture, + vendor, + operating_system, + environment, + binary_format, + }; - CrossCompileConfig::from_env_vars(env_vars, target_info).map(Some) + cross_compiling_from_to(&host, &target) } -fn is_not_cross_compiling(host: &str, target_info: &TargetInfo) -> bool { - let target_triple = target_info.to_target_triple(); - // Not cross-compiling if arch-vendor-os is all the same - // e.g. x86_64-unknown-linux-musl on x86_64-unknown-linux-gnu host - // x86_64-pc-windows-gnu on x86_64-pc-windows-msvc host - host.starts_with(&target_triple) - // Not cross-compiling to compile for 32-bit Python from windows 64-bit - || (target_triple == "i686-pc-windows" && host.starts_with("x86_64-pc-windows")) - // Not cross-compiling to compile for x86-64 Python from macOS arm64 - || (target_triple == "x86_64-apple-darwin" && host == "aarch64-apple-darwin") - // Not cross-compiling to compile for arm64 Python from macOS x86_64 - || (target_triple == "aarch64-apple-darwin" && host == "x86_64-apple-darwin") +/// Detect whether we are cross compiling and return an assembled CrossCompileConfig if so. +/// +/// This function relies on PyO3 cross-compiling environment variables: +/// +/// * `PYO3_CROSS`: If present, forces PyO3 to configure as a cross-compilation. +/// * `PYO3_CROSS_LIB_DIR`: If present, must be set to the directory containing +/// the target's libpython DSO and the associated `_sysconfigdata*.py` file for +/// Unix-like targets, or the Python DLL import libraries for the Windows target. +/// * `PYO3_CROSS_PYTHON_VERSION`: Major and minor version (e.g. 3.9) of the target Python +/// installation. This variable is only needed if PyO3 cannnot determine the version to target +/// from `abi3-py3*` features, or if there are multiple versions of Python present in +/// `PYO3_CROSS_LIB_DIR`. +/// +/// See the [PyO3 User Guide](https://pyo3.rs/) for more info on cross-compiling. +pub fn cross_compiling_from_to( + host: &Triple, + target: &Triple, +) -> Result> { + let env_vars = cross_compile_env_vars(); + CrossCompileConfig::try_from_env_vars_host_target(env_vars, host, target) +} + +/// Detect whether we are cross compiling from Cargo and `PYO3_CROSS_*` environment +/// variables and return an assembled `CrossCompileConfig` if so. +/// +/// This must be called from PyO3's build script, because it relies on environment +/// variables such as `CARGO_CFG_TARGET_OS` which aren't available at any other time. +pub fn cross_compiling_from_cargo_env() -> Result> { + let env_vars = cross_compile_env_vars(); + let host = Triple::host(); + let target = target_triple_from_env(); + + CrossCompileConfig::try_from_env_vars_host_target(env_vars, &host, &target) } #[allow(non_camel_case_types)] @@ -1091,15 +1219,11 @@ fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec, cross: &CrossCompileConfig) -> Vec 1 { let temp = sysconfig_paths .iter() - .filter(|p| p.to_string_lossy().contains(&cross.target_info.arch)) + .filter(|p| { + p.to_string_lossy() + .contains(&cross.target.architecture.to_string()) + }) .cloned() .collect::>(); if !temp.is_empty() { @@ -1150,11 +1277,19 @@ fn cross_compile_from_sysconfigdata( fn windows_hardcoded_cross_compile( cross_compile_config: CrossCompileConfig, ) -> Result { - let version = cross_compile_config.version.or_else(get_abi3_version) - .ok_or("PYO3_CROSS_PYTHON_VERSION or an abi3-py3* feature must be specified when cross-compiling for Windows.")?; + let version = cross_compile_config + .version + .or_else(get_abi3_version) + .ok_or( + "PYO3_CROSS_PYTHON_VERSION or an abi3-py3* feature must be specified \ + when cross-compiling and PYO3_CROSS_LIB_DIR is not set.", + )?; let abi3 = is_abi3(); let implementation = PythonImplementation::CPython; + let mingw = cross_compile_config.target.environment == Environment::Gnu; + + let lib_dir = Some(cross_compile_config.lib_dir_string()); Ok(InterpreterConfig { implementation, @@ -1165,9 +1300,9 @@ fn windows_hardcoded_cross_compile( version, PythonImplementation::CPython, abi3, - false, + mingw, )), - lib_dir: cross_compile_config.lib_dir.to_str().map(String::from), + lib_dir, executable: None, pointer_width: None, build_flags: BuildFlags::default(), @@ -1303,7 +1438,7 @@ fn conda_env_interpreter(conda_prefix: &OsStr, windows: bool) -> PathBuf { fn get_env_interpreter() -> Option { match (env_var("VIRTUAL_ENV"), env_var("CONDA_PREFIX")) { - // Use cfg rather can CARGO_TARGET_OS because this affects where files are located on the + // Use cfg rather than CARGO_CFG_TARGET_OS because this affects where files are located on the // build host (Some(dir), None) => Some(venv_interpreter(&dir, cfg!(windows))), (None, Some(dir)) => Some(conda_env_interpreter(&dir, cfg!(windows))), @@ -1347,62 +1482,16 @@ pub fn find_interpreter() -> Result { } } -pub fn get_abi3_version() -> Option { - let minor_version = (MINIMUM_SUPPORTED_VERSION.minor..=ABI3_MAX_MINOR) - .find(|i| cargo_env_var(&format!("CARGO_FEATURE_ABI3_PY3{}", i)).is_some()); - minor_version.map(|minor| PythonVersion { major: 3, minor }) -} - -/// Lowers the configured version to the abi3 version, if set. -fn fixup_config_for_abi3( - config: &mut InterpreterConfig, - abi3_version: Option, -) -> Result<()> { - // PyPy doesn't support abi3; don't adjust the version - if config.implementation.is_pypy() { - return Ok(()); - } - - if let Some(version) = abi3_version { - ensure!( - version <= config.version, - "cannot set a minimum Python version {} higher than the interpreter version {} \ - (the minimum Python version is implied by the abi3-py3{} feature)", - version, - config.version, - version.minor, - ); - - config.version = version; - } - Ok(()) -} - /// Generates an interpreter config suitable for cross-compilation. /// /// This must be called from PyO3's build script, because it relies on environment variables such as /// CARGO_CFG_TARGET_OS which aren't available at any other time. pub fn make_cross_compile_config() -> Result> { - let env_vars = cross_compile_env_vars(); - - let host = cargo_env_var("HOST").ok_or("expected HOST env var")?; - let target = cargo_env_var("TARGET").ok_or("expected TARGET env var")?; - - let target_info = TargetInfo::from_cargo_env()?; - - let interpreter_config = if env_vars.any() { - let cross_config = CrossCompileConfig::from_env_vars(env_vars, target_info)?; + let interpreter_config = if let Some(cross_config) = cross_compiling_from_cargo_env()? { let mut interpreter_config = load_cross_compile_config(cross_config)?; - fixup_config_for_abi3(&mut interpreter_config, get_abi3_version())?; + interpreter_config.fixup_for_abi3_version(get_abi3_version())?; Some(interpreter_config) } else { - ensure!( - host == target || is_not_cross_compiling(&host, &target_info), - "PyO3 detected compile host {host} and build target {target}, but none of PYO3_CROSS, PYO3_CROSS_LIB_DIR \ - or PYO3_CROSS_PYTHON_VERSION environment variables are set.", - host=host, - target=target, - ); None }; @@ -1414,13 +1503,14 @@ pub fn make_cross_compile_config() -> Result> { #[allow(dead_code)] pub fn make_interpreter_config() -> Result { let mut interpreter_config = InterpreterConfig::from_interpreter(find_interpreter()?)?; - fixup_config_for_abi3(&mut interpreter_config, get_abi3_version())?; + interpreter_config.fixup_for_abi3_version(get_abi3_version())?; Ok(interpreter_config) } #[cfg(test)] mod tests { use std::{io::Cursor, iter::FromIterator}; + use target_lexicon::triple; use super::*; @@ -1670,11 +1760,7 @@ mod tests { let cross_config = CrossCompileConfig { lib_dir: "C:\\some\\path".into(), version: Some(PythonVersion { major: 3, minor: 7 }), - target_info: TargetInfo { - os: "os".into(), - arch: "arch".into(), - vendor: "vendor".into(), - }, + target: triple!("i686-pc-windows-msvc"), }; assert_eq!( @@ -1695,6 +1781,32 @@ mod tests { ); } + #[test] + fn mingw_hardcoded_cross_compile() { + let cross_config = CrossCompileConfig { + lib_dir: "/usr/lib/mingw".into(), + version: Some(PythonVersion { major: 3, minor: 8 }), + target: triple!("i686-pc-windows-gnu"), + }; + + assert_eq!( + super::windows_hardcoded_cross_compile(cross_config).unwrap(), + InterpreterConfig { + implementation: PythonImplementation::CPython, + version: PythonVersion { major: 3, minor: 8 }, + shared: true, + abi3: false, + lib_name: Some("python3.8".into()), + lib_dir: Some("/usr/lib/mingw".into()), + executable: None, + pointer_width: None, + build_flags: BuildFlags::default(), + suppress_build_script_link_lines: false, + extra_build_script_lines: vec![], + } + ); + } + #[test] fn default_lib_name_windows() { use PythonImplementation::*; @@ -1780,6 +1892,36 @@ mod tests { ); } + #[test] + fn parse_cross_python_version() { + let env_vars = CrossCompileEnvVars { + pyo3_cross: None, + pyo3_cross_lib_dir: None, + pyo3_cross_python_version: Some("3.9".into()), + }; + + assert_eq!( + env_vars.parse_version().unwrap(), + Some(PythonVersion { major: 3, minor: 9 }) + ); + + let env_vars = CrossCompileEnvVars { + pyo3_cross: None, + pyo3_cross_lib_dir: None, + pyo3_cross_python_version: None, + }; + + assert_eq!(env_vars.parse_version().unwrap(), None); + + let env_vars = CrossCompileEnvVars { + pyo3_cross: None, + pyo3_cross_lib_dir: None, + pyo3_cross_python_version: Some("100".into()), + }; + + assert!(env_vars.parse_version().is_err()); + } + #[test] fn interpreter_version_reduced_to_abi3() { let mut config = InterpreterConfig { @@ -1796,7 +1938,9 @@ mod tests { extra_build_script_lines: vec![], }; - fixup_config_for_abi3(&mut config, Some(PythonVersion { major: 3, minor: 7 })).unwrap(); + config + .fixup_for_abi3_version(Some(PythonVersion { major: 3, minor: 7 })) + .unwrap(); assert_eq!(config.version, PythonVersion { major: 3, minor: 7 }); } @@ -1816,12 +1960,13 @@ mod tests { extra_build_script_lines: vec![], }; - assert!( - fixup_config_for_abi3(&mut config, Some(PythonVersion { major: 3, minor: 8 })) - .unwrap_err() - .to_string() - .contains("cannot set a minimum Python version 3.8 higher than the interpreter version 3.7") - ); + assert!(config + .fixup_for_abi3_version(Some(PythonVersion { major: 3, minor: 8 })) + .unwrap_err() + .to_string() + .contains( + "cannot set a minimum Python version 3.8 higher than the interpreter version 3.7" + )); } #[test] @@ -1846,11 +1991,7 @@ mod tests { let cross = CrossCompileConfig { lib_dir: lib_dir.into(), version: Some(interpreter_config.version), - target_info: TargetInfo { - arch: "x86_64".into(), - vendor: "unknown".into(), - os: "linux".into(), - }, + target: triple!("x86_64-unknown-linux-gnu"), }; let sysconfigdata_path = match find_sysconfigdata(&cross) { @@ -1906,6 +2047,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_not_cross_compiling() { assert!( cross_compiling("aarch64-apple-darwin", "x86_64", "apple", "darwin") @@ -1924,6 +2066,51 @@ mod tests { ); } + #[test] + fn test_not_cross_compiling_from_to() { + assert!(cross_compiling_from_to( + &triple!("x86_64-unknown-linux-gnu"), + &triple!("x86_64-unknown-linux-gnu"), + ) + .unwrap() + .is_none()); + + assert!(cross_compiling_from_to( + &triple!("x86_64-apple-darwin"), + &triple!("x86_64-apple-darwin") + ) + .unwrap() + .is_none()); + + assert!(cross_compiling_from_to( + &triple!("aarch64-apple-darwin"), + &triple!("x86_64-apple-darwin") + ) + .unwrap() + .is_none()); + + assert!(cross_compiling_from_to( + &triple!("x86_64-apple-darwin"), + &triple!("aarch64-apple-darwin") + ) + .unwrap() + .is_none()); + + assert!(cross_compiling_from_to( + &triple!("x86_64-pc-windows-msvc"), + &triple!("i686-pc-windows-msvc") + ) + .unwrap() + .is_none()); + + assert!(cross_compiling_from_to( + &triple!("x86_64-unknown-linux-gnu"), + &triple!("x86_64-unknown-linux-musl") + ) + .unwrap() + .is_none()); + } + #[test] fn test_run_python_script() { // as above, this should be okay in CI where Python is presumed installed diff --git a/pyo3-build-config/src/lib.rs b/pyo3-build-config/src/lib.rs index 4cf49e3b49b..bd86e30db4e 100644 --- a/pyo3-build-config/src/lib.rs +++ b/pyo3-build-config/src/lib.rs @@ -17,9 +17,11 @@ use std::{env, process::Command}; #[cfg(feature = "resolve-config")] use once_cell::sync::OnceCell; +#[allow(deprecated)] pub use impl_::{ - cross_compiling, find_all_sysconfigdata, parse_sysconfigdata, BuildFlag, BuildFlags, - CrossCompileConfig, InterpreterConfig, PythonImplementation, PythonVersion, + cross_compiling, cross_compiling_from_to, find_all_sysconfigdata, parse_sysconfigdata, + BuildFlag, BuildFlags, CrossCompileConfig, InterpreterConfig, PythonImplementation, + PythonVersion, Triple, }; /// Adds all the [`#[cfg]` flags](index.html) to the current compilation. @@ -166,7 +168,8 @@ pub mod pyo3_build_script_impl { pub use crate::errors::*; } pub use crate::impl_::{ - cargo_env_var, env_var, make_cross_compile_config, InterpreterConfig, PythonVersion, + cargo_env_var, env_var, is_linking_libpython, make_cross_compile_config, InterpreterConfig, + PythonVersion, }; /// Gets the configuration for use from PyO3's build script. @@ -180,7 +183,7 @@ pub mod pyo3_build_script_impl { InterpreterConfig::from_reader(Cursor::new(CONFIG_FILE)) } else if !ABI3_CONFIG.is_empty() { Ok(abi3_config()) - } else if let Some(interpreter_config) = impl_::make_cross_compile_config()? { + } else if let Some(interpreter_config) = make_cross_compile_config()? { // This is a cross compile and need to write the config file. let path = Path::new(DEFAULT_CROSS_COMPILE_CONFIG_PATH); let parent_dir = path.parent().ok_or_else(|| { diff --git a/pyo3-ffi/build.rs b/pyo3-ffi/build.rs index ddc60e62e5e..f46c13b50bd 100644 --- a/pyo3-ffi/build.rs +++ b/pyo3-ffi/build.rs @@ -1,8 +1,8 @@ use pyo3_build_config::{ bail, ensure, print_feature_cfgs, pyo3_build_script_impl::{ - cargo_env_var, env_var, errors::Result, resolve_interpreter_config, InterpreterConfig, - PythonVersion, + cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config, + InterpreterConfig, PythonVersion, }, }; @@ -44,33 +44,27 @@ fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result fn emit_link_config(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(); - if target_os == "windows" || target_os == "android" || !is_extension_module { - // windows and android - always link - // other systems - only link if not extension module - println!( - "cargo:rustc-link-lib={link_model}{alias}{lib_name}", - link_model = if interpreter_config.shared { - "" - } else { - "static=" - }, - alias = if target_os == "windows" { - "pythonXY:" - } else { - "" - }, - lib_name = interpreter_config.lib_name.as_ref().ok_or( - "attempted to link to Python shared library but config does not contain lib_name" - )?, - ); - if let Some(lib_dir) = &interpreter_config.lib_dir { - println!("cargo:rustc-link-search=native={}", lib_dir); - } - } - // serialize the whole interpreter config in DEP_PYTHON_PYO3_CONFIG - interpreter_config.to_cargo_dep_env()?; + println!( + "cargo:rustc-link-lib={link_model}{alias}{lib_name}", + link_model = if interpreter_config.shared { + "" + } else { + "static=" + }, + alias = if target_os == "windows" { + "pythonXY:" + } else { + "" + }, + lib_name = interpreter_config.lib_name.as_ref().ok_or( + "attempted to link to Python shared library but config does not contain lib_name" + )?, + ); + + if let Some(lib_dir) = &interpreter_config.lib_dir { + println!("cargo:rustc-link-search=native={}", lib_dir); + } Ok(()) } @@ -92,7 +86,10 @@ fn configure_pyo3() -> Result<()> { ensure_python_version(&interpreter_config)?; ensure_target_pointer_width(&interpreter_config)?; - if !interpreter_config.suppress_build_script_link_lines { + // Serialize the whole interpreter config into DEP_PYTHON_PYO3_CONFIG env var. + interpreter_config.to_cargo_dep_env()?; + + if is_linking_libpython() && !interpreter_config.suppress_build_script_link_lines { emit_link_config(&interpreter_config)?; }