Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for PYO3_CONFIG_FILE #899

Merged
merged 4 commits into from
May 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ jobs:
sudo apt-get install -y mingw-w64
rustup target add x86_64-pc-windows-gnu
cargo run -- build --no-sdist -m test-crates/pyo3-pure/Cargo.toml --target x86_64-pc-windows-gnu
- name: test compiling with PYO3_CONFIG_FILE
shell: bash
run: |
rustup target add x86_64-unknown-linux-gnu
export PYO3_CONFIG_FILE=$(pwd)/test-crates/pyo3-mixed/pyo3-config.txt
cargo run -- build --no-sdist -m test-crates/pyo3-mixed/Cargo.toml --target x86_64-unknown-linux-gnu --zig

test-alpine:
name: Test Alpine Linux
Expand Down
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Re-export `__all__` for pure Rust projects in [#886](https://github.com/PyO3/maturin/pull/886)
* Stop setting `RUSTFLAGS` environment variable to an empty string in [#887](https://github.com/PyO3/maturin/pull/887)
* Add hardcoded well-known sysconfigs for effortless cross compiling in [#896](https://github.com/PyO3/maturin/pull/896)
* Add support for `PYO3_CONFIG_FILE` in [#899](https://github.com/PyO3/maturin/pull/899)

## [0.12.14] - 2022-04-25

Expand Down
13 changes: 12 additions & 1 deletion src/build_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,18 @@ pub fn find_interpreter(
match bridge {
BridgeModel::Bindings(binding_name, _) => {
let mut interpreters = Vec::new();
if binding_name.starts_with("pyo3") && target.is_unix() && target.cross_compiling() {
if let Some(config_file) = env::var_os("PYO3_CONFIG_FILE") {
if !binding_name.starts_with("pyo3") {
bail!("Only pyo3 bindings can be configured with PYO3_CONFIG_FILE");
}
let interpreter_config =
InterpreterConfig::from_pyo3_config(config_file.as_ref(), target)
.context("Invalid PYO3_CONFIG_FILE")?;
interpreters.push(PythonInterpreter::from_config(interpreter_config));
} else if binding_name.starts_with("pyo3")
&& target.is_unix()
&& target.cross_compiling()
{
if let Some(cross_lib_dir) = std::env::var_os("PYO3_CROSS_LIB_DIR") {
let host_interpreters =
find_host_interpreter(bridge, interpreter, target, min_python_minor)?;
Expand Down
122 changes: 122 additions & 0 deletions src/python_interpreter/config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use super::InterpreterKind;
use crate::target::{Arch, Os};
use crate::Target;
use anyhow::{format_err, Context, Result};
use fs_err as fs;
use once_cell::sync::Lazy;
use serde::Deserialize;
use std::collections::HashMap;
use std::io::{BufRead, BufReader};
use std::path::Path;

/// Wellknown Python interpreter sysconfig values
static WELLKNOWN_SYSCONFIG: Lazy<HashMap<Os, HashMap<Arch, Vec<InterpreterConfig>>>> =
Expand Down Expand Up @@ -62,6 +67,123 @@ impl InterpreterConfig {
None
}

/// Construct a new InterpreterConfig from a pyo3 config file
pub fn from_pyo3_config(config_file: &Path, target: &Target) -> Result<Self> {
let config_file = fs::File::open(config_file)?;
let reader = BufReader::new(config_file);
let lines = reader.lines();

macro_rules! parse_value {
($variable:ident, $value:ident) => {
$variable = Some($value.trim().parse().context(format!(
concat!(
"failed to parse ",
stringify!($variable),
" from config value '{}'"
),
$value
))?)
};
}

let mut implementation = None;
let mut version = None;
let mut abiflags = None;
let mut ext_suffix = None;
let mut abi_tag = None;
let mut pointer_width = None;

for (i, line) in lines.enumerate() {
let line = line.context("failed to read line from config")?;
let (key, value) = line
.split_once('=')
.with_context(|| format!("expected key=value pair on line {}", i + 1))?;
match key {
"implementation" => parse_value!(implementation, value),
"version" => parse_value!(version, value),
"abiflags" => parse_value!(abiflags, value),
"ext_suffix" => parse_value!(ext_suffix, value),
"abi_tag" => parse_value!(abi_tag, value),
"pointer_width" => parse_value!(pointer_width, value),
_ => continue,
}
}
let version: String = version.context("missing value for version")?;
let (ver_major, ver_minor) = version
.split_once('.')
.context("Invalid python interpreter version")?;
let major = ver_major.parse::<usize>().with_context(|| {
format!(
"Invalid python interpreter major version '{}', expect a digit",
ver_major
)
})?;
let minor = ver_minor.parse::<usize>().with_context(|| {
format!(
"Invalid python interpreter minor version '{}', expect a digit",
ver_minor
)
})?;
let implementation = implementation.unwrap_or_else(|| "cpython".to_string());
let interpreter_kind = implementation.parse().map_err(|e| format_err!("{}", e))?;
let abi_tag = match interpreter_kind {
InterpreterKind::CPython => {
if (major, minor) >= (3, 8) {
abi_tag.unwrap_or_else(|| format!("{}{}", major, minor))
} else {
abi_tag.unwrap_or_else(|| format!("{}{}m", major, minor))
}
}
InterpreterKind::PyPy => abi_tag.unwrap_or_else(|| "pp73".to_string()),
};
let file_ext = if target.is_windows() { "pyd" } else { "so" };
let ext_suffix = if target.is_linux() || target.is_macos() {
// See https://github.com/pypa/auditwheel/issues/349
let target_env = if (major, minor) >= (3, 11) {
target.target_env().to_string()
} else {
"gnu".to_string()
};
match interpreter_kind {
InterpreterKind::CPython => ext_suffix.unwrap_or_else(|| {
// Eg: .cpython-38-x86_64-linux-gnu.so
format!(
".cpython-{}-{}-{}-{}.{}",
abi_tag,
target.get_python_arch(),
target.get_python_os(),
target_env,
file_ext,
)
}),
InterpreterKind::PyPy => ext_suffix.unwrap_or_else(|| {
// Eg: .pypy38-pp73-x86_64-linux-gnu.so
format!(
".pypy{}{}-{}-{}-{}-{}.{}",
major,
minor,
abi_tag,
target.get_python_arch(),
target.get_python_os(),
target_env,
file_ext,
)
}),
}
} else {
ext_suffix.context("missing value for ext_suffix")?
};
Ok(Self {
major,
minor,
interpreter_kind,
abiflags: abiflags.unwrap_or_default(),
ext_suffix,
abi_tag: Some(abi_tag),
pointer_width,
})
}

/// Generate pyo3 config file content
pub fn pyo3_config_file(&self) -> String {
let mut content = format!(
Expand Down
14 changes: 13 additions & 1 deletion src/python_interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::io;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::str;
use std::str::{self, FromStr};

mod config;

Expand Down Expand Up @@ -248,6 +248,18 @@ impl fmt::Display for InterpreterKind {
}
}

impl FromStr for InterpreterKind {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_ascii_lowercase().as_str() {
"cpython" => Ok(InterpreterKind::CPython),
"pypy" => Ok(InterpreterKind::PyPy),
unknown => Err(format!("Unknown interpreter kind '{}'", unknown)),
}
}
}

/// The output format of [GET_INTERPRETER_METADATA]
#[derive(Deserialize)]
struct IntepreterMetadataMessage {
Expand Down
18 changes: 18 additions & 0 deletions src/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,19 @@ impl Target {
Ok(tag)
}

/// Returns the name python uses in `sys.platform` for this architecture.
pub fn get_python_arch(&self) -> &str {
match self.arch {
Arch::Aarch64 => "aarch64",
Arch::Armv7L => "armv7l",
Arch::Powerpc64Le => "powerpc64le",
Arch::Powerpc64 => "powerpc64",
Arch::X86 => "i386",
Arch::X86_64 => "x86_64",
Arch::S390X => "s390x",
}
}

/// Returns the name python uses in `sys.platform` for this os
pub fn get_python_os(&self) -> &str {
match self.os {
Expand Down Expand Up @@ -364,6 +377,11 @@ impl Target {
self.arch
}

/// Returns target environment
pub fn target_env(&self) -> Environment {
self.env
}

/// Returns true if the current platform is linux
pub fn is_linux(&self) -> bool {
self.os == Os::Linux
Expand Down
6 changes: 6 additions & 0 deletions test-crates/pyo3-mixed/pyo3-config.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
implementation=CPython
version=3.10
shared=true
abi3=false
suppress_build_script_link_lines=false
pointer_width=64