-
Notifications
You must be signed in to change notification settings - Fork 223
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is pyo3 at 57735540e85e496b448a2fc438e6343b6533ae7a. To maintain compatibility when _Py APIs are removed from pyo3-ffi.
- Loading branch information
Showing
94 changed files
with
13,134 additions
and
8 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
[package] | ||
name = "pyo3-build-config" | ||
version = "0.21.0-dev" | ||
description = "Build configuration for the PyO3 ecosystem" | ||
authors = ["PyO3 Project and Contributors <https://github.com/PyO3>"] | ||
keywords = ["pyo3", "python", "cpython", "ffi"] | ||
homepage = "https://github.com/pyo3/pyo3" | ||
repository = "https://github.com/pyo3/pyo3" | ||
categories = ["api-bindings", "development-tools::ffi"] | ||
license = "MIT OR Apache-2.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
once_cell = "1" | ||
target-lexicon = "^0.12" | ||
|
||
[build-dependencies] | ||
target-lexicon = "^0.12" | ||
|
||
[features] | ||
default = [] | ||
|
||
# Attempt to resolve a Python interpreter config for building in the build | ||
# script. If this feature isn't enabled, the build script no-ops. | ||
resolve-config = [] | ||
|
||
# This feature is enabled by pyo3 when building an extension module. | ||
extension-module = [] | ||
|
||
# These features are enabled by pyo3 when building Stable ABI extension modules. | ||
abi3 = [] | ||
abi3-py37 = ["abi3-py38"] | ||
abi3-py38 = ["abi3-py39"] | ||
abi3-py39 = ["abi3-py310"] | ||
abi3-py310 = ["abi3-py311"] | ||
abi3-py311 = ["abi3-py312"] | ||
abi3-py312 = ["abi3"] | ||
|
||
[package.metadata.docs.rs] | ||
features = ["resolve-config"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Import some modules from this crate inline to generate the build config. | ||
// Allow dead code because not all code in the modules is used in this build script. | ||
|
||
#[path = "src/impl_.rs"] | ||
#[allow(dead_code)] | ||
mod impl_; | ||
|
||
#[path = "src/errors.rs"] | ||
#[allow(dead_code)] | ||
mod errors; | ||
|
||
use std::{env, path::Path}; | ||
|
||
use errors::{Context, Result}; | ||
use impl_::{env_var, make_interpreter_config, InterpreterConfig}; | ||
|
||
fn configure(interpreter_config: Option<InterpreterConfig>, name: &str) -> Result<bool> { | ||
let target = Path::new(&env::var_os("OUT_DIR").unwrap()).join(name); | ||
if let Some(config) = interpreter_config { | ||
config | ||
.to_writer(&mut std::fs::File::create(&target).with_context(|| { | ||
format!("failed to write config file at {}", target.display()) | ||
})?)?; | ||
Ok(true) | ||
} else { | ||
std::fs::File::create(&target) | ||
.with_context(|| format!("failed to create new file at {}", target.display()))?; | ||
Ok(false) | ||
} | ||
} | ||
|
||
/// If PYO3_CONFIG_FILE is set, copy it into the crate. | ||
fn config_file() -> Result<Option<InterpreterConfig>> { | ||
if let Some(path) = env_var("PYO3_CONFIG_FILE") { | ||
let path = Path::new(&path); | ||
println!("cargo:rerun-if-changed={}", path.display()); | ||
// Absolute path is necessary because this build script is run with a cwd different to the | ||
// original `cargo build` instruction. | ||
ensure!( | ||
path.is_absolute(), | ||
"PYO3_CONFIG_FILE must be an absolute path" | ||
); | ||
|
||
let interpreter_config = InterpreterConfig::from_path(path) | ||
.context("failed to parse contents of PYO3_CONFIG_FILE")?; | ||
Ok(Some(interpreter_config)) | ||
} else { | ||
Ok(None) | ||
} | ||
} | ||
|
||
fn generate_build_configs() -> Result<()> { | ||
let configured = configure(config_file()?, "pyo3-build-config-file.txt")?; | ||
|
||
if configured { | ||
// Don't bother trying to find an interpreter on the host system | ||
// if the user-provided config file is present. | ||
configure(None, "pyo3-build-config.txt")?; | ||
} else { | ||
configure(Some(make_interpreter_config()?), "pyo3-build-config.txt")?; | ||
} | ||
Ok(()) | ||
} | ||
|
||
fn main() { | ||
if std::env::var("CARGO_FEATURE_RESOLVE_CONFIG").is_ok() { | ||
if let Err(e) = generate_build_configs() { | ||
eprintln!("error: {}", e.report()); | ||
std::process::exit(1) | ||
} | ||
} else { | ||
eprintln!("resolve-config feature not enabled; build script in no-op mode"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/// A simple macro for returning an error. Resembles anyhow::bail. | ||
#[macro_export] | ||
#[doc(hidden)] | ||
macro_rules! bail { | ||
($($args: tt)+) => { return Err(format!($($args)+).into()) }; | ||
} | ||
|
||
/// A simple macro for checking a condition. Resembles anyhow::ensure. | ||
#[macro_export] | ||
#[doc(hidden)] | ||
macro_rules! ensure { | ||
($condition:expr, $($args: tt)+) => { if !($condition) { bail!($($args)+) } }; | ||
} | ||
|
||
/// Show warning. | ||
#[macro_export] | ||
#[doc(hidden)] | ||
macro_rules! warn { | ||
($($args: tt)+) => { | ||
println!("{}", $crate::format_warn!($($args)+)) | ||
}; | ||
} | ||
|
||
/// Format warning into string. | ||
#[macro_export] | ||
#[doc(hidden)] | ||
macro_rules! format_warn { | ||
($($args: tt)+) => { | ||
format!("cargo:warning={}", format_args!($($args)+)) | ||
}; | ||
} | ||
|
||
/// A simple error implementation which allows chaining of errors, inspired somewhat by anyhow. | ||
#[derive(Debug)] | ||
pub struct Error { | ||
value: String, | ||
source: Option<Box<dyn std::error::Error>>, | ||
} | ||
|
||
/// Error report inspired by | ||
/// <https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html#2-error-reporter> | ||
pub struct ErrorReport<'a>(&'a Error); | ||
|
||
impl Error { | ||
pub fn report(&self) -> ErrorReport<'_> { | ||
ErrorReport(self) | ||
} | ||
} | ||
|
||
impl std::fmt::Display for Error { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "{}", self.value) | ||
} | ||
} | ||
|
||
impl std::error::Error for Error { | ||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||
self.source.as_deref() | ||
} | ||
} | ||
|
||
impl std::fmt::Display for ErrorReport<'_> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
use std::error::Error; | ||
self.0.fmt(f)?; | ||
let mut source = self.0.source(); | ||
if source.is_some() { | ||
writeln!(f, "\ncaused by:")?; | ||
let mut index = 0; | ||
while let Some(some_source) = source { | ||
writeln!(f, " - {}: {}", index, some_source)?; | ||
source = some_source.source(); | ||
index += 1; | ||
} | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl From<String> for Error { | ||
fn from(value: String) -> Self { | ||
Self { | ||
value, | ||
source: None, | ||
} | ||
} | ||
} | ||
|
||
impl From<&'_ str> for Error { | ||
fn from(value: &str) -> Self { | ||
value.to_string().into() | ||
} | ||
} | ||
|
||
impl From<std::convert::Infallible> for Error { | ||
fn from(value: std::convert::Infallible) -> Self { | ||
match value {} | ||
} | ||
} | ||
|
||
pub type Result<T, E = Error> = std::result::Result<T, E>; | ||
|
||
pub trait Context<T> { | ||
fn context(self, message: impl Into<String>) -> Result<T>; | ||
fn with_context(self, message: impl FnOnce() -> String) -> Result<T>; | ||
} | ||
|
||
impl<T, E> Context<T> for Result<T, E> | ||
where | ||
E: std::error::Error + 'static, | ||
{ | ||
fn context(self, message: impl Into<String>) -> Result<T> { | ||
self.map_err(|error| Error { | ||
value: message.into(), | ||
source: Some(Box::new(error)), | ||
}) | ||
} | ||
|
||
fn with_context(self, message: impl FnOnce() -> String) -> Result<T> { | ||
self.map_err(|error| Error { | ||
value: message(), | ||
source: Some(Box::new(error)), | ||
}) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn error_report() { | ||
let error: Result<()> = Err(Error::from("there was an internal error")) | ||
.with_context(|| format!("failed to do {}", "something difficult")) | ||
.context("things went wrong"); | ||
|
||
assert_eq!( | ||
error | ||
.unwrap_err() | ||
.report() | ||
.to_string() | ||
.split('\n') | ||
.collect::<Vec<&str>>(), | ||
vec![ | ||
"things went wrong", | ||
"caused by:", | ||
" - 0: failed to do something difficult", | ||
" - 1: there was an internal error", | ||
"" | ||
] | ||
); | ||
} | ||
} |
Oops, something went wrong.