#![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};
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;
#[doc = concat!("[see PyO3's guide](https://pyo3.rs/v", env!("CARGO_PKG_VERSION"), "/building-and-distribution/multiple_python_versions.html)")]
#[cfg(feature = "resolve-config")]
pub fn use_pyo3_cfgs() {
print_expected_cfgs();
for cargo_command in get().build_script_outputs() {
println!("{}", cargo_command)
}
}
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);
#[allow(unknown_lints, clippy::const_is_empty)]
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() {
let rustc_minor_version = rustc_minor_version().unwrap_or(0);
if rustc_minor_version >= 74 {
println!("cargo:rustc-cfg=invalid_from_utf8_lint");
}
if rustc_minor_version >= 79 {
println!("cargo:rustc-cfg=c_str_lit");
}
if rustc_minor_version >= 79 {
println!("cargo:rustc-cfg=diagnostic_namespace");
}
}
#[doc(hidden)]
pub fn print_expected_cfgs() {
if rustc_minor_version().map_or(false, |version| version < 80) {
return;
}
println!("cargo:rustc-check-cfg=cfg(Py_LIMITED_API)");
println!("cargo:rustc-check-cfg=cfg(PyPy)");
println!("cargo:rustc-check-cfg=cfg(GraalPy)");
println!("cargo:rustc-check-cfg=cfg(py_sys_config, values(\"Py_DEBUG\", \"Py_REF_DEBUG\", \"Py_TRACE_REFS\", \"COUNT_ALLOCS\"))");
println!("cargo:rustc-check-cfg=cfg(invalid_from_utf8_lint)");
println!("cargo:rustc-check-cfg=cfg(pyo3_disable_reference_pool)");
println!("cargo:rustc-check-cfg=cfg(pyo3_leak_on_drop_without_reference_pool)");
println!("cargo:rustc-check-cfg=cfg(diagnostic_namespace)");
println!("cargo:rustc-check-cfg=cfg(c_str_lit)");
for i in impl_::MINIMUM_SUPPORTED_VERSION.minor..=impl_::ABI3_MAX_MINOR + 1 {
println!("cargo:rustc-check-cfg=cfg(Py_3_{i})");
}
}
#[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> {
#[allow(unknown_lints, clippy::const_is_empty)]
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))
}
}
}
fn rustc_minor_version() -> Option<u32> {
static RUSTC_MINOR_VERSION: OnceCell<Option<u32>> = OnceCell::new();
*RUSTC_MINOR_VERSION.get_or_init(|| {
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()
})
}
#[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"
);
}
}