Skip to content

Commit

Permalink
pyo3-build-config: Create per-target cross config files
Browse files Browse the repository at this point in the history
Rename `$OUT_DIR/pyo3-cross-compile-config.txt` to
`$OUT_DIR/<triple>/pyo3-build-config.txt` to exclude the possibility
of using stale build configuration data when the build target changes.

Use the presence of the corresponding build configuration file
in the `pyo3-build-config` build script output directory
to detect whether we are cross compiling or not.

This patch enables cross compilation without using
any of `PYO3_CROSS_*` env variables in many cases.
  • Loading branch information
ravenexp committed Mar 29, 2022
1 parent dbbcb5b commit 525bbe3
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 13 deletions.
2 changes: 1 addition & 1 deletion pyo3-build-config/src/impl_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ pub(crate) struct CrossCompileEnvVars {
}

impl CrossCompileEnvVars {
pub fn any(&self) -> bool {
fn any(&self) -> bool {
self.pyo3_cross.is_some()
|| self.pyo3_cross_lib_dir.is_some()
|| self.pyo3_cross_python_version.is_some()
Expand Down
44 changes: 32 additions & 12 deletions pyo3-build-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ mod errors;
mod impl_;

#[cfg(feature = "resolve-config")]
use std::io::Cursor;
use std::{
io::Cursor,
path::{Path, PathBuf},
};

use std::{env, process::Command};

#[cfg(feature = "resolve-config")]
Expand Down Expand Up @@ -69,27 +73,28 @@ fn _add_extension_module_link_args(target_os: &str, mut writer: impl std::io::Wr
pub fn get() -> &'static InterpreterConfig {
static CONFIG: OnceCell<InterpreterConfig> = OnceCell::new();
CONFIG.get_or_init(|| {
// Check if we are in a build script and cross compiling to a different target.
let cross_compile_config_path = resolve_cross_compile_config_path();
let cross_compiling = cross_compile_config_path
.as_ref()
.map(|path| path.exists())
.unwrap_or(false);

if let Some(interpreter_config) = InterpreterConfig::from_cargo_dep_env() {
interpreter_config
} else if !CONFIG_FILE.is_empty() {
InterpreterConfig::from_reader(Cursor::new(CONFIG_FILE))
} else if !ABI3_CONFIG.is_empty() {
Ok(abi3_config())
} else if impl_::cross_compile_env_vars().any() {
InterpreterConfig::from_path(DEFAULT_CROSS_COMPILE_CONFIG_PATH)
} else if cross_compiling {
InterpreterConfig::from_path(cross_compile_config_path.as_ref().unwrap())
} else {
InterpreterConfig::from_reader(Cursor::new(HOST_CONFIG))
}
.expect("failed to parse PyO3 config")
})
}

/// Path where PyO3's build.rs will write configuration by default.
#[doc(hidden)]
#[cfg(feature = "resolve-config")]
const DEFAULT_CROSS_COMPILE_CONFIG_PATH: &str =
concat!(env!("OUT_DIR"), "/pyo3-cross-compile-config.txt");

/// Build configuration provided by `PYO3_CONFIG_FILE`. May be empty if env var not set.
#[doc(hidden)]
#[cfg(feature = "resolve-config")]
Expand All @@ -107,6 +112,22 @@ const ABI3_CONFIG: &str = include_str!(concat!(env!("OUT_DIR"), "/pyo3-build-con
#[cfg(feature = "resolve-config")]
const HOST_CONFIG: &str = include_str!(concat!(env!("OUT_DIR"), "/pyo3-build-config.txt"));

/// Returns the path where PyO3's build.rs writes its cross compile configuration.
///
/// The config file will be named `$OUT_DIR/<triple>/pyo3-build-config.txt`.
///
/// Must be called from a build script, returns `None` if not.
#[doc(hidden)]
#[cfg(feature = "resolve-config")]
fn resolve_cross_compile_config_path() -> Option<PathBuf> {
env::var_os("TARGET").map(|target| {
let mut path = PathBuf::from(env!("OUT_DIR"));
path.push(Path::new(&target));
path.push("pyo3-build-config.txt");
path
})
}

#[cfg(feature = "resolve-config")]
fn abi3_config() -> InterpreterConfig {
let mut interpreter_config = InterpreterConfig::from_reader(Cursor::new(ABI3_CONFIG))
Expand Down Expand Up @@ -158,8 +179,6 @@ pub fn print_feature_cfgs() {
pub mod pyo3_build_script_impl {
#[cfg(feature = "resolve-config")]
use crate::errors::{Context, Result};
#[cfg(feature = "resolve-config")]
use std::path::Path;

#[cfg(feature = "resolve-config")]
use super::*;
Expand All @@ -185,7 +204,8 @@ pub mod pyo3_build_script_impl {
Ok(abi3_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 path = resolve_cross_compile_config_path()
.expect("resolve_interpreter_config() must be called from a build script");
let parent_dir = path.parent().ok_or_else(|| {
format!(
"failed to resolve parent directory of config file {}",
Expand Down

0 comments on commit 525bbe3

Please sign in to comment.