From fbab45c4b904d122ac0321404cd6037f6650f77f Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Sat, 13 Nov 2021 12:43:04 -0500 Subject: [PATCH 01/17] Initial refactor - expose cross-compiling functions and add necessary fields to InterpreterConfig --- pyo3-build-config/build.rs | 3 + pyo3-build-config/src/impl_.rs | 156 +++++++++++++++++++++++++++------ pyo3-build-config/src/lib.rs | 7 +- 3 files changed, 136 insertions(+), 30 deletions(-) diff --git a/pyo3-build-config/build.rs b/pyo3-build-config/build.rs index 7e2cae9bc03..edff89c2c6d 100644 --- a/pyo3-build-config/build.rs +++ b/pyo3-build-config/build.rs @@ -61,6 +61,9 @@ pub fn abi3_config() -> Option { // NB PyPy doesn't support abi3 yet implementation: PythonImplementation::CPython, abi3: true, + abi_flags: None, + abi_tag: None, + ext_suffix: Some(".abi3.so".to_string()), lib_name: None, lib_dir: None, build_flags: BuildFlags::abi3(), diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 4690b6e70ba..7694ae8a754 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -65,6 +65,21 @@ pub struct InterpreterConfig { /// Serialized to `abi3`. pub abi3: bool, + /// ABI flags as specified by [PEP 3149](https://www.python.org/dev/peps/pep-3149/). + /// + /// Serialized to `abi_flags`. + pub abi_flags: Option, + + /// Python abi tag - typically '{major}{minor}{abi_flags}' (e.g. '39m'). + /// + /// Serialized to `abi_tag`. + pub abi_tag: Option, + + /// Platform-dependent extension module suffix (e.g. '.cpython-39-darwin.so'). + /// + /// Serialized to `ext_suffix`. + pub ext_suffix: Option, + /// The name of the link library defining Python. /// /// This effectively controls the `cargo:rustc-link-lib=` value to @@ -196,6 +211,9 @@ SHARED = bool(get_config_var("Py_ENABLE_SHARED")) print("implementation", platform.python_implementation()) print("version_major", sys.version_info[0]) print("version_minor", sys.version_info[1]) +print("abiflags", get_config_var("ABIFLAGS")) +print("soabi", get_config_var("SOABI")) +print("ext_suffix", get_config_var("EXT_SUFFIX")) print("shared", PYPY or ANACONDA or WINDOWS or FRAMEWORK or SHARED) print_if_set("ld_version", get_config_var("LDVERSION")) print_if_set("libdir", get_config_var("LIBDIR")) @@ -218,6 +236,15 @@ print("mingw", get_platform() == "mingw") }; let abi3 = is_abi3(); + let abi_flags = map["abiflags"].to_string(); + let soabi = map["soabi"].as_str(); + let abi_tag = soabi + .split('-') + .nth(1) + .map(ToString::to_string) + .expect("unable to parse ABI tag from SOABI"); + let ext_suffix = map["ext_suffix"].to_string(); + let implementation = map["implementation"].parse()?; let lib_name = if cfg!(windows) { @@ -251,6 +278,9 @@ print("mingw", get_platform() == "mingw") implementation, shared, abi3, + abi_flags: Some(abi_flags), + abi_tag: Some(abi_tag), + ext_suffix: Some(ext_suffix), lib_name: Some(lib_name), lib_dir, executable: map.get("executable").cloned(), @@ -292,6 +322,9 @@ print("mingw", get_platform() == "mingw") let mut version = None; let mut shared = None; let mut abi3 = None; + let mut abi_flags = None; + let mut abi_tag = None; + let mut ext_suffix = None; let mut lib_name = None; let mut lib_dir = None; let mut executable = None; @@ -316,6 +349,9 @@ print("mingw", get_platform() == "mingw") "version" => parse_value!(version, value), "shared" => parse_value!(shared, value), "abi3" => parse_value!(abi3, value), + "abi_flags" => parse_value!(abi_flags, value), + "abi_tag" => parse_value!(abi_tag, value), + "ext_suffix" => parse_value!(ext_suffix, value), "lib_name" => parse_value!(lib_name, value), "lib_dir" => parse_value!(lib_dir, value), "executable" => parse_value!(executable, value), @@ -340,6 +376,9 @@ print("mingw", get_platform() == "mingw") version, shared: shared.unwrap_or(true), abi3, + abi_flags, + abi_tag, + ext_suffix, lib_name, lib_dir, executable, @@ -387,6 +426,9 @@ print("mingw", get_platform() == "mingw") write_line!(version)?; write_line!(shared)?; write_line!(abi3)?; + write_option_line!(abi_flags)?; + write_option_line!(abi_tag)?; + write_option_line!(ext_suffix)?; write_option_line!(lib_name)?; write_option_line!(lib_dir)?; write_option_line!(executable)?; @@ -472,8 +514,13 @@ fn is_abi3() -> bool { cargo_env_var("CARGO_FEATURE_ABI3").is_some() } -struct CrossCompileConfig { - lib_dir: PathBuf, +/// Configuration needed by PyO3 to cross-compile for a target platform. +/// +/// Usually this is collected from the environment (i.e. `PYO3_CROSS_*` and `CARGO_CFG_TARGET_*`) +/// when a cross-compilation configuration is detected. +#[derive(Debug)] +pub struct CrossCompileConfig { + pub lib_dir: PathBuf, version: Option, os: String, arch: String, @@ -486,48 +533,61 @@ pub fn any_cross_compiling_env_vars_set() -> bool { || env::var_os("PYO3_CROSS_PYTHON_VERSION").is_some() } -fn cross_compiling() -> Result> { +fn cross_compiling_from_cargo_env() -> Result> { + let host = cargo_env_var("HOST").unwrap(); + let target = cargo_env_var("TARGET").unwrap(); + + if host == target { + // definitely not cross compiling + return Ok(None); + } + + let target_arch = cargo_env_var("CARGO_CFG_TARGET_ARCH").unwrap(); + let target_vendor = cargo_env_var("CARGO_CFG_TARGET_VENDOR").unwrap(); + let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap(); + + cross_compiling( + &host, + &format!("{}-{}-{}", target_arch, target_vendor, target_os), + &target_arch, + &target_os, + ) +} + +/// Detect whether we are cross compiling and return an assembled CrossCompileConfig if so. +pub fn cross_compiling( + host: &str, + target_triple: &str, + target_arch: &str, + target_os: &str, +) -> Result> { let cross = env_var("PYO3_CROSS"); let cross_lib_dir = env_var("PYO3_CROSS_LIB_DIR"); let cross_python_version = env_var("PYO3_CROSS_PYTHON_VERSION"); - let target_arch = cargo_env_var("CARGO_CFG_TARGET_ARCH"); - let target_vendor = cargo_env_var("CARGO_CFG_TARGET_VENDOR"); - let target_os = cargo_env_var("CARGO_CFG_TARGET_OS"); - if cross.is_none() && cross_lib_dir.is_none() && cross_python_version.is_none() { // No cross-compiling environment variables set; try to determine if this is a known case // which is not cross-compilation. - - let target = cargo_env_var("TARGET").unwrap(); - let host = cargo_env_var("HOST").unwrap(); - if target == host { - // Not cross-compiling + if host.starts_with(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 return Ok(None); } - if target == "i686-pc-windows-msvc" && host == "x86_64-pc-windows-msvc" { + if target_triple == "i686-pc-windows-msvc" && host == "x86_64-pc-windows-msvc" { // Not cross-compiling to compile for 32-bit Python from windows 64-bit return Ok(None); } - if target == "x86_64-apple-darwin" && host == "aarch64-apple-darwin" { + if target_triple == "x86_64-apple-darwin" && host == "aarch64-apple-darwin" { // Not cross-compiling to compile for x86-64 Python from macOS arm64 return Ok(None); } - if target == "aarch64-apple-darwin" && host == "x86_64-apple-darwin" { + if target_triple == "aarch64-apple-darwin" && host == "x86_64-apple-darwin" { // Not cross-compiling to compile for arm64 Python from macOS x86_64 return Ok(None); } - - if let (Some(arch), Some(vendor), Some(os)) = (&target_arch, &target_vendor, &target_os) { - if host.starts_with(&format!("{}-{}-{}", arch, vendor, os)) { - // 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 - return Ok(None); - } - } } // At this point we assume that we are cross compiling. @@ -536,8 +596,8 @@ fn cross_compiling() -> Result> { lib_dir: cross_lib_dir .ok_or("The PYO3_CROSS_LIB_DIR environment variable must be set when cross-compiling")? .into(), - os: target_os.unwrap(), - arch: target_arch.unwrap(), + os: target_os.to_string(), + arch: target_arch.to_string(), version: cross_python_version .map(|os_string| { let utf8_str = os_string @@ -736,7 +796,7 @@ fn parse_script_output(output: &str) -> HashMap { /// The sysconfigdata is simply a dictionary containing all the build time variables used for the /// python executable and library. Here it is read and added to a script to extract only what is /// necessary. This necessitates a python interpreter for the host machine to work. -fn parse_sysconfigdata(sysconfigdata_path: impl AsRef) -> Result { +pub fn parse_sysconfigdata(sysconfigdata_path: impl AsRef) -> Result { let sysconfigdata_path = sysconfigdata_path.as_ref(); let mut script = fs::read_to_string(&sysconfigdata_path).with_context(|| { format!( @@ -747,6 +807,8 @@ fn parse_sysconfigdata(sysconfigdata_path: impl AsRef) -> Result bail!("expected a bool (1/true/True or 0/false/False) for Py_ENABLE_SHARED"), }; + let abi_flags = get_key!(sysconfigdata, "ABIFLAGS")?.to_string(); + let abi_tag = soabi + .split('-') + .nth(1) + .map(ToString::to_string) + .expect("unable to parse ABI tag from SOABI"); + let ext_suffix = get_key!(sysconfigdata, "EXT_SUFFIX")?.to_string(); + Ok(InterpreterConfig { implementation, version, shared, abi3: is_abi3(), + abi_flags: Some(abi_flags), + abi_tag: Some(abi_tag), + ext_suffix: Some(ext_suffix), lib_dir: get_key!(sysconfigdata, "LIBDIR").ok().cloned(), lib_name: Some(default_lib_name_unix( version, @@ -864,7 +937,7 @@ fn ends_with(entry: &DirEntry, pat: &str) -> bool { /// ``` /// /// [1]: https://github.com/python/cpython/blob/3.5/Lib/sysconfig.py#L389 -fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result { +pub fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result { let sysconfig_paths = search_lib_dir(&cross.lib_dir, cross); let sysconfig_name = env_var("_PYTHON_SYSCONFIGDATA_NAME"); let mut sysconfig_paths = sysconfig_paths @@ -983,12 +1056,16 @@ fn windows_hardcoded_cross_compile( .ok_or("PYO3_CROSS_PYTHON_VERSION or an abi3-py3* feature must be specified when cross-compiling for Windows.")?; let abi3 = is_abi3(); + let implementation = PythonImplementation::CPython; Ok(InterpreterConfig { - implementation: PythonImplementation::CPython, + implementation, version, shared: true, abi3, + abi_flags: Some("".to_string()), + abi_tag: None, + ext_suffix: Some(".pyd".to_string()), lib_name: Some(default_lib_name_windows(version, abi3, false)), lib_dir: cross_compile_config.lib_dir.to_str().map(String::from), executable: None, @@ -1175,7 +1252,7 @@ fn fixup_config_for_abi3( /// 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 mut interpreter_config = if let Some(paths) = cross_compiling()? { + let mut interpreter_config = if let Some(paths) = cross_compiling_from_cargo_env()? { load_cross_compile_config(paths)? } else { return Ok(None); @@ -1203,6 +1280,9 @@ mod tests { fn test_config_file_roundtrip() { let config = InterpreterConfig { abi3: true, + abi_flags: None, + abi_tag: None, + ext_suffix: None, build_flags: BuildFlags::abi3(), pointer_width: Some(32), executable: Some("executable".into()), @@ -1226,6 +1306,9 @@ mod tests { let config = InterpreterConfig { abi3: false, + abi_flags: Some("".to_string()), + abi_tag: Some("310".to_string()), + ext_suffix: Some(".cpython-310-darwin.so".to_string()), build_flags: { let mut flags = HashSet::new(); flags.insert(BuildFlag::Py_DEBUG); @@ -1264,6 +1347,9 @@ mod tests { implementation: PythonImplementation::CPython, shared: true, abi3: false, + abi_flags: None, + abi_tag: None, + ext_suffix: None, lib_name: None, lib_dir: None, executable: None, @@ -1376,6 +1462,9 @@ mod tests { version: PythonVersion { major: 3, minor: 6 }, shared: true, abi3: false, + abi_flags: Some("".to_string()), + abi_tag: None, + ext_suffix: Some(".pyd".to_string()), lib_name: Some("python36".into()), lib_dir: Some("C:\\some\\path".into()), executable: None, @@ -1440,6 +1529,9 @@ mod tests { fn interpreter_version_reduced_to_abi3() { let mut config = InterpreterConfig { abi3: true, + abi_flags: None, + abi_tag: None, + ext_suffix: Some(".abi3.so".to_string()), build_flags: BuildFlags::new(), pointer_width: None, executable: None, @@ -1460,6 +1552,9 @@ mod tests { fn abi3_version_cannot_be_higher_than_interpreter() { let mut config = InterpreterConfig { abi3: true, + abi_flags: None, + abi_tag: Some("36".to_string()), + ext_suffix: Some(".abi3.so".to_string()), build_flags: BuildFlags::new(), pointer_width: None, executable: None, @@ -1517,6 +1612,9 @@ mod tests { parsed_config, InterpreterConfig { abi3: false, + abi_flags: Some("m".to_string()), + abi_tag: Some("37m".to_string()), + ext_suffix: Some(".cpython-37m-x86_64-linux-gnu.so".to_string()), build_flags: BuildFlags(interpreter_config.build_flags.0.clone()), pointer_width: Some(64), executable: None, diff --git a/pyo3-build-config/src/lib.rs b/pyo3-build-config/src/lib.rs index 8021952622c..92fb968ec49 100644 --- a/pyo3-build-config/src/lib.rs +++ b/pyo3-build-config/src/lib.rs @@ -15,7 +15,12 @@ use std::io::Cursor; #[cfg(feature = "resolve-config")] use once_cell::sync::OnceCell; -pub use impl_::{BuildFlag, BuildFlags, InterpreterConfig, PythonImplementation, PythonVersion}; +pub use impl_::{ + BuildFlag, BuildFlags, CrossCompileConfig, InterpreterConfig, PythonImplementation, + PythonVersion, +}; + +pub use impl_::{cross_compiling, find_sysconfigdata, parse_sysconfigdata}; /// Adds all the [`#[cfg]` flags](index.html) to the current compilation. /// From 1bd27f7dc40b3608fb729a92c926444e5996984a Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Mon, 15 Nov 2021 09:27:00 -0500 Subject: [PATCH 02/17] Refactor cross_compiling to take arch/vendor/os separately. --- pyo3-build-config/src/impl_.rs | 51 ++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 7694ae8a754..c5543f05991 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -65,7 +65,7 @@ pub struct InterpreterConfig { /// Serialized to `abi3`. pub abi3: bool, - /// ABI flags as specified by [PEP 3149](https://www.python.org/dev/peps/pep-3149/). + /// ABI flags as specified in [PEP 3149](https://www.python.org/dev/peps/pep-3149/). /// /// Serialized to `abi_flags`. pub abi_flags: Option, @@ -520,10 +520,26 @@ fn is_abi3() -> bool { /// when a cross-compilation configuration is detected. #[derive(Debug)] pub struct CrossCompileConfig { + /// The directory containing the Python library to link against. pub lib_dir: PathBuf, + + /// The version of the Python library to link against. version: Option, - os: String, + + /// The `arch` component of the compilaton target triple. + /// + /// e.g. x86_64, i386, arm, thumb, mips, etc. arch: String, + + /// The `vendor` component of the compilaton target triple. + /// + /// e.g. apple, pc, unknown, etc. + vendor: String, + + /// The `os` component of the compilaton target triple. + /// + /// e.g. darwin, freebsd, linux, windows, etc. + os: String, } #[allow(unused)] @@ -546,33 +562,25 @@ fn cross_compiling_from_cargo_env() -> Result> { let target_vendor = cargo_env_var("CARGO_CFG_TARGET_VENDOR").unwrap(); let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap(); - cross_compiling( - &host, - &format!("{}-{}-{}", target_arch, target_vendor, target_os), - &target_arch, - &target_os, - ) + cross_compiling(&host, &target_vendor, &target_arch, &target_os) } /// Detect whether we are cross compiling and return an assembled CrossCompileConfig if so. pub fn cross_compiling( host: &str, - target_triple: &str, target_arch: &str, + target_vendor: &str, target_os: &str, ) -> Result> { let cross = env_var("PYO3_CROSS"); let cross_lib_dir = env_var("PYO3_CROSS_LIB_DIR"); let cross_python_version = env_var("PYO3_CROSS_PYTHON_VERSION"); + let target_triple = format!("{}-{}-{}", target_arch, target_vendor, target_os); + if cross.is_none() && cross_lib_dir.is_none() && cross_python_version.is_none() { // No cross-compiling environment variables set; try to determine if this is a known case // which is not cross-compilation. - if host.starts_with(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 - return Ok(None); - } if target_triple == "i686-pc-windows-msvc" && host == "x86_64-pc-windows-msvc" { // Not cross-compiling to compile for 32-bit Python from windows 64-bit @@ -588,6 +596,12 @@ pub fn cross_compiling( // Not cross-compiling to compile for arm64 Python from macOS x86_64 return Ok(None); } + + if host.starts_with(&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 + return Ok(None); + } } // At this point we assume that we are cross compiling. @@ -596,8 +610,9 @@ pub fn cross_compiling( lib_dir: cross_lib_dir .ok_or("The PYO3_CROSS_LIB_DIR environment variable must be set when cross-compiling")? .into(), - os: target_os.to_string(), arch: target_arch.to_string(), + vendor: target_vendor.to_string(), + os: target_os.to_string(), version: cross_python_version .map(|os_string| { let utf8_str = os_string @@ -1451,8 +1466,9 @@ mod tests { let cross_config = CrossCompileConfig { lib_dir: "C:\\some\\path".into(), version: Some(PythonVersion { major: 3, minor: 6 }), - os: "os".into(), arch: "arch".into(), + vendor: "vendor".into(), + os: "os".into(), }; assert_eq!( @@ -1597,8 +1613,9 @@ mod tests { let cross = CrossCompileConfig { lib_dir: lib_dir.into(), version: Some(interpreter_config.version), - os: "linux".into(), arch: "x86_64".into(), + vendor: "unknown".into(), + os: "linux".into(), }; let sysconfigdata_path = match find_sysconfigdata(&cross) { From 69cd710d3a3a42f5747476da2d5c943cc6e572c7 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Mon, 15 Nov 2021 21:16:41 -0500 Subject: [PATCH 03/17] Address review comments. --- pyo3-build-config/src/impl_.rs | 59 +++++++++++++++++++--------------- pyo3-build-config/src/lib.rs | 6 ++-- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index c5543f05991..fd4878ad42c 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -554,7 +554,12 @@ fn cross_compiling_from_cargo_env() -> Result> { let target = cargo_env_var("TARGET").unwrap(); if host == target { - // definitely not cross compiling + // Definitely not cross compiling if the host matches the target + return Ok(None); + } + + if target == "i686-pc-windows-msvc" && host == "x86_64-pc-windows-msvc" { + // Not cross-compiling to compile for 32-bit Python from windows 64-bit return Ok(None); } @@ -582,11 +587,6 @@ pub fn cross_compiling( // No cross-compiling environment variables set; try to determine if this is a known case // which is not cross-compilation. - if target_triple == "i686-pc-windows-msvc" && host == "x86_64-pc-windows-msvc" { - // Not cross-compiling to compile for 32-bit Python from windows 64-bit - return Ok(None); - } - if target_triple == "x86_64-apple-darwin" && host == "aarch64-apple-darwin" { // Not cross-compiling to compile for x86-64 Python from macOS arm64 return Ok(None); @@ -921,7 +921,30 @@ fn ends_with(entry: &DirEntry, pat: &str) -> bool { name.to_string_lossy().ends_with(pat) } -/// Finds the `_sysconfigdata*.py` file in the library path. +fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result { + let mut sysconfig_paths = find_all_sysconfigdata(cross); + if sysconfig_paths.is_empty() { + bail!( + "Could not find either libpython.so or _sysconfigdata*.py in {}", + cross.lib_dir.display() + ); + } else if sysconfig_paths.len() > 1 { + let mut error_msg = String::from( + "Detected multiple possible Python versions. Please set either the \ + PYO3_CROSS_PYTHON_VERSION variable to the wanted version or the \ + _PYTHON_SYSCONFIGDATA_NAME variable to the wanted sysconfigdata file name.\n\n\ + sysconfigdata files found:", + ); + for path in sysconfig_paths { + error_msg += &format!("\n\t{}", path.display()); + } + bail!("{}\n", error_msg); + } + + Ok(sysconfig_paths.remove(0)) +} + +/// Finds `_sysconfigdata*.py` files for detected Python interpreters. /// /// From the python source for `_sysconfigdata*.py` is always going to be located at /// `build/lib.{PLATFORM}-{PY_MINOR_VERSION}` when built from source. The [exact line][1] is defined as: @@ -952,7 +975,7 @@ fn ends_with(entry: &DirEntry, pat: &str) -> bool { /// ``` /// /// [1]: https://github.com/python/cpython/blob/3.5/Lib/sysconfig.py#L389 -pub fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result { +pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Vec { let sysconfig_paths = search_lib_dir(&cross.lib_dir, cross); let sysconfig_name = env_var("_PYTHON_SYSCONFIGDATA_NAME"); let mut sysconfig_paths = sysconfig_paths @@ -965,27 +988,11 @@ pub fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result { } }) .collect::>(); + sysconfig_paths.sort(); sysconfig_paths.dedup(); - if sysconfig_paths.is_empty() { - bail!( - "Could not find either libpython.so or _sysconfigdata*.py in {}", - cross.lib_dir.display() - ); - } else if sysconfig_paths.len() > 1 { - let mut error_msg = String::from( - "Detected multiple possible Python versions. Please set either the \ - PYO3_CROSS_PYTHON_VERSION variable to the wanted version or the \ - _PYTHON_SYSCONFIGDATA_NAME variable to the wanted sysconfigdata file name.\n\n\ - sysconfigdata files found:", - ); - for path in sysconfig_paths { - error_msg += &format!("\n\t{}", path.display()); - } - bail!("{}\n", error_msg); - } - Ok(sysconfig_paths.remove(0)) + sysconfig_paths } /// recursive search for _sysconfigdata, returns all possibilities of sysconfigdata paths diff --git a/pyo3-build-config/src/lib.rs b/pyo3-build-config/src/lib.rs index 92fb968ec49..0a95b8de81e 100644 --- a/pyo3-build-config/src/lib.rs +++ b/pyo3-build-config/src/lib.rs @@ -16,12 +16,10 @@ use std::io::Cursor; use once_cell::sync::OnceCell; pub use impl_::{ - BuildFlag, BuildFlags, CrossCompileConfig, InterpreterConfig, PythonImplementation, - PythonVersion, + cross_compiling, find_all_sysconfigdata, parse_sysconfigdata, BuildFlag, BuildFlags, + CrossCompileConfig, InterpreterConfig, PythonImplementation, PythonVersion, }; -pub use impl_::{cross_compiling, find_sysconfigdata, parse_sysconfigdata}; - /// Adds all the [`#[cfg]` flags](index.html) to the current compilation. /// /// This should be called from a build script. From 52301f0d6dad2143c96486149e1736cecde7bd9d Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Tue, 16 Nov 2021 11:41:40 -0500 Subject: [PATCH 04/17] Update changelog with note about pyo3-build-config APIs. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a416212b92..6db7d834a6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add implementations for `Py::as_ref()` and `Py::into_ref()` for `Py`, `Py` and `Py`. [#1682](https://github.com/PyO3/pyo3/pull/1682) - Add `PyTraceback` type to represent and format Python tracebacks. [#1977](https://github.com/PyO3/pyo3/pull/1977) +- Expose `pyo3-build-config` APIs for cross-compiling and Python configuration discovery for use in other projects. [#1996](https://github.com/PyO3/pyo3/pull/1996) ### Changed From 7ab810aa95cf80a023ebb7c5b15f964c67032d42 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Tue, 16 Nov 2021 11:41:54 -0500 Subject: [PATCH 05/17] Fix panic when parsing ABI tag on Windows. --- pyo3-build-config/src/impl_.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index ce39239c2f4..776e1c7dad9 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -236,11 +236,7 @@ print("mingw", get_platform().startswith("mingw")) let abi3 = is_abi3(); let abi_flags = map["abiflags"].to_string(); let soabi = map["soabi"].as_str(); - let abi_tag = soabi - .split('-') - .nth(1) - .map(ToString::to_string) - .expect("unable to parse ABI tag from SOABI"); + let abi_tag = soabi.split('-').nth(1).map(ToString::to_string); let ext_suffix = map["ext_suffix"].to_string(); let implementation = map["implementation"].parse()?; @@ -282,7 +278,7 @@ print("mingw", get_platform().startswith("mingw")) shared, abi3, abi_flags: Some(abi_flags), - abi_tag: Some(abi_tag), + abi_tag, ext_suffix: Some(ext_suffix), lib_name: Some(lib_name), lib_dir, From c8f0785cb30c489af6ad36009f134b0c5c6e6684 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Tue, 16 Nov 2021 13:50:24 -0500 Subject: [PATCH 06/17] Update parse_sysconfigdata test to best-guess values for linux. --- pyo3-build-config/src/impl_.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 776e1c7dad9..e3cbb3c3edb 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -1671,13 +1671,29 @@ mod tests { }; let parsed_config = super::parse_sysconfigdata(sysconfigdata_path).unwrap(); + // this is generally the format of the abi_flags, abi_tag, and ext_suffix on linux so this + // should work for CI + let expected_abi_flags = if PythonVersion::PY37 >= interpreter_config.version { + "m".to_string() + } else { + "".to_string() + }; + let expected_abi_tag = format!( + "{}{}{}", + interpreter_config.version.major, + interpreter_config.version.minor, + expected_abi_flags.clone(), + ); + let expected_ext_suffix = + format!(".cpython-{}-x86_64-linux-gnu.so", expected_abi_tag.clone()); + assert_eq!( parsed_config, InterpreterConfig { abi3: false, - abi_flags: Some("m".to_string()), - abi_tag: Some("37m".to_string()), - ext_suffix: Some(".cpython-37m-x86_64-linux-gnu.so".to_string()), + abi_flags: Some(expected_abi_flags), + abi_tag: Some(expected_abi_tag), + ext_suffix: Some(expected_ext_suffix), build_flags: BuildFlags(interpreter_config.build_flags.0.clone()), pointer_width: Some(64), executable: None, From 490fe61429910e61c5a7995e63849e355dbc3a2f Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Thu, 18 Nov 2021 20:25:04 -0500 Subject: [PATCH 07/17] Revert added fields in InterpreterConfig. --- pyo3-build-config/build.rs | 3 -- pyo3-build-config/src/impl_.rs | 89 ---------------------------------- 2 files changed, 92 deletions(-) diff --git a/pyo3-build-config/build.rs b/pyo3-build-config/build.rs index edff89c2c6d..7e2cae9bc03 100644 --- a/pyo3-build-config/build.rs +++ b/pyo3-build-config/build.rs @@ -61,9 +61,6 @@ pub fn abi3_config() -> Option { // NB PyPy doesn't support abi3 yet implementation: PythonImplementation::CPython, abi3: true, - abi_flags: None, - abi_tag: None, - ext_suffix: Some(".abi3.so".to_string()), lib_name: None, lib_dir: None, build_flags: BuildFlags::abi3(), diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index e3cbb3c3edb..5328ecf6c97 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -65,21 +65,6 @@ pub struct InterpreterConfig { /// Serialized to `abi3`. pub abi3: bool, - /// ABI flags as specified in [PEP 3149](https://www.python.org/dev/peps/pep-3149/). - /// - /// Serialized to `abi_flags`. - pub abi_flags: Option, - - /// Python abi tag - typically '{major}{minor}{abi_flags}' (e.g. '39m'). - /// - /// Serialized to `abi_tag`. - pub abi_tag: Option, - - /// Platform-dependent extension module suffix (e.g. '.cpython-39-darwin.so'). - /// - /// Serialized to `ext_suffix`. - pub ext_suffix: Option, - /// The name of the link library defining Python. /// /// This effectively controls the `cargo:rustc-link-lib=` value to @@ -209,9 +194,6 @@ SHARED = bool(get_config_var("Py_ENABLE_SHARED")) print("implementation", platform.python_implementation()) print("version_major", sys.version_info[0]) print("version_minor", sys.version_info[1]) -print("abiflags", get_config_var("ABIFLAGS")) -print("soabi", get_config_var("SOABI")) -print("ext_suffix", get_config_var("EXT_SUFFIX")) print("shared", PYPY or ANACONDA or WINDOWS or FRAMEWORK or SHARED) print_if_set("ld_version", get_config_var("LDVERSION")) print_if_set("libdir", get_config_var("LIBDIR")) @@ -234,10 +216,6 @@ print("mingw", get_platform().startswith("mingw")) }; let abi3 = is_abi3(); - let abi_flags = map["abiflags"].to_string(); - let soabi = map["soabi"].as_str(); - let abi_tag = soabi.split('-').nth(1).map(ToString::to_string); - let ext_suffix = map["ext_suffix"].to_string(); let implementation = map["implementation"].parse()?; @@ -277,9 +255,6 @@ print("mingw", get_platform().startswith("mingw")) implementation, shared, abi3, - abi_flags: Some(abi_flags), - abi_tag, - ext_suffix: Some(ext_suffix), lib_name: Some(lib_name), lib_dir, executable: map.get("executable").cloned(), @@ -321,9 +296,6 @@ print("mingw", get_platform().startswith("mingw")) let mut version = None; let mut shared = None; let mut abi3 = None; - let mut abi_flags = None; - let mut abi_tag = None; - let mut ext_suffix = None; let mut lib_name = None; let mut lib_dir = None; let mut executable = None; @@ -348,9 +320,6 @@ print("mingw", get_platform().startswith("mingw")) "version" => parse_value!(version, value), "shared" => parse_value!(shared, value), "abi3" => parse_value!(abi3, value), - "abi_flags" => parse_value!(abi_flags, value), - "abi_tag" => parse_value!(abi_tag, value), - "ext_suffix" => parse_value!(ext_suffix, value), "lib_name" => parse_value!(lib_name, value), "lib_dir" => parse_value!(lib_dir, value), "executable" => parse_value!(executable, value), @@ -375,9 +344,6 @@ print("mingw", get_platform().startswith("mingw")) version, shared: shared.unwrap_or(true), abi3, - abi_flags, - abi_tag, - ext_suffix, lib_name, lib_dir, executable, @@ -425,9 +391,6 @@ print("mingw", get_platform().startswith("mingw")) write_line!(version)?; write_line!(shared)?; write_line!(abi3)?; - write_option_line!(abi_flags)?; - write_option_line!(abi_tag)?; - write_option_line!(ext_suffix)?; write_option_line!(lib_name)?; write_option_line!(lib_dir)?; write_option_line!(executable)?; @@ -821,7 +784,6 @@ pub fn parse_sysconfigdata(sysconfigdata_path: impl AsRef) -> Result bail!("expected a bool (1/true/True or 0/false/False) for Py_ENABLE_SHARED"), }; - let abi_flags = get_key!(sysconfigdata, "ABIFLAGS")?.to_string(); - let abi_tag = soabi - .split('-') - .nth(1) - .map(ToString::to_string) - .expect("unable to parse ABI tag from SOABI"); - let ext_suffix = get_key!(sysconfigdata, "EXT_SUFFIX")?.to_string(); - Ok(InterpreterConfig { implementation, version, shared, abi3: is_abi3(), - abi_flags: Some(abi_flags), - abi_tag: Some(abi_tag), - ext_suffix: Some(ext_suffix), lib_dir: get_key!(sysconfigdata, "LIBDIR").ok().cloned(), lib_name: Some(default_lib_name_unix( version, @@ -1084,9 +1035,6 @@ fn windows_hardcoded_cross_compile( version, shared: true, abi3, - abi_flags: Some("".to_string()), - abi_tag: None, - ext_suffix: Some(".pyd".to_string()), lib_name: Some(default_lib_name_windows( version, PythonImplementation::CPython, @@ -1311,9 +1259,6 @@ mod tests { fn test_config_file_roundtrip() { let config = InterpreterConfig { abi3: true, - abi_flags: None, - abi_tag: None, - ext_suffix: None, build_flags: BuildFlags::abi3(), pointer_width: Some(32), executable: Some("executable".into()), @@ -1337,9 +1282,6 @@ mod tests { let config = InterpreterConfig { abi3: false, - abi_flags: Some("".to_string()), - abi_tag: Some("310".to_string()), - ext_suffix: Some(".cpython-310-darwin.so".to_string()), build_flags: { let mut flags = HashSet::new(); flags.insert(BuildFlag::Py_DEBUG); @@ -1378,9 +1320,6 @@ mod tests { implementation: PythonImplementation::CPython, shared: true, abi3: false, - abi_flags: None, - abi_tag: None, - ext_suffix: None, lib_name: None, lib_dir: None, executable: None, @@ -1494,9 +1433,6 @@ mod tests { version: PythonVersion { major: 3, minor: 6 }, shared: true, abi3: false, - abi_flags: Some("".to_string()), - abi_tag: None, - ext_suffix: Some(".pyd".to_string()), lib_name: Some("python36".into()), lib_dir: Some("C:\\some\\path".into()), executable: None, @@ -1591,9 +1527,6 @@ mod tests { fn interpreter_version_reduced_to_abi3() { let mut config = InterpreterConfig { abi3: true, - abi_flags: None, - abi_tag: None, - ext_suffix: Some(".abi3.so".to_string()), build_flags: BuildFlags::new(), pointer_width: None, executable: None, @@ -1614,9 +1547,6 @@ mod tests { fn abi3_version_cannot_be_higher_than_interpreter() { let mut config = InterpreterConfig { abi3: true, - abi_flags: None, - abi_tag: Some("36".to_string()), - ext_suffix: Some(".abi3.so".to_string()), build_flags: BuildFlags::new(), pointer_width: None, executable: None, @@ -1671,29 +1601,10 @@ mod tests { }; let parsed_config = super::parse_sysconfigdata(sysconfigdata_path).unwrap(); - // this is generally the format of the abi_flags, abi_tag, and ext_suffix on linux so this - // should work for CI - let expected_abi_flags = if PythonVersion::PY37 >= interpreter_config.version { - "m".to_string() - } else { - "".to_string() - }; - let expected_abi_tag = format!( - "{}{}{}", - interpreter_config.version.major, - interpreter_config.version.minor, - expected_abi_flags.clone(), - ); - let expected_ext_suffix = - format!(".cpython-{}-x86_64-linux-gnu.so", expected_abi_tag.clone()); - assert_eq!( parsed_config, InterpreterConfig { abi3: false, - abi_flags: Some(expected_abi_flags), - abi_tag: Some(expected_abi_tag), - ext_suffix: Some(expected_ext_suffix), build_flags: BuildFlags(interpreter_config.build_flags.0.clone()), pointer_width: Some(64), executable: None, From 4974fc8a68bc582e366074f712e90666e31705ee Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Fri, 19 Nov 2021 14:44:16 -0500 Subject: [PATCH 08/17] Refactor parse_sysconfigdata to return Sysconfigdata (HashMap). Add InterpreterConfig::from_sysconfigdata. --- pyo3-build-config/src/impl_.rs | 201 ++++++++++++++++----------------- 1 file changed, 98 insertions(+), 103 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 5328ecf6c97..39f65683274 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -265,6 +265,62 @@ print("mingw", get_platform().startswith("mingw")) }) } + /// Generate from parsed sysconfigdata file + /// + /// Use [`parse_sysconfigdata`](parse_sysconfigdata) to generate a hash map of + /// configuration values which may be used to build an `InterpreterConfig`. + pub fn from_sysconfigdata(sysconfigdata: Sysconfigdata) -> Result { + macro_rules! get_key { + ($sysconfigdata:expr, $key:literal) => { + $sysconfigdata + .get_value($key) + .ok_or(concat!($key, " not found in sysconfigdata file")) + }; + } + + macro_rules! parse_key { + ($sysconfigdata:expr, $key:literal) => { + get_key!($sysconfigdata, $key)? + .parse() + .context(concat!("could not parse value of ", $key)) + }; + } + + let soabi = get_key!(sysconfigdata, "SOABI")?; + let implementation = PythonImplementation::from_soabi(soabi)?; + let version = parse_key!(sysconfigdata, "VERSION")?; + let shared = match sysconfigdata.get_value("Py_ENABLE_SHARED") { + Some("1") | Some("true") | Some("True") => true, + Some("0") | Some("false") | Some("False") => false, + _ => bail!("expected a bool (1/true/True or 0/false/False) for Py_ENABLE_SHARED"), + }; + let lib_dir = get_key!(sysconfigdata, "LIBDIR").ok().map(str::to_string); + let lib_name = Some(default_lib_name_unix( + version, + implementation, + sysconfigdata.get_value("LDVERSION"), + )); + let pointer_width = parse_key!(sysconfigdata, "SIZEOF_VOID_P") + .map(|bytes_width: u32| bytes_width * 8) + .ok(); + let build_flags = + BuildFlags::from_sysconfigdata(&sysconfigdata).fixup(version, implementation); + + Ok(InterpreterConfig { + implementation, + version, + shared, + abi3: is_abi3(), + lib_dir, + lib_name, + executable: None, + pointer_width, + build_flags, + suppress_build_script_link_lines: false, + extra_build_script_lines: vec![], + }) + } + #[doc(hidden)] pub fn from_path(path: impl AsRef) -> Result { let path = path.as_ref(); @@ -450,6 +506,17 @@ impl PythonImplementation { pub fn is_pypy(self) -> bool { self == PythonImplementation::PyPy } + + #[doc(hidden)] + pub fn from_soabi(soabi: &str) -> Result { + if soabi.starts_with("pypy") { + Ok(PythonImplementation::PyPy) + } else if soabi.starts_with("cpython") { + Ok(PythonImplementation::CPython) + } else { + bail!("unsupported Python interpreter"); + } + } } impl Display for PythonImplementation { @@ -652,14 +719,14 @@ impl BuildFlags { BuildFlags(HashSet::new()) } - fn from_config_map(config_map: &HashMap) -> Self { + fn from_sysconfigdata(config_map: &Sysconfigdata) -> Self { Self( BuildFlags::ALL .iter() .cloned() .filter(|flag| { config_map - .get(&flag.to_string()) + .get_value(&flag.to_string()) .map_or(false, |value| value == "1") }) .collect(), @@ -768,12 +835,25 @@ fn parse_script_output(output: &str) -> HashMap { .collect() } +/// Parsed data from Python sysconfigdata file +/// +/// A hash map of all values from a sysconfigdata file. +pub struct Sysconfigdata(std::collections::HashMap); + +impl Sysconfigdata { + fn get_value(&self, k: &str) -> Option<&str> { + self.0.get(k).map(String::as_str) + } +} + /// Parse sysconfigdata file /// /// The sysconfigdata is simply a dictionary containing all the build time variables used for the -/// python executable and library. Here it is read and added to a script to extract only what is -/// necessary. This necessitates a python interpreter for the host machine to work. -pub fn parse_sysconfigdata(sysconfigdata_path: impl AsRef) -> Result { +/// python executable and library. This function necessitates a python interpreter on the host +/// machine to work. Here it is read into a `Sysconfigdata` (hash map), which can be turned into an +/// [`InterpreterConfig`](InterpreterConfig) using +/// [`from_sysconfigdata`](InterpreterConfig::from_sysconfigdata). +pub fn parse_sysconfigdata(sysconfigdata_path: impl AsRef) -> Result { let sysconfigdata_path = sysconfigdata_path.as_ref(); let mut script = fs::read_to_string(&sysconfigdata_path).with_context(|| { format!( @@ -782,84 +862,13 @@ pub fn parse_sysconfigdata(sysconfigdata_path: impl AsRef) -> Result { - $sysconfigdata - .get($key) - .ok_or(concat!($key, " not found in sysconfigdata file")) - }; - } - - macro_rules! parse_key { - ($sysconfigdata:expr, $key:literal) => { - get_key!($sysconfigdata, $key)? - .parse() - .context(concat!("could not parse value of ", $key)) - }; - } - - let version = parse_key!(sysconfigdata, "version")?; - let pointer_width = parse_key!(sysconfigdata, "SIZEOF_VOID_P") - .map(|bytes_width: u32| bytes_width * 8) - .ok(); - - let soabi = get_key!(sysconfigdata, "SOABI")?; - let implementation = if soabi.starts_with("pypy") { - PythonImplementation::PyPy - } else if soabi.starts_with("cpython") { - PythonImplementation::CPython - } else { - bail!("unsupported Python interpreter"); - }; - - let shared = match sysconfigdata - .get("Py_ENABLE_SHARED") - .map(|string| string.as_str()) - { - Some("1") | Some("true") | Some("True") => true, - Some("0") | Some("false") | Some("False") | None => false, - _ => bail!("expected a bool (1/true/True or 0/false/False) for Py_ENABLE_SHARED"), - }; + let output = run_python_script(&find_interpreter()?, &script)?; - Ok(InterpreterConfig { - implementation, - version, - shared, - abi3: is_abi3(), - lib_dir: get_key!(sysconfigdata, "LIBDIR").ok().cloned(), - lib_name: Some(default_lib_name_unix( - version, - implementation, - sysconfigdata.get("LDVERSION").map(String::as_str), - )), - executable: None, - pointer_width, - build_flags: BuildFlags::from_config_map(&sysconfigdata).fixup(version, implementation), - suppress_build_script_link_lines: false, - extra_build_script_lines: vec![], - }) + Ok(Sysconfigdata(parse_script_output(&output))) } fn starts_with(entry: &DirEntry, pat: &str) -> bool { @@ -1018,7 +1027,7 @@ fn load_cross_compile_from_sysconfigdata( cross_compile_config: CrossCompileConfig, ) -> Result { let sysconfigdata_path = find_sysconfigdata(&cross_compile_config)?; - parse_sysconfigdata(sysconfigdata_path) + InterpreterConfig::from_sysconfigdata(parse_sysconfigdata(sysconfigdata_path)?) } fn windows_hardcoded_cross_compile( @@ -1336,27 +1345,6 @@ mod tests { assert_eq!(BuildFlags::default(), BuildFlags::new()); } - #[test] - fn build_flags_from_config_map() { - let mut config_map = HashMap::new(); - - assert_eq!(BuildFlags::from_config_map(&config_map).0, HashSet::new()); - - for flag in &BuildFlags::ALL { - config_map.insert(flag.to_string(), "0".into()); - } - - assert_eq!(BuildFlags::from_config_map(&config_map).0, HashSet::new()); - - let mut expected_flags = HashSet::new(); - for flag in &BuildFlags::ALL { - config_map.insert(flag.to_string(), "1".into()); - expected_flags.insert(flag.clone()); - } - - assert_eq!(BuildFlags::from_config_map(&config_map).0, expected_flags); - } - #[test] fn build_flags_fixup_py36_debug() { let mut build_flags = BuildFlags::new(); @@ -1599,7 +1587,14 @@ mod tests { // Couldn't find a matching sysconfigdata; never mind! Err(_) => return, }; - let parsed_config = super::parse_sysconfigdata(sysconfigdata_path).unwrap(); + let sysconfigdata = match super::parse_sysconfigdata(sysconfigdata_path) { + Ok(sysconfigdata) => sysconfigdata, + Err(_) => return, + }; + let parsed_config = match InterpreterConfig::from_sysconfigdata(sysconfigdata) { + Ok(parsed_config) => parsed_config, + Err(_) => return, + }; assert_eq!( parsed_config, From c698c2eb535f14e55e4e854367a6be31f1189539 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Mon, 22 Nov 2021 13:04:30 -0500 Subject: [PATCH 09/17] Update BuildFlags test to use from_sysconfigdata. --- pyo3-build-config/src/impl_.rs | 45 ++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 39f65683274..541c6fbd9ad 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -838,12 +838,22 @@ fn parse_script_output(output: &str) -> HashMap { /// Parsed data from Python sysconfigdata file /// /// A hash map of all values from a sysconfigdata file. -pub struct Sysconfigdata(std::collections::HashMap); +pub struct Sysconfigdata(HashMap); impl Sysconfigdata { - fn get_value(&self, k: &str) -> Option<&str> { + pub fn get_value(&self, k: &str) -> Option<&str> { self.0.get(k).map(String::as_str) } + + #[allow(dead_code)] + fn new() -> Self { + Sysconfigdata(HashMap::new()) + } + + #[allow(dead_code)] + fn insert(&mut self, k: String, v: String) { + self.0.insert(k, v); + } } /// Parse sysconfigdata file @@ -1345,6 +1355,37 @@ mod tests { assert_eq!(BuildFlags::default(), BuildFlags::new()); } + #[test] + fn build_flags_from_sysconfigdata() { + let mut sysconfigdata = Sysconfigdata::new(); + // let mut config_map = HashMap::new(); + + assert_eq!( + BuildFlags::from_sysconfigdata(&sysconfigdata).0, + HashSet::new() + ); + + for flag in &BuildFlags::ALL { + sysconfigdata.insert(flag.to_string(), "0".into()); + } + + assert_eq!( + BuildFlags::from_sysconfigdata(&sysconfigdata).0, + HashSet::new() + ); + + let mut expected_flags = HashSet::new(); + for flag in &BuildFlags::ALL { + sysconfigdata.insert(flag.to_string(), "1".into()); + expected_flags.insert(flag.clone()); + } + + assert_eq!( + BuildFlags::from_sysconfigdata(&sysconfigdata).0, + expected_flags + ); + } + #[test] fn build_flags_fixup_py36_debug() { let mut build_flags = BuildFlags::new(); From 011d8fb809049fea8a9e245124b89a3f647f3f0b Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Mon, 22 Nov 2021 16:18:34 -0500 Subject: [PATCH 10/17] Add tests for from_sysconfigdata. Refactor Sysconfigdata API to be more open. --- pyo3-build-config/src/impl_.rs | 39 ++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index ec47aaf1cae..59c09a0faea 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -272,7 +272,7 @@ print("mingw", get_platform().startswith("mingw")) /// /// Use [`parse_sysconfigdata`](parse_sysconfigdata) to generate a hash map of /// configuration values which may be used to build an `InterpreterConfig`. - pub fn from_sysconfigdata(sysconfigdata: Sysconfigdata) -> Result { + pub fn from_sysconfigdata(sysconfigdata: &Sysconfigdata) -> Result { macro_rules! get_key { ($sysconfigdata:expr, $key:literal) => { $sysconfigdata @@ -306,8 +306,7 @@ print("mingw", get_platform().startswith("mingw")) let pointer_width = parse_key!(sysconfigdata, "SIZEOF_VOID_P") .map(|bytes_width: u32| bytes_width * 8) .ok(); - let build_flags = - BuildFlags::from_sysconfigdata(&sysconfigdata).fixup(version, implementation); + let build_flags = BuildFlags::from_sysconfigdata(sysconfigdata).fixup(version); Ok(InterpreterConfig { implementation, @@ -815,8 +814,8 @@ fn parse_script_output(output: &str) -> HashMap { pub struct Sysconfigdata(HashMap); impl Sysconfigdata { - pub fn get_value(&self, k: &str) -> Option<&str> { - self.0.get(k).map(String::as_str) + pub fn get_value>(&self, k: S) -> Option<&str> { + self.0.get(k.as_ref()).map(String::as_str) } #[allow(dead_code)] @@ -825,8 +824,8 @@ impl Sysconfigdata { } #[allow(dead_code)] - fn insert(&mut self, k: String, v: String) { - self.0.insert(k, v); + fn insert>(&mut self, k: S, v: S) { + self.0.insert(k.into(), v.into()); } } @@ -1008,7 +1007,7 @@ fn load_cross_compile_from_sysconfigdata( cross_compile_config: CrossCompileConfig, ) -> Result { let sysconfigdata_path = find_sysconfigdata(&cross_compile_config)?; - InterpreterConfig::from_sysconfigdata(parse_sysconfigdata(sysconfigdata_path)?) + InterpreterConfig::from_sysconfigdata(&parse_sysconfigdata(sysconfigdata_path)?) } fn windows_hardcoded_cross_compile( @@ -1397,6 +1396,28 @@ mod tests { assert!(make_interpreter_config().is_ok()) } + #[test] + fn config_from_empty_sysconfigdata() { + // let sysconfigdata = parse_sysconfigdata(find_sysconfigdata()?)?; + // AGA3 TODO + let sysconfigdata = Sysconfigdata::new(); + assert!(InterpreterConfig::from_sysconfigdata(&sysconfigdata).is_err()); + } + + #[test] + fn config_from_sysconfigdata() { + let mut sysconfigdata = Sysconfigdata::new(); + // these are the minimal values required such that InterpreterConfig::from_sysconfigdata + // does not error + sysconfigdata.insert("SOABI", "cpython-37m-x86_64-linux-gnu"); + sysconfigdata.insert("VERSION", "3.7"); + sysconfigdata.insert("Py_ENABLE_SHARED", "1"); + sysconfigdata.insert("LIBDIR", "/usr/lib"); + sysconfigdata.insert("LDVERSION", "3.7m"); + sysconfigdata.insert("SIZEOF_VOID_P", "8"); + assert!(InterpreterConfig::from_sysconfigdata(&sysconfigdata).is_ok()); + } + #[test] fn windows_hardcoded_cross_compile() { let cross_config = CrossCompileConfig { @@ -1584,7 +1605,7 @@ mod tests { Ok(sysconfigdata) => sysconfigdata, Err(_) => return, }; - let parsed_config = match InterpreterConfig::from_sysconfigdata(sysconfigdata) { + let parsed_config = match InterpreterConfig::from_sysconfigdata(&sysconfigdata) { Ok(parsed_config) => parsed_config, Err(_) => return, }; From 79565d73df6d92bcb0201ca5ffc92b1e65d159b1 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Mon, 22 Nov 2021 21:28:49 -0500 Subject: [PATCH 11/17] Add basic tests for not cross compiling. Add some error handling. --- pyo3-build-config/src/impl_.rs | 67 ++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 59c09a0faea..6473f244118 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -574,8 +574,8 @@ pub fn any_cross_compiling_env_vars_set() -> bool { } fn cross_compiling_from_cargo_env() -> Result> { - let host = cargo_env_var("HOST").unwrap(); - let target = cargo_env_var("TARGET").unwrap(); + let host = cargo_env_var("HOST").ok_or("expected HOST env var")?; + let target = cargo_env_var("TARGET").ok_or("expected TARGET env var")?; if host == target { // Definitely not cross compiling if the host matches the target @@ -587,11 +587,14 @@ fn cross_compiling_from_cargo_env() -> Result> { return Ok(None); } - let target_arch = cargo_env_var("CARGO_CFG_TARGET_ARCH").unwrap(); - let target_vendor = cargo_env_var("CARGO_CFG_TARGET_VENDOR").unwrap(); - let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap(); + let target_arch = + cargo_env_var("CARGO_CFG_TARGET_ARCH").ok_or("expected CARGO_CFG_TARGET_ARCH env var")?; + let target_vendor = cargo_env_var("CARGO_CFG_TARGET_VENDOR") + .ok_or("expected CARGO_CFG_TARGET_VENDOR env var")?; + let target_os = + cargo_env_var("CARGO_CFG_TARGET_OS").ok_or("expected CARGO_CFG_TARGET_OS env var")?; - cross_compiling(&host, &target_vendor, &target_arch, &target_os) + cross_compiling(&host, &target_arch, &target_vendor, &target_os) } /// Detect whether we are cross compiling and return an assembled CrossCompileConfig if so. @@ -1398,8 +1401,6 @@ mod tests { #[test] fn config_from_empty_sysconfigdata() { - // let sysconfigdata = parse_sysconfigdata(find_sysconfigdata()?)?; - // AGA3 TODO let sysconfigdata = Sysconfigdata::new(); assert!(InterpreterConfig::from_sysconfigdata(&sysconfigdata).is_err()); } @@ -1653,4 +1654,54 @@ mod tests { PathBuf::from_iter(&["base", "bin", "python"]) ); } + + #[test] + fn test_not_cross_compiling_from_cargo_env() { + // make sure the HOST and TARGET env vars match, but set them back (or unset them) + match (cargo_env_var("HOST"), cargo_env_var("TARGET")) { + (Some(host), Some(target)) => { + env::set_var("HOST", target); + assert!(cross_compiling_from_cargo_env().unwrap().is_none()); + env::set_var("HOST", host); + } + (None, Some(target)) => { + env::set_var("HOST", target); + assert!(cross_compiling_from_cargo_env().unwrap().is_none()); + env::remove_var("HOST"); + } + (Some(host), None) => { + env::set_var("TARGET", host); + assert!(cross_compiling_from_cargo_env().unwrap().is_none()); + env::remove_var("TARGET"); + } + (None, None) => { + env::set_var("HOST", "x86_64-unknown-linux-gnu"); + env::set_var("TARGET", "x86_64-unknown-linux-gnu"); + assert!(cross_compiling_from_cargo_env().unwrap().is_none()); + env::remove_var("HOST"); + env::remove_var("TARGET"); + } + } + } + + #[test] + fn test_not_cross_compiling() { + assert!( + cross_compiling("aarch64-apple-darwin", "x86_64", "apple", "darwin") + .unwrap() + .is_none() + ); + assert!( + cross_compiling("x86_64-apple-darwin", "aarch64", "apple", "darwin") + .unwrap() + .is_none() + ); + assert!( + cross_compiling("x86_64-unknown-linux-gnu", "x86_64", "unknown", "linux") + .unwrap() + .is_none() + ); + } + + // TODO test cross compiling } From 8b0c3911d56c50bcd7d0ca5b5c0e4d15fc838ee7 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Mon, 29 Nov 2021 15:03:41 -0500 Subject: [PATCH 12/17] Address review comments. --- pyo3-build-config/src/impl_.rs | 84 +++++++++++++++------------------- pyo3-build-config/src/lib.rs | 5 +- 2 files changed, 39 insertions(+), 50 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 6473f244118..fa5f5c77080 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -270,8 +270,8 @@ print("mingw", get_platform().startswith("mingw")) /// Generate from parsed sysconfigdata file /// - /// Use [`parse_sysconfigdata`](parse_sysconfigdata) to generate a hash map of - /// configuration values which may be used to build an `InterpreterConfig`. + /// Use [`parse_sysconfigdata`] to generate a hash map of configuration values which may be + /// used to build an [`InterpreterConfig`]. pub fn from_sysconfigdata(sysconfigdata: &Sysconfigdata) -> Result { macro_rules! get_key { ($sysconfigdata:expr, $key:literal) => { @@ -542,7 +542,7 @@ fn is_abi3() -> bool { /// /// Usually this is collected from the environment (i.e. `PYO3_CROSS_*` and `CARGO_CFG_TARGET_*`) /// when a cross-compilation configuration is detected. -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct CrossCompileConfig { /// The directory containing the Python library to link against. pub lib_dir: PathBuf, @@ -598,6 +598,19 @@ fn cross_compiling_from_cargo_env() -> Result> { } /// 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`: 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( host: &str, target_arch: &str, @@ -637,9 +650,9 @@ pub fn cross_compiling( lib_dir: cross_lib_dir .ok_or("The PYO3_CROSS_LIB_DIR environment variable must be set when cross-compiling")? .into(), - arch: target_arch.to_string(), - vendor: target_vendor.to_string(), - os: target_os.to_string(), + arch: target_arch.into(), + vendor: target_vendor.into(), + os: target_os.into(), version: cross_python_version .map(|os_string| { let utf8_str = os_string @@ -1331,7 +1344,6 @@ mod tests { #[test] fn build_flags_from_sysconfigdata() { let mut sysconfigdata = Sysconfigdata::new(); - // let mut config_map = HashMap::new(); assert_eq!( BuildFlags::from_sysconfigdata(&sysconfigdata).0, @@ -1416,7 +1428,22 @@ mod tests { sysconfigdata.insert("LIBDIR", "/usr/lib"); sysconfigdata.insert("LDVERSION", "3.7m"); sysconfigdata.insert("SIZEOF_VOID_P", "8"); - assert!(InterpreterConfig::from_sysconfigdata(&sysconfigdata).is_ok()); + assert_eq!( + InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(), + InterpreterConfig { + abi3: false, + build_flags: BuildFlags::from_sysconfigdata(&sysconfigdata), + pointer_width: Some(64), + executable: None, + implementation: PythonImplementation::CPython, + lib_dir: Some("/usr/lib".into()), + lib_name: Some("python3.7m".into()), + shared: true, + version: PythonVersion::PY37, + suppress_build_script_link_lines: false, + extra_build_script_lines: vec![], + } + ); } #[test] @@ -1602,14 +1629,8 @@ mod tests { // Couldn't find a matching sysconfigdata; never mind! Err(_) => return, }; - let sysconfigdata = match super::parse_sysconfigdata(sysconfigdata_path) { - Ok(sysconfigdata) => sysconfigdata, - Err(_) => return, - }; - let parsed_config = match InterpreterConfig::from_sysconfigdata(&sysconfigdata) { - Ok(parsed_config) => parsed_config, - Err(_) => return, - }; + let sysconfigdata = super::parse_sysconfigdata(sysconfigdata_path).unwrap(); + let parsed_config = InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap(); assert_eq!( parsed_config, @@ -1655,35 +1676,6 @@ mod tests { ); } - #[test] - fn test_not_cross_compiling_from_cargo_env() { - // make sure the HOST and TARGET env vars match, but set them back (or unset them) - match (cargo_env_var("HOST"), cargo_env_var("TARGET")) { - (Some(host), Some(target)) => { - env::set_var("HOST", target); - assert!(cross_compiling_from_cargo_env().unwrap().is_none()); - env::set_var("HOST", host); - } - (None, Some(target)) => { - env::set_var("HOST", target); - assert!(cross_compiling_from_cargo_env().unwrap().is_none()); - env::remove_var("HOST"); - } - (Some(host), None) => { - env::set_var("TARGET", host); - assert!(cross_compiling_from_cargo_env().unwrap().is_none()); - env::remove_var("TARGET"); - } - (None, None) => { - env::set_var("HOST", "x86_64-unknown-linux-gnu"); - env::set_var("TARGET", "x86_64-unknown-linux-gnu"); - assert!(cross_compiling_from_cargo_env().unwrap().is_none()); - env::remove_var("HOST"); - env::remove_var("TARGET"); - } - } - } - #[test] fn test_not_cross_compiling() { assert!( @@ -1702,6 +1694,4 @@ mod tests { .is_none() ); } - - // TODO test cross compiling } diff --git a/pyo3-build-config/src/lib.rs b/pyo3-build-config/src/lib.rs index ca26e9b3bf1..0a4a147c9ba 100644 --- a/pyo3-build-config/src/lib.rs +++ b/pyo3-build-config/src/lib.rs @@ -1,8 +1,7 @@ //! Configuration used by PyO3 for conditional support of varying Python versions. //! -//! This crate exposes two functions, [`use_pyo3_cfgs`] and [`add_extension_module_link_args`], -//! which are intended to be called from build scripts to simplify building crates which depend on -//! PyO3. +//! This crate exposes functionality to be called from build scripts to simplify building crates +//! which depend on PyO3. //! //! It used internally by the PyO3 crate's build script to apply the same configuration. From 6b40cb4118ed5d8bd8dffb14b5838eadeaa13843 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Tue, 30 Nov 2021 12:10:13 -0500 Subject: [PATCH 13/17] Update search_lib_dir to recurse into lib and pypy dirs. --- pyo3-build-config/src/impl_.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index fa5f5c77080..332d6a8f040 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -918,10 +918,11 @@ fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result { /// So we must find the file in the following possible locations: /// /// ```sh -/// # distribution from package manager, lib_dir should include lib/ +/// # distribution from package manager, (lib_dir may or may not include lib/) /// ${INSTALL_PREFIX}/lib/python3.Y/_sysconfigdata*.py /// ${INSTALL_PREFIX}/lib/libpython3.Y.so /// ${INSTALL_PREFIX}/lib/python3.Y/config-3.Y-${HOST_TRIPLE}/libpython3.Y.so +/// ${INSTALL_PREFIX}/lib/pypy3.Y/_sysconfigdata.py /// /// # Built from source from host /// ${CROSS_COMPILED_LOCATION}/build/lib.linux-x86_64-Y/_sysconfigdata*.py @@ -955,12 +956,12 @@ pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Vec { /// recursive search for _sysconfigdata, returns all possibilities of sysconfigdata paths fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec { - let mut sysconfig_paths = vec![]; - let version_pat = if let Some(v) = &cross.version { - format!("python{}", v) + let (pypy_version_pat, cpython_version_pat) = if let Some(v) = &cross.version { + (format!("pypy{}", v), format!("python{}", v)) } else { - "python3.".into() + ("pypy3.".into(), "python3.".into()) }; + let mut sysconfig_paths = vec![]; for f in fs::read_dir(path).expect("Path does not exist").into_iter() { sysconfig_paths.extend(match &f { // Python 3.7+ sysconfigdata with platform specifics @@ -968,7 +969,7 @@ fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec { let file_name = f.file_name(); let file_name = file_name.to_string_lossy(); - if file_name.starts_with("build") { + if file_name == "build" || file_name == "lib" { search_lib_dir(f.path(), cross) } else if file_name.starts_with("lib.") { // check if right target os @@ -984,7 +985,9 @@ fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec Date: Tue, 30 Nov 2021 20:31:30 -0500 Subject: [PATCH 14/17] Look even harder for sysconfigdata. --- pyo3-build-config/src/impl_.rs | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 5e162a3b1d4..0ae9f3ed4a8 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -922,7 +922,6 @@ fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result { /// ${INSTALL_PREFIX}/lib/python3.Y/_sysconfigdata*.py /// ${INSTALL_PREFIX}/lib/libpython3.Y.so /// ${INSTALL_PREFIX}/lib/python3.Y/config-3.Y-${HOST_TRIPLE}/libpython3.Y.so -/// ${INSTALL_PREFIX}/lib/pypy3.Y/_sysconfigdata.py /// /// # Built from source from host /// ${CROSS_COMPILED_LOCATION}/build/lib.linux-x86_64-Y/_sysconfigdata*.py @@ -931,6 +930,10 @@ fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result { /// # if cross compiled, kernel release is only present on certain OS targets. /// ${CROSS_COMPILED_LOCATION}/build/lib.{OS}(-{OS-KERNEL-RELEASE})?-{ARCH}-Y/_sysconfigdata*.py /// ${CROSS_COMPILED_LOCATION}/libpython3.Y.so +/// +/// # PyPy includes a similar file since v73 +/// ${INSTALL_PREFIX}/lib/pypy3.Y/_sysconfigdata.py +/// ${INSTALL_PREFIX}/lib_pypy/_sysconfigdata.py /// ``` /// /// [1]: https://github.com/python/cpython/blob/3.5/Lib/sysconfig.py#L389 @@ -954,13 +957,26 @@ pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Vec { sysconfig_paths } -/// recursive search for _sysconfigdata, returns all possibilities of sysconfigdata paths -fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec { - let (pypy_version_pat, cpython_version_pat) = if let Some(v) = &cross.version { - (format!("pypy{}", v), format!("python{}", v)) +fn is_pypy_lib_dir(path: &str, v: &Option) -> bool { + let pypy_version_pat = if let Some(v) = v { + format!("pypy{}", v) } else { - ("pypy3.".into(), "python3.".into()) + "pypy3.".into() }; + path == "lib_pypy" || path.starts_with(&pypy_version_pat) +} + +fn is_cpython_lib_dir(path: &str, v: &Option) -> bool { + let cpython_version_pat = if let Some(v) = v { + format!("python{}", v) + } else { + "python3.".into() + }; + path.starts_with(&cpython_version_pat) +} + +/// recursive search for _sysconfigdata, returns all possibilities of sysconfigdata paths +fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec { let mut sysconfig_paths = vec![]; for f in fs::read_dir(path).expect("Path does not exist") { sysconfig_paths.extend(match &f { @@ -985,8 +1001,8 @@ fn search_lib_dir(path: impl AsRef, cross: &CrossCompileConfig) -> Vec Date: Wed, 1 Dec 2021 12:48:39 -0500 Subject: [PATCH 15/17] Add skip-build-config feature. --- Cargo.toml | 1 + build.rs | 8 +++++--- pyo3-build-config/src/impl_.rs | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bc8d54dafda..4b66a245d09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ pyo3-build-config = { path = "pyo3-build-config", version = "0.15.1", features = [features] default = ["macros"] +skip-build-config = [] # Enables macros: #[pyclass], #[pymodule], #[pyfunction] etc. macros = ["pyo3-macros", "indoc", "paste", "unindent"] diff --git a/build.rs b/build.rs index 2affbe0d8de..ffc09faca27 100644 --- a/build.rs +++ b/build.rs @@ -172,8 +172,10 @@ fn print_config_and_exit(config: &InterpreterConfig) { } fn main() { - if let Err(e) = configure_pyo3() { - eprintln!("error: {}", e.report()); - std::process::exit(1) + if cfg!(not(feature="skip-build-config")) { + if let Err(e) = configure_pyo3() { + eprintln!("error: {}", e.report()); + std::process::exit(1) + } } } diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 0ae9f3ed4a8..7efbe93b9f4 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -32,7 +32,9 @@ pub fn cargo_env_var(var: &str) -> Option { /// Gets an external environment variable, and registers the build script to rerun if /// the variable changes. pub fn env_var(var: &str) -> Option { - println!("cargo:rerun-if-env-changed={}", var); + if cfg!(feature = "resolve-config") { + println!("cargo:rerun-if-env-changed={}", var); + } env::var_os(var) } From 2986aca0f60f2968ef60b32fd6caeb98b2b18290 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Thu, 2 Dec 2021 11:56:47 -0500 Subject: [PATCH 16/17] Revert skip-build-config feature. --- Cargo.toml | 1 - build.rs | 8 +++----- pyo3-build-config/src/impl_.rs | 4 +--- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4b66a245d09..bc8d54dafda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,7 +54,6 @@ pyo3-build-config = { path = "pyo3-build-config", version = "0.15.1", features = [features] default = ["macros"] -skip-build-config = [] # Enables macros: #[pyclass], #[pymodule], #[pyfunction] etc. macros = ["pyo3-macros", "indoc", "paste", "unindent"] diff --git a/build.rs b/build.rs index ffc09faca27..2affbe0d8de 100644 --- a/build.rs +++ b/build.rs @@ -172,10 +172,8 @@ fn print_config_and_exit(config: &InterpreterConfig) { } fn main() { - if cfg!(not(feature="skip-build-config")) { - if let Err(e) = configure_pyo3() { - eprintln!("error: {}", e.report()); - std::process::exit(1) - } + if let Err(e) = configure_pyo3() { + eprintln!("error: {}", e.report()); + std::process::exit(1) } } diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 7efbe93b9f4..0ae9f3ed4a8 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -32,9 +32,7 @@ pub fn cargo_env_var(var: &str) -> Option { /// Gets an external environment variable, and registers the build script to rerun if /// the variable changes. pub fn env_var(var: &str) -> Option { - if cfg!(feature = "resolve-config") { - println!("cargo:rerun-if-env-changed={}", var); - } + println!("cargo:rerun-if-env-changed={}", var); env::var_os(var) } From 41cc76df4116513c771f0bec23cdefe98724c831 Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Fri, 3 Dec 2021 12:08:48 -0500 Subject: [PATCH 17/17] Suppress cargo:rerun-if-env-changed without resolve-config feature. --- pyo3-build-config/src/impl_.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 0ae9f3ed4a8..7efbe93b9f4 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -32,7 +32,9 @@ pub fn cargo_env_var(var: &str) -> Option { /// Gets an external environment variable, and registers the build script to rerun if /// the variable changes. pub fn env_var(var: &str) -> Option { - println!("cargo:rerun-if-env-changed={}", var); + if cfg!(feature = "resolve-config") { + println!("cargo:rerun-if-env-changed={}", var); + } env::var_os(var) }