#![warn(elided_lifetimes_in_paths, unused_lifetimes)]
mod errors;
mod impl_;
#[cfg(feature = "resolve-config")]
use std::{
io::Cursor,
path::{Path, PathBuf},
};
use std::{env, process::Command, str::FromStr};
#[cfg(feature = "resolve-config")]
use once_cell::sync::OnceCell;
pub use impl_::{
cross_compiling_from_to, find_all_sysconfigdata, parse_sysconfigdata, BuildFlag, BuildFlags,
CrossCompileConfig, InterpreterConfig, PythonImplementation, PythonVersion, Triple,
};
use target_lexicon::OperatingSystem;
#[cfg(feature = "resolve-config")]
pub fn use_pyo3_cfgs() {
get().emit_pyo3_cfgs();
}
pub fn add_extension_module_link_args() {
_add_extension_module_link_args(&impl_::target_triple_from_env(), std::io::stdout())
}
fn _add_extension_module_link_args(triple: &Triple, mut writer: impl std::io::Write) {
if triple.operating_system == OperatingSystem::Darwin {
writeln!(writer, "cargo:rustc-cdylib-link-arg=-undefined").unwrap();
writeln!(writer, "cargo:rustc-cdylib-link-arg=dynamic_lookup").unwrap();
} else if triple == &Triple::from_str("wasm32-unknown-emscripten").unwrap() {
writeln!(writer, "cargo:rustc-cdylib-link-arg=-sSIDE_MODULE=2").unwrap();
writeln!(writer, "cargo:rustc-cdylib-link-arg=-sWASM_BIGINT").unwrap();
}
}
#[cfg(feature = "resolve-config")]
pub fn get() -> &'static InterpreterConfig {
static CONFIG: OnceCell<InterpreterConfig> = OnceCell::new();
CONFIG.get_or_init(|| {
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 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")
})
}
#[doc(hidden)]
#[cfg(feature = "resolve-config")]
const CONFIG_FILE: &str = include_str!(concat!(env!("OUT_DIR"), "/pyo3-build-config-file.txt"));
#[doc(hidden)]
#[cfg(feature = "resolve-config")]
const HOST_CONFIG: &str = include_str!(concat!(env!("OUT_DIR"), "/pyo3-build-config.txt"));
#[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
})
}
#[doc(hidden)]
pub fn print_feature_cfgs() {
fn rustc_minor_version() -> Option<u32> {
let rustc = env::var_os("RUSTC")?;
let output = Command::new(rustc).arg("--version").output().ok()?;
let version = core::str::from_utf8(&output.stdout).ok()?;
let mut pieces = version.split('.');
if pieces.next() != Some("rustc 1") {
return None;
}
pieces.next()?.parse().ok()
}
let rustc_minor_version = rustc_minor_version().unwrap_or(0);
if rustc_minor_version >= 51 {
println!("cargo:rustc-cfg=min_const_generics");
}
if rustc_minor_version >= 51 {
println!("cargo:rustc-cfg=addr_of");
}
if rustc_minor_version >= 53 {
println!("cargo:rustc-cfg=option_insert");
}
if rustc_minor_version >= 59 {
println!("cargo:rustc-cfg=thread_local_const_init");
}
if rustc_minor_version >= 60 {
println!("cargo:rustc-cfg=panic_unwind");
}
}
#[doc(hidden)]
pub mod pyo3_build_script_impl {
#[cfg(feature = "resolve-config")]
use crate::errors::{Context, Result};
#[cfg(feature = "resolve-config")]
use super::*;
pub mod errors {
pub use crate::errors::*;
}
pub use crate::impl_::{
cargo_env_var, env_var, is_linking_libpython, make_cross_compile_config, InterpreterConfig,
PythonVersion,
};
#[cfg(feature = "resolve-config")]
pub fn resolve_interpreter_config() -> Result<InterpreterConfig> {
if !CONFIG_FILE.is_empty() {
let mut interperter_config = InterpreterConfig::from_reader(Cursor::new(CONFIG_FILE))?;
interperter_config.generate_import_libs()?;
Ok(interperter_config)
} else if let Some(interpreter_config) = make_cross_compile_config()? {
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 {}",
path.display()
)
})?;
std::fs::create_dir_all(parent_dir).with_context(|| {
format!(
"failed to create config file directory {}",
parent_dir.display()
)
})?;
interpreter_config.to_writer(&mut std::fs::File::create(&path).with_context(
|| format!("failed to create config file at {}", path.display()),
)?)?;
Ok(interpreter_config)
} else {
InterpreterConfig::from_reader(Cursor::new(HOST_CONFIG))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extension_module_link_args() {
let mut buf = Vec::new();
_add_extension_module_link_args(
&Triple::from_str("x86_64-pc-windows-msvc").unwrap(),
&mut buf,
);
assert_eq!(buf, Vec::new());
_add_extension_module_link_args(
&Triple::from_str("x86_64-apple-darwin").unwrap(),
&mut buf,
);
assert_eq!(
std::str::from_utf8(&buf).unwrap(),
"cargo:rustc-cdylib-link-arg=-undefined\n\
cargo:rustc-cdylib-link-arg=dynamic_lookup\n"
);
buf.clear();
_add_extension_module_link_args(
&Triple::from_str("wasm32-unknown-emscripten").unwrap(),
&mut buf,
);
assert_eq!(
std::str::from_utf8(&buf).unwrap(),
"cargo:rustc-cdylib-link-arg=-sSIDE_MODULE=2\n\
cargo:rustc-cdylib-link-arg=-sWASM_BIGINT\n"
);
}
}