pyo3/build.rs

215 lines
8.8 KiB
Rust
Raw Normal View History

use std::{env, process::Command};
2020-03-08 07:17:25 +00:00
use pyo3_build_config::{InterpreterConfig, PythonImplementation, PythonVersion};
2020-03-08 07:17:25 +00:00
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
/// Minimum Python version PyO3 supports.
const MINIMUM_SUPPORTED_VERSION: PythonVersion = PythonVersion { major: 3, minor: 6 };
// A simple macro for returning an error. Resembles anyhow::bail.
macro_rules! bail {
($msg: expr) => { return Err($msg.into()); };
($fmt: literal $($args: tt)+) => { return Err(format!($fmt $($args)+).into()); };
}
// A simple macro for checking a condition. Resembles anyhow::ensure.
macro_rules! ensure {
($condition:expr, $($args: tt)+) => { if !($condition) { bail!($($args)+) } };
}
fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> {
ensure!(
interpreter_config.version >= MINIMUM_SUPPORTED_VERSION,
"the configured Python interpreter version ({}) is lower than PyO3's minimum supported version ({})",
interpreter_config.version,
MINIMUM_SUPPORTED_VERSION,
);
Ok(())
}
fn ensure_target_architecture(interpreter_config: &InterpreterConfig) -> Result<()> {
// Try to check whether the target architecture matches the python library
let rust_target = match env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap().as_str() {
"64" => "64-bit",
"32" => "32-bit",
x => bail!("unexpected Rust target pointer width: {}", x),
};
// The reason we don't use platform.architecture() here is that it's not
// reliable on macOS. See https://stackoverflow.com/a/1405971/823869.
// Similarly, sys.maxsize is not reliable on Windows. See
// https://stackoverflow.com/questions/1405913/how-do-i-determine-if-my-python-shell-is-executing-in-32bit-or-64bit-mode-on-os/1405971#comment6209952_1405971
// and https://stackoverflow.com/a/3411134/823869.
let python_target = match interpreter_config.calcsize_pointer {
Some(8) => "64-bit",
Some(4) => "32-bit",
None => {
// Unset, e.g. because we're cross-compiling. Don't check anything
// in this case.
return Ok(());
}
Some(n) => bail!("unexpected Python calcsize_pointer value: {}", n),
};
ensure!(
rust_target == python_target,
"Your Rust target architecture ({}) does not match your python interpreter ({})",
rust_target,
python_target
);
Ok(())
}
2021-04-24 09:52:58 +00:00
fn get_rustc_link_lib(config: &InterpreterConfig) -> Result<String> {
let link_name = if env::var_os("CARGO_CFG_TARGET_OS").unwrap() == "windows" {
if config.abi3 {
// Link against python3.lib for the stable ABI on Windows.
// See https://www.python.org/dev/peps/pep-0384/#linkage
//
// This contains only the limited ABI symbols.
"pythonXY:python3".to_owned()
} else if env::var_os("CARGO_CFG_TARGET_ENV").unwrap() == "gnu" {
// https://packages.msys2.org/base/mingw-w64-python
format!(
"pythonXY:python{}.{}",
config.version.major, config.version.minor
)
} else {
format!(
"pythonXY:python{}{}",
config.version.major, config.version.minor
)
}
Support rust extensions for PyPy via cpyext (#393) * wip * removed stuff * removed another change * implemented minimum amouth of ifdefs to make pypy3 hello world to compile * implemented minimum amount of ifdefs to make pypy3 hello world to compile * hacking on build.rs * compiler is happy! * few todos remain * extracted build logic to seperate module * added pypy test * finally fixed pypy structs * removed some todos * test should now be machine independent * fixed all pypy3 symbols * added pypy feature * removed `is_pypy` * added pypy2 declerations also * fix for cpython2 * improved libpypy detection * added all pypy2 macros * fixed errneous type * more fixes * fix python2 string macros * modsupport symbol * fix * fixed and added many symbols * fixes * remove dup * remove mac-specific config * fix all name mangling macros * unite imports * missing symbol * fix pybool * implemented another missing symbol * it works * fix merge conflict * uncomment non default features * cargo.toml * Cargo fmt * small merge fixes * use newer build version * whoops * fix build script * more build hacks * some random hiccups * small fixes * it builds! * it builds and runs * revert everything in FFI2 * revert changes to ffi2 * check python3 for pypy * tiny fix * revert ffi2 for real * revert weird formatting changes * bring back missing feature * tiny error * fix py3.7 issue * add pypy3.5 6.0 to travis * remove dbg! * another tiny fix * removed some useless annotations, and fixed inlines annotations * removed `pretty_assertions` * removed pypy feature from cargo.toml * fix for Py_CompileStringFlags * tox runs word_count! * __dict__ changes are not supported for PyPy * fix 3.7 and copy comment * fix test script :flushed: * transfer ownership of strings to cpython when possible * remove cstr! macro * added missing nuls * as_bytes() -> b’’ string * symbol removed by mistake * properly shim pypy date time API, some tests are passing! * extension_module tests now not crashing! (some still skipped) * maybe travis has new pypy version? * small error on windows (build script) * fix conditional compilation * try to make tests run on travis.. * invert condition * added pytest-faulthandler to facilitate debugging * correctly name dir * use full paths * say —yes to conda * fix * syntax error * change PATH * fixed a terrible bug with PyTypeObjects in PyPy * fix PyTypeObject defs * re-enabled tests! * all tests are passing! * make the fix ad-hoc for now * removed build module * revert changes that cause an additional GC bug * prevented buggy test from failing pypy * removed unused comment * don’t run coverage on pypy * removed some erroneous symbols from function calls which are actually macros * restore py37 pyunicode missing def * use only `link_name` in PyPy specific declarations * only setup PyPy when testing against PyPy * annotation that was eaten during merge * remove change to comment by mistake + unnecessary changes to cargo.toml * xfail dates test only on pypy * changed comment to be a little more helpful * cleaned up some warnings * Update src/ffi3/ceval.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin PR notes * rustfmt * some documentation * if configured via env var only, default to cpython * remove extra unsafe * refer users to guide for pypy * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin applied patch * check that pypy at least build * search explicitly for libpypy * added note about some known unsupported features * use ld_version * export PYTHON_SYS_EXECUTABLE to `cargo build` test * inverted if * always link pypy dynamically * remove unused imports * Apply @kngwyu’s suggestion * fix tox configuration * try conda virtualenv * try to simply not install python at all inside pypy environment * setup pypy before using “python" * use system_site_packages * revert change to .travis * moved cpyext datetime documentation to module level, and revised it. * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * rustfmt * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * kept only notes that are relevant to users. * invert if * use bash and not sh
2019-04-23 11:18:42 +00:00
} else {
2021-03-05 06:27:36 +00:00
match config.implementation {
2021-04-24 09:52:58 +00:00
PythonImplementation::CPython => match &config.ld_version {
Some(ld_version) => format!("python{}", ld_version),
None => {
return Err("failed to configure `ld_version` when compiling for unix".into())
}
2021-04-24 09:52:58 +00:00
},
PythonImplementation::PyPy => format!("pypy{}-c", config.version.major),
Support rust extensions for PyPy via cpyext (#393) * wip * removed stuff * removed another change * implemented minimum amouth of ifdefs to make pypy3 hello world to compile * implemented minimum amount of ifdefs to make pypy3 hello world to compile * hacking on build.rs * compiler is happy! * few todos remain * extracted build logic to seperate module * added pypy test * finally fixed pypy structs * removed some todos * test should now be machine independent * fixed all pypy3 symbols * added pypy feature * removed `is_pypy` * added pypy2 declerations also * fix for cpython2 * improved libpypy detection * added all pypy2 macros * fixed errneous type * more fixes * fix python2 string macros * modsupport symbol * fix * fixed and added many symbols * fixes * remove dup * remove mac-specific config * fix all name mangling macros * unite imports * missing symbol * fix pybool * implemented another missing symbol * it works * fix merge conflict * uncomment non default features * cargo.toml * Cargo fmt * small merge fixes * use newer build version * whoops * fix build script * more build hacks * some random hiccups * small fixes * it builds! * it builds and runs * revert everything in FFI2 * revert changes to ffi2 * check python3 for pypy * tiny fix * revert ffi2 for real * revert weird formatting changes * bring back missing feature * tiny error * fix py3.7 issue * add pypy3.5 6.0 to travis * remove dbg! * another tiny fix * removed some useless annotations, and fixed inlines annotations * removed `pretty_assertions` * removed pypy feature from cargo.toml * fix for Py_CompileStringFlags * tox runs word_count! * __dict__ changes are not supported for PyPy * fix 3.7 and copy comment * fix test script :flushed: * transfer ownership of strings to cpython when possible * remove cstr! macro * added missing nuls * as_bytes() -> b’’ string * symbol removed by mistake * properly shim pypy date time API, some tests are passing! * extension_module tests now not crashing! (some still skipped) * maybe travis has new pypy version? * small error on windows (build script) * fix conditional compilation * try to make tests run on travis.. * invert condition * added pytest-faulthandler to facilitate debugging * correctly name dir * use full paths * say —yes to conda * fix * syntax error * change PATH * fixed a terrible bug with PyTypeObjects in PyPy * fix PyTypeObject defs * re-enabled tests! * all tests are passing! * make the fix ad-hoc for now * removed build module * revert changes that cause an additional GC bug * prevented buggy test from failing pypy * removed unused comment * don’t run coverage on pypy * removed some erroneous symbols from function calls which are actually macros * restore py37 pyunicode missing def * use only `link_name` in PyPy specific declarations * only setup PyPy when testing against PyPy * annotation that was eaten during merge * remove change to comment by mistake + unnecessary changes to cargo.toml * xfail dates test only on pypy * changed comment to be a little more helpful * cleaned up some warnings * Update src/ffi3/ceval.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin PR notes * rustfmt * some documentation * if configured via env var only, default to cpython * remove extra unsafe * refer users to guide for pypy * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin applied patch * check that pypy at least build * search explicitly for libpypy * added note about some known unsupported features * use ld_version * export PYTHON_SYS_EXECUTABLE to `cargo build` test * inverted if * always link pypy dynamically * remove unused imports * Apply @kngwyu’s suggestion * fix tox configuration * try conda virtualenv * try to simply not install python at all inside pypy environment * setup pypy before using “python" * use system_site_packages * revert change to .travis * moved cpyext datetime documentation to module level, and revised it. * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * rustfmt * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * kept only notes that are relevant to users. * invert if * use bash and not sh
2019-04-23 11:18:42 +00:00
}
2020-12-30 16:27:18 +00:00
};
2017-05-13 05:05:00 +00:00
2021-04-24 09:52:58 +00:00
Ok(format!(
2020-12-30 16:27:18 +00:00
"cargo:rustc-link-lib={link_model}{link_name}",
link_model = if config.shared { "" } else { "static=" },
link_name = link_name
2021-04-24 09:52:58 +00:00
))
Support rust extensions for PyPy via cpyext (#393) * wip * removed stuff * removed another change * implemented minimum amouth of ifdefs to make pypy3 hello world to compile * implemented minimum amount of ifdefs to make pypy3 hello world to compile * hacking on build.rs * compiler is happy! * few todos remain * extracted build logic to seperate module * added pypy test * finally fixed pypy structs * removed some todos * test should now be machine independent * fixed all pypy3 symbols * added pypy feature * removed `is_pypy` * added pypy2 declerations also * fix for cpython2 * improved libpypy detection * added all pypy2 macros * fixed errneous type * more fixes * fix python2 string macros * modsupport symbol * fix * fixed and added many symbols * fixes * remove dup * remove mac-specific config * fix all name mangling macros * unite imports * missing symbol * fix pybool * implemented another missing symbol * it works * fix merge conflict * uncomment non default features * cargo.toml * Cargo fmt * small merge fixes * use newer build version * whoops * fix build script * more build hacks * some random hiccups * small fixes * it builds! * it builds and runs * revert everything in FFI2 * revert changes to ffi2 * check python3 for pypy * tiny fix * revert ffi2 for real * revert weird formatting changes * bring back missing feature * tiny error * fix py3.7 issue * add pypy3.5 6.0 to travis * remove dbg! * another tiny fix * removed some useless annotations, and fixed inlines annotations * removed `pretty_assertions` * removed pypy feature from cargo.toml * fix for Py_CompileStringFlags * tox runs word_count! * __dict__ changes are not supported for PyPy * fix 3.7 and copy comment * fix test script :flushed: * transfer ownership of strings to cpython when possible * remove cstr! macro * added missing nuls * as_bytes() -> b’’ string * symbol removed by mistake * properly shim pypy date time API, some tests are passing! * extension_module tests now not crashing! (some still skipped) * maybe travis has new pypy version? * small error on windows (build script) * fix conditional compilation * try to make tests run on travis.. * invert condition * added pytest-faulthandler to facilitate debugging * correctly name dir * use full paths * say —yes to conda * fix * syntax error * change PATH * fixed a terrible bug with PyTypeObjects in PyPy * fix PyTypeObject defs * re-enabled tests! * all tests are passing! * make the fix ad-hoc for now * removed build module * revert changes that cause an additional GC bug * prevented buggy test from failing pypy * removed unused comment * don’t run coverage on pypy * removed some erroneous symbols from function calls which are actually macros * restore py37 pyunicode missing def * use only `link_name` in PyPy specific declarations * only setup PyPy when testing against PyPy * annotation that was eaten during merge * remove change to comment by mistake + unnecessary changes to cargo.toml * xfail dates test only on pypy * changed comment to be a little more helpful * cleaned up some warnings * Update src/ffi3/ceval.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin PR notes * rustfmt * some documentation * if configured via env var only, default to cpython * remove extra unsafe * refer users to guide for pypy * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * Update guide/src/pypy.md Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * @konstin applied patch * check that pypy at least build * search explicitly for libpypy * added note about some known unsupported features * use ld_version * export PYTHON_SYS_EXECUTABLE to `cargo build` test * inverted if * always link pypy dynamically * remove unused imports * Apply @kngwyu’s suggestion * fix tox configuration * try conda virtualenv * try to simply not install python at all inside pypy environment * setup pypy before using “python" * use system_site_packages * revert change to .travis * moved cpyext datetime documentation to module level, and revised it. * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * rustfmt * Update src/ffi/datetime.rs Co-Authored-By: omerbenamram <omerbenamram@gmail.com> * kept only notes that are relevant to users. * invert if * use bash and not sh
2019-04-23 11:18:42 +00:00
}
2021-03-31 15:04:15 +00:00
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()
}
2021-04-24 09:52:58 +00:00
fn emit_cargo_configuration(interpreter_config: &InterpreterConfig) -> Result<()> {
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let is_extension_module = env::var_os("CARGO_FEATURE_EXTENSION_MODULE").is_some();
match (is_extension_module, target_os.as_str()) {
(_, "windows") => {
// always link on windows, even with extension module
2021-04-24 09:52:58 +00:00
println!("{}", get_rustc_link_lib(&interpreter_config)?);
// Set during cross-compiling.
if let Some(libdir) = &interpreter_config.libdir {
println!("cargo:rustc-link-search=native={}", libdir);
}
// Set if we have an interpreter to use.
if let Some(base_prefix) = &interpreter_config.base_prefix {
println!("cargo:rustc-link-search=native={}\\libs", base_prefix);
}
2017-05-13 05:05:00 +00:00
}
(true, "macos") => {
// with extension module on macos some extra linker arguments are needed
println!("cargo:rustc-cdylib-link-arg=-undefined");
println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
}
(false, _) | (_, "android") => {
// other systems, only link libs if not extension module
// android always link.
2021-04-24 09:52:58 +00:00
println!("{}", get_rustc_link_lib(&interpreter_config)?);
if let Some(libdir) = &interpreter_config.libdir {
println!("cargo:rustc-link-search=native={}", libdir);
}
2021-04-24 09:52:58 +00:00
if interpreter_config.implementation == PythonImplementation::PyPy {
// PyPy 7.3.4 changed LIBDIR to point to base_prefix/lib as a regression, so need
// to hard-code /bin search path too: https://foss.heptapod.net/pypy/pypy/-/issues/3442
//
// TODO: this workaround can probably be removed when PyPy 7.3.5 is released (and we
// can call it a PyPy bug).
if let Some(base_prefix) = &interpreter_config.base_prefix {
println!("cargo:rustc-link-search=native={}/bin", base_prefix);
}
2021-04-18 07:17:20 +00:00
}
}
_ => {}
2017-05-13 05:05:00 +00:00
}
if env::var_os("CARGO_FEATURE_AUTO_INITIALIZE").is_some() {
if !interpreter_config.shared {
return Err(format!(
"The `auto-initialize` feature is enabled, but your python installation only supports \
embedding the Python interpreter statically. If you are attempting to run tests, or a \
binary which is okay to link dynamically, install a Python distribution which ships \
with the Python shared library.\n\
\n\
Embedding the Python interpreter statically does not yet have first-class support in \
PyO3. If you are sure you intend to do this, disable the `auto-initialize` feature.\n\
\n\
For more information, see \
https://pyo3.rs/v{pyo3_version}/\
building_and_distribution.html#embedding-python-in-rust",
pyo3_version = env::var("CARGO_PKG_VERSION").unwrap()
)
.into());
}
// TODO: PYO3_CI env is a hack to workaround CI with PyPy, where the `dev-dependencies`
// currently cause `auto-initialize` to be enabled in CI.
// Once cargo's `resolver = "2"` is stable (~ MSRV Rust 1.52), remove this.
if interpreter_config.is_pypy() && env::var_os("PYO3_CI").is_none() {
return Err("The `auto-initialize` feature is not supported with PyPy.".into());
2020-10-31 07:35:14 +00:00
}
2017-06-11 23:35:24 +00:00
}
Ok(())
}
2021-04-24 09:52:58 +00:00
/// Generates the interpreter config suitable for the host / target / cross-compilation at hand.
///
/// The result is written to pyo3_build_config::PATH, which downstream scripts can read from
/// (including `pyo3-macros-backend` during macro expansion).
fn configure_pyo3() -> Result<()> {
let interpreter_config = pyo3_build_config::make_interpreter_config()?;
ensure_python_version(&interpreter_config)?;
ensure_target_architecture(&interpreter_config)?;
emit_cargo_configuration(&interpreter_config)?;
interpreter_config.to_writer(&mut std::fs::File::create(pyo3_build_config::PATH)?)?;
interpreter_config.emit_pyo3_cfgs();
2021-03-31 15:04:15 +00:00
// Enable use of const generics on Rust 1.51 and greater
2021-03-31 15:04:15 +00:00
if rustc_minor_version().unwrap_or(0) >= 51 {
println!("cargo:rustc-cfg=min_const_generics");
}
2020-12-30 23:45:03 +00:00
Ok(())
2017-06-11 23:35:24 +00:00
}
2017-05-13 05:05:00 +00:00
fn main() {
// Print out error messages using display, to get nicer formatting.
2021-04-24 09:52:58 +00:00
if let Err(e) = configure_pyo3() {
eprintln!("error: {}", e);
std::process::exit(1)
2021-04-24 09:52:58 +00:00
}
}