pyo3-build-config: Make lib_dir
optional in CrossCompileConfig
Change the `CrossCompileConfig` structure definition and make the public `lib_dir` field optional to support more flexible cross-compilation configuration in the future. FIXME: This change breaks the public `pyo3-build-config` crate API. Update the sysconfigdata extraction functions to fall through when `lib_dir` field is not set. WIP: Add `unwrap()` stubs to the main cross compile switch.
This commit is contained in:
parent
601e3d6517
commit
54ae025722
|
@ -685,7 +685,7 @@ fn is_linking_libpython_for_target(target: &Triple) -> bool {
|
|||
#[derive(Debug, PartialEq)]
|
||||
pub struct CrossCompileConfig {
|
||||
/// The directory containing the Python library to link against.
|
||||
pub lib_dir: PathBuf,
|
||||
pub lib_dir: Option<PathBuf>,
|
||||
|
||||
/// The version of the Python library to link against.
|
||||
version: Option<PythonVersion>,
|
||||
|
@ -745,8 +745,10 @@ impl CrossCompileConfig {
|
|||
///
|
||||
/// 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()
|
||||
fn lib_dir_string(&self) -> Option<String> {
|
||||
self.lib_dir
|
||||
.as_ref()
|
||||
.map(|s| s.to_str().unwrap().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -786,7 +788,7 @@ impl CrossCompileEnvVars {
|
|||
/// into a `PathBuf` instance.
|
||||
///
|
||||
/// Ensures that the path is a valid UTF-8 string.
|
||||
fn lib_dir_path(&self) -> Result<PathBuf> {
|
||||
fn lib_dir_path(&self) -> Result<Option<PathBuf>> {
|
||||
let lib_dir = self.pyo3_cross_lib_dir.as_ref().map(PathBuf::from);
|
||||
|
||||
if let Some(dir) = lib_dir.as_ref() {
|
||||
|
@ -794,11 +796,9 @@ impl CrossCompileEnvVars {
|
|||
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")
|
||||
}
|
||||
|
||||
Ok(lib_dir)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -815,9 +815,9 @@ pub(crate) fn cross_compile_env_vars() -> CrossCompileEnvVars {
|
|||
/// 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_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
|
||||
|
@ -1109,13 +1109,22 @@ fn ends_with(entry: &DirEntry, pat: &str) -> bool {
|
|||
name.to_string_lossy().ends_with(pat)
|
||||
}
|
||||
|
||||
fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result<PathBuf> {
|
||||
/// Finds the sysconfigdata file when the target Python library directory is set.
|
||||
///
|
||||
/// Returns `None` if the library directory is not available, and a runtime error
|
||||
/// when no or multiple sysconfigdata files are found.
|
||||
fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result<Option<PathBuf>> {
|
||||
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()
|
||||
);
|
||||
if let Some(lib_dir) = cross.lib_dir.as_ref() {
|
||||
bail!(
|
||||
"Could not find either libpython.so or _sysconfigdata*.py in {}",
|
||||
lib_dir.display()
|
||||
);
|
||||
} else {
|
||||
// Continue with the default configuration when PYO3_CROSS_LIB_DIR is not set.
|
||||
return Ok(None);
|
||||
}
|
||||
} else if sysconfig_paths.len() > 1 {
|
||||
let mut error_msg = String::from(
|
||||
"Detected multiple possible Python versions. Please set either the \
|
||||
|
@ -1129,7 +1138,7 @@ fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result<PathBuf> {
|
|||
bail!("{}\n", error_msg);
|
||||
}
|
||||
|
||||
Ok(sysconfig_paths.remove(0))
|
||||
Ok(Some(sysconfig_paths.remove(0)))
|
||||
}
|
||||
|
||||
/// Finds `_sysconfigdata*.py` files for detected Python interpreters.
|
||||
|
@ -1167,8 +1176,16 @@ fn find_sysconfigdata(cross: &CrossCompileConfig) -> Result<PathBuf> {
|
|||
/// ```
|
||||
///
|
||||
/// [1]: https://github.com/python/cpython/blob/3.5/Lib/sysconfig.py#L389
|
||||
///
|
||||
/// Returns an empty vector when the target Python library directory
|
||||
/// is not set via `PYO3_CROSS_LIB_DIR`.
|
||||
pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Vec<PathBuf> {
|
||||
let sysconfig_paths = search_lib_dir(&cross.lib_dir, cross);
|
||||
let sysconfig_paths = if let Some(lib_dir) = cross.lib_dir.as_ref() {
|
||||
search_lib_dir(lib_dir, cross)
|
||||
} else {
|
||||
return Vec::new();
|
||||
};
|
||||
|
||||
let sysconfig_name = env_var("_PYTHON_SYSCONFIGDATA_NAME");
|
||||
let mut sysconfig_paths = sysconfig_paths
|
||||
.iter()
|
||||
|
@ -1267,11 +1284,19 @@ fn search_lib_dir(path: impl AsRef<Path>, cross: &CrossCompileConfig) -> Vec<Pat
|
|||
/// first find sysconfigdata file which follows the pattern [`_sysconfigdata_{abi}_{platform}_{multiarch}`][1]
|
||||
///
|
||||
/// [1]: https://github.com/python/cpython/blob/3.8/Lib/sysconfig.py#L348
|
||||
///
|
||||
/// Returns `None` when the target Python library directory is not set.
|
||||
fn cross_compile_from_sysconfigdata(
|
||||
cross_compile_config: CrossCompileConfig,
|
||||
) -> Result<InterpreterConfig> {
|
||||
let sysconfigdata_path = find_sysconfigdata(&cross_compile_config)?;
|
||||
InterpreterConfig::from_sysconfigdata(&parse_sysconfigdata(sysconfigdata_path)?)
|
||||
cross_compile_config: &CrossCompileConfig,
|
||||
) -> Result<Option<InterpreterConfig>> {
|
||||
if let Some(path) = find_sysconfigdata(cross_compile_config)? {
|
||||
let data = parse_sysconfigdata(path)?;
|
||||
let config = InterpreterConfig::from_sysconfigdata(&data)?;
|
||||
|
||||
Ok(Some(config))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn windows_hardcoded_cross_compile(
|
||||
|
@ -1289,7 +1314,7 @@ fn windows_hardcoded_cross_compile(
|
|||
let implementation = PythonImplementation::CPython;
|
||||
let mingw = cross_compile_config.target.environment == Environment::Gnu;
|
||||
|
||||
let lib_dir = Some(cross_compile_config.lib_dir_string());
|
||||
let lib_dir = cross_compile_config.lib_dir_string();
|
||||
|
||||
Ok(InterpreterConfig {
|
||||
implementation,
|
||||
|
@ -1316,11 +1341,15 @@ fn load_cross_compile_config(
|
|||
) -> Result<InterpreterConfig> {
|
||||
match cargo_env_var("CARGO_CFG_TARGET_FAMILY") {
|
||||
// Configure for unix platforms using the sysconfigdata file
|
||||
Some(os) if os == "unix" => cross_compile_from_sysconfigdata(cross_compile_config),
|
||||
Some(os) if os == "unix" => cross_compile_from_sysconfigdata(&cross_compile_config)
|
||||
.transpose()
|
||||
.unwrap(),
|
||||
// Use hardcoded interpreter config when targeting Windows
|
||||
Some(os) if os == "windows" => windows_hardcoded_cross_compile(cross_compile_config),
|
||||
// sysconfigdata works fine on wasm/wasi
|
||||
Some(os) if os == "wasm" => cross_compile_from_sysconfigdata(cross_compile_config),
|
||||
Some(os) if os == "wasm" => cross_compile_from_sysconfigdata(&cross_compile_config)
|
||||
.transpose()
|
||||
.unwrap(),
|
||||
// Waiting for users to tell us what they expect on their target platform
|
||||
Some(os) => bail!(
|
||||
"Unknown target OS family for cross-compilation: {:?}.\n\
|
||||
|
@ -1330,7 +1359,9 @@ fn load_cross_compile_config(
|
|||
os
|
||||
),
|
||||
// Unknown os family - try to do something useful
|
||||
None => cross_compile_from_sysconfigdata(cross_compile_config),
|
||||
None => cross_compile_from_sysconfigdata(&cross_compile_config)
|
||||
.transpose()
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1758,7 +1789,7 @@ mod tests {
|
|||
#[test]
|
||||
fn windows_hardcoded_cross_compile() {
|
||||
let cross_config = CrossCompileConfig {
|
||||
lib_dir: "C:\\some\\path".into(),
|
||||
lib_dir: Some("C:\\some\\path".into()),
|
||||
version: Some(PythonVersion { major: 3, minor: 7 }),
|
||||
target: triple!("i686-pc-windows-msvc"),
|
||||
};
|
||||
|
@ -1784,7 +1815,7 @@ mod tests {
|
|||
#[test]
|
||||
fn mingw_hardcoded_cross_compile() {
|
||||
let cross_config = CrossCompileConfig {
|
||||
lib_dir: "/usr/lib/mingw".into(),
|
||||
lib_dir: Some("/usr/lib/mingw".into()),
|
||||
version: Some(PythonVersion { major: 3, minor: 8 }),
|
||||
target: triple!("i686-pc-windows-gnu"),
|
||||
};
|
||||
|
@ -1989,15 +2020,15 @@ mod tests {
|
|||
};
|
||||
|
||||
let cross = CrossCompileConfig {
|
||||
lib_dir: lib_dir.into(),
|
||||
lib_dir: Some(lib_dir.into()),
|
||||
version: Some(interpreter_config.version),
|
||||
target: triple!("x86_64-unknown-linux-gnu"),
|
||||
};
|
||||
|
||||
let sysconfigdata_path = match find_sysconfigdata(&cross) {
|
||||
Ok(path) => path,
|
||||
Ok(Some(path)) => path,
|
||||
// Couldn't find a matching sysconfigdata; never mind!
|
||||
Err(_) => return,
|
||||
_ => return,
|
||||
};
|
||||
let sysconfigdata = super::parse_sysconfigdata(sysconfigdata_path).unwrap();
|
||||
let parsed_config = InterpreterConfig::from_sysconfigdata(&sysconfigdata).unwrap();
|
||||
|
|
Loading…
Reference in a new issue