213 lines
7.5 KiB
Rust
213 lines
7.5 KiB
Rust
use pyo3_build_config::{
|
|
bail, ensure, print_feature_cfgs,
|
|
pyo3_build_script_impl::{
|
|
cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config,
|
|
InterpreterConfig, PythonVersion,
|
|
},
|
|
PythonImplementation,
|
|
};
|
|
|
|
/// Minimum Python version PyO3 supports.
|
|
struct SupportedVersions {
|
|
min: PythonVersion,
|
|
max: PythonVersion,
|
|
}
|
|
|
|
const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions {
|
|
min: PythonVersion { major: 3, minor: 7 },
|
|
max: PythonVersion {
|
|
major: 3,
|
|
minor: 12,
|
|
},
|
|
};
|
|
|
|
const SUPPORTED_VERSIONS_PYPY: SupportedVersions = SupportedVersions {
|
|
min: PythonVersion { major: 3, minor: 7 },
|
|
max: PythonVersion {
|
|
major: 3,
|
|
minor: 10,
|
|
},
|
|
};
|
|
|
|
const SUPPORTED_VERSIONS_GRAALPY: SupportedVersions = SupportedVersions {
|
|
min: PythonVersion {
|
|
major: 3,
|
|
minor: 10,
|
|
},
|
|
max: PythonVersion {
|
|
major: 3,
|
|
minor: 11,
|
|
},
|
|
};
|
|
|
|
fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> {
|
|
// This is an undocumented env var which is only really intended to be used in CI / for testing
|
|
// and development.
|
|
if std::env::var("UNSAFE_PYO3_SKIP_VERSION_CHECK").as_deref() == Ok("1") {
|
|
return Ok(());
|
|
}
|
|
|
|
match interpreter_config.implementation {
|
|
PythonImplementation::CPython => {
|
|
let versions = SUPPORTED_VERSIONS_CPYTHON;
|
|
ensure!(
|
|
interpreter_config.version >= versions.min,
|
|
"the configured Python interpreter version ({}) is lower than PyO3's minimum supported version ({})",
|
|
interpreter_config.version,
|
|
versions.min,
|
|
);
|
|
ensure!(
|
|
interpreter_config.version <= versions.max || env_var("PYO3_USE_ABI3_FORWARD_COMPATIBILITY").map_or(false, |os_str| os_str == "1"),
|
|
"the configured Python interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\
|
|
= help: please check if an updated version of PyO3 is available. Current version: {}\n\
|
|
= help: set PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 to suppress this check and build anyway using the stable ABI",
|
|
interpreter_config.version,
|
|
versions.max,
|
|
std::env::var("CARGO_PKG_VERSION").unwrap(),
|
|
);
|
|
}
|
|
PythonImplementation::PyPy => {
|
|
let versions = SUPPORTED_VERSIONS_PYPY;
|
|
ensure!(
|
|
interpreter_config.version >= versions.min,
|
|
"the configured PyPy interpreter version ({}) is lower than PyO3's minimum supported version ({})",
|
|
interpreter_config.version,
|
|
versions.min,
|
|
);
|
|
// PyO3 does not support abi3, so we cannot offer forward compatibility
|
|
ensure!(
|
|
interpreter_config.version <= versions.max,
|
|
"the configured PyPy interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\
|
|
= help: please check if an updated version of PyO3 is available. Current version: {}",
|
|
interpreter_config.version,
|
|
versions.max,
|
|
std::env::var("CARGO_PKG_VERSION").unwrap()
|
|
);
|
|
}
|
|
PythonImplementation::GraalPy => {
|
|
let versions = SUPPORTED_VERSIONS_GRAALPY;
|
|
ensure!(
|
|
interpreter_config.version >= versions.min,
|
|
"the configured GraalPy interpreter version ({}) is lower than PyO3's minimum supported version ({})",
|
|
interpreter_config.version,
|
|
versions.min,
|
|
);
|
|
// GraalPy does not support abi3, so we cannot offer forward compatibility
|
|
ensure!(
|
|
interpreter_config.version <= versions.max,
|
|
"the configured GraalPy interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\
|
|
= help: please check if an updated version of PyO3 is available. Current version: {}",
|
|
interpreter_config.version,
|
|
versions.max,
|
|
std::env::var("CARGO_PKG_VERSION").unwrap()
|
|
);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> {
|
|
if let Some(pointer_width) = interpreter_config.pointer_width {
|
|
// Try to check whether the target architecture matches the python library
|
|
let rust_target = match cargo_env_var("CARGO_CFG_TARGET_POINTER_WIDTH")
|
|
.unwrap()
|
|
.as_str()
|
|
{
|
|
"64" => 64,
|
|
"32" => 32,
|
|
x => bail!("unexpected Rust target pointer width: {}", x),
|
|
};
|
|
|
|
ensure!(
|
|
rust_target == pointer_width,
|
|
"your Rust target architecture ({}-bit) does not match your python interpreter ({}-bit)",
|
|
rust_target,
|
|
pointer_width
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn emit_link_config(interpreter_config: &InterpreterConfig) -> Result<()> {
|
|
let target_os = cargo_env_var("CARGO_CFG_TARGET_OS").unwrap();
|
|
|
|
println!(
|
|
"cargo:rustc-link-lib={link_model}{alias}{lib_name}",
|
|
link_model = if interpreter_config.shared {
|
|
""
|
|
} else {
|
|
"static="
|
|
},
|
|
alias = if target_os == "windows" {
|
|
"pythonXY:"
|
|
} else {
|
|
""
|
|
},
|
|
lib_name = interpreter_config.lib_name.as_ref().ok_or(
|
|
"attempted to link to Python shared library but config does not contain lib_name"
|
|
)?,
|
|
);
|
|
|
|
if let Some(lib_dir) = &interpreter_config.lib_dir {
|
|
println!("cargo:rustc-link-search=native={}", lib_dir);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Prepares the PyO3 crate for compilation.
|
|
///
|
|
/// This loads the config from pyo3-build-config and then makes some additional checks to improve UX
|
|
/// for users.
|
|
///
|
|
/// Emits the cargo configuration based on this config as well as a few checks of the Rust compiler
|
|
/// version to enable features which aren't supported on MSRV.
|
|
fn configure_pyo3() -> Result<()> {
|
|
let interpreter_config = resolve_interpreter_config()?;
|
|
|
|
if env_var("PYO3_PRINT_CONFIG").map_or(false, |os_str| os_str == "1") {
|
|
print_config_and_exit(&interpreter_config);
|
|
}
|
|
|
|
ensure_python_version(&interpreter_config)?;
|
|
ensure_target_pointer_width(&interpreter_config)?;
|
|
|
|
// Serialize the whole interpreter config into DEP_PYTHON_PYO3_CONFIG env var.
|
|
interpreter_config.to_cargo_dep_env()?;
|
|
|
|
if is_linking_libpython() && !interpreter_config.suppress_build_script_link_lines {
|
|
emit_link_config(&interpreter_config)?;
|
|
}
|
|
|
|
for cfg in interpreter_config.build_script_outputs() {
|
|
println!("{}", cfg)
|
|
}
|
|
|
|
// Extra lines come last, to support last write wins.
|
|
for line in &interpreter_config.extra_build_script_lines {
|
|
println!("{}", line);
|
|
}
|
|
|
|
// Emit cfgs like `thread_local_const_init`
|
|
print_feature_cfgs();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn print_config_and_exit(config: &InterpreterConfig) {
|
|
println!("\n-- PYO3_PRINT_CONFIG=1 is set, printing configuration and halting compile --");
|
|
config
|
|
.to_writer(std::io::stdout())
|
|
.expect("failed to print config to stdout");
|
|
println!("\nnote: unset the PYO3_PRINT_CONFIG environment variable and retry to compile with the above config");
|
|
std::process::exit(101);
|
|
}
|
|
|
|
fn main() {
|
|
if let Err(e) = configure_pyo3() {
|
|
eprintln!("error: {}", e.report());
|
|
std::process::exit(1)
|
|
}
|
|
}
|