From 755bf97fdbe9399cfbd072ff5e9a26c81d5890dc Mon Sep 17 00:00:00 2001 From: Ashley Anderson Date: Fri, 7 Jan 2022 20:44:36 -0500 Subject: [PATCH] Add export-config feature to pyo3-build-config. --- pyo3-build-config/Cargo.toml | 3 ++ pyo3-build-config/src/impl_.rs | 73 +++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/pyo3-build-config/Cargo.toml b/pyo3-build-config/Cargo.toml index 2233dd78..88c1b1df 100644 --- a/pyo3-build-config/Cargo.toml +++ b/pyo3-build-config/Cargo.toml @@ -20,6 +20,9 @@ default = [] # script. If this feature isn't enabled, the build script no-ops. resolve-config = [] +# Export the resolved build config into a file for use by e.g. other build scripts. +export-config = ["resolve-config"] + abi3 = [] abi3-py37 = ["abi3-py38"] abi3-py38 = ["abi3-py39"] diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index f18221ae..243c0df3 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -4,7 +4,7 @@ use std::{ env, ffi::{OsStr, OsString}, fmt::Display, - fs::{self, DirEntry}, + fs::{self, DirEntry, File}, io::{BufRead, BufReader, Read, Write}, path::{Path, PathBuf}, process::{Command, Stdio}, @@ -21,6 +21,8 @@ use crate::{ const MINIMUM_SUPPORTED_VERSION: PythonVersion = PythonVersion { major: 3, minor: 7 }; /// Maximum Python version that can be used as minimum required Python version with abi3. const ABI3_MAX_MINOR: u8 = 9; +/// Name of config file exported by "export-config" feature. +const EXPORT_CONFIG_FILENAME: &str = ".pyo3-export-config"; /// Gets an environment variable owned by cargo. /// @@ -155,6 +157,24 @@ impl InterpreterConfig { for flag in &self.build_flags.0 { println!("cargo:rustc-cfg=py_sys_config=\"{}\"", flag) } + + if cfg!(feature = "export-config") { + let output_path = + Path::new(&cargo_env_var("OUT_DIR").unwrap()).join(EXPORT_CONFIG_FILENAME); + if let Ok(config_file) = File::create(&output_path) { + if self.to_writer(config_file).is_err() { + warn!( + "Failed to export build config - this may be a problem for external crates that \ + depend on the pyo3 config." + ); + } else { + println!( + "cargo:PYO3_EXPORT_CONFIG={}", + output_path.canonicalize().unwrap().to_str().unwrap() + ); + } + } + } } #[doc(hidden)] @@ -339,6 +359,14 @@ print("mingw", get_platform().startswith("mingw")) InterpreterConfig::from_reader(reader) } + /// Create an InterpreterConfig generated from pyo3-build-config using the "export-config" + /// feature + pub fn from_pyo3_export_config() -> Result { + InterpreterConfig::from_path(Path::new( + &cargo_env_var("DEP_PYTHON_PYO3_EXPORT_CONFIG").unwrap(), + )) + } + #[doc(hidden)] pub fn from_reader(reader: impl Read) -> Result { let reader = BufReader::new(reader); @@ -461,6 +489,37 @@ print("mingw", get_platform().startswith("mingw")) } Ok(()) } + + /// Run a python script using the executable of this InterpreterConfig with additional + /// environment variables (e.g. PYTHONPATH) set. + pub fn run_python_script_with_envs(&self, script: &str, envs: I) -> Result + where + I: IntoIterator, + K: AsRef, + V: AsRef, + { + run_python_script_with_envs( + Path::new( + self.executable + .as_ref() + .expect("No interpreter executable!"), + ), + script, + envs, + ) + } + + /// Run a python script using the executable of this InterpreterConfig. + pub fn run_python_script(&self, script: &str) -> Result { + run_python_script( + Path::new( + self.executable + .as_ref() + .expect("No interpreter executable!"), + ), + script, + ) + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -1183,8 +1242,20 @@ fn default_lib_name_unix( /// Run a python script using the specified interpreter binary. fn run_python_script(interpreter: &Path, script: &str) -> Result { + run_python_script_with_envs(interpreter, script, std::iter::empty::<(&str, &str)>()) +} + +/// Run a python script using the specified interpreter binary with additional environment +/// variables (e.g. PYTHONPATH) set. +fn run_python_script_with_envs(interpreter: &Path, script: &str, envs: I) -> Result +where + I: IntoIterator, + K: AsRef, + V: AsRef, +{ let out = Command::new(interpreter) .env("PYTHONIOENCODING", "utf-8") + .envs(envs) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::inherit())