Skip to content

Commit

Permalink
Merge pull request #605 from messense/tool-maturin
Browse files Browse the repository at this point in the history
Use `[tool.maturin]` options from `pyproject.toml` in build command
  • Loading branch information
messense authored Aug 13, 2021
2 parents a057f4a + 4d21a0a commit 9ab2099
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 41 deletions.
2 changes: 1 addition & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ To include arbitrary files in the sdist for use during compilation specify `sdis
sdist-include = ["path/**/*"]
```

There's a `cargo sdist` command for only building a source distribution as workaround for [pypa/pip#6041](https://github.com/pypa/pip/issues/6041).
There's a `maturin sdist` command for only building a source distribution as workaround for [pypa/pip#6041](https://github.com/pypa/pip/issues/6041).

## Manylinux and auditwheel

Expand Down
32 changes: 1 addition & 31 deletions maturin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,51 +14,22 @@
import subprocess
import sys
from subprocess import SubprocessError
from typing import List, Dict
from typing import Dict

import toml

# these are only used when creating the sdist, not when building it
create_only_options = [
"sdist-include",
]

available_options = [
"bindings",
"cargo-extra-args",
"compatibility",
"manylinux",
"rustc-extra-args",
"skip-auditwheel",
"strip",
]


def get_config() -> Dict[str, str]:
with open("pyproject.toml", encoding="utf-8") as fp:
pyproject_toml = toml.load(fp)
return pyproject_toml.get("tool", {}).get("maturin", {})


def get_config_options() -> List[str]:
config = get_config()
options = []
for key, value in config.items():
if key in create_only_options:
continue
if key not in available_options:
# attempt to install even if keys from newer or older versions are present
sys.stderr.write(f"WARNING: {key} is not a recognized option for maturin\n")
options.append("--{}={}".format(key, value))
return options


# noinspection PyUnusedLocal
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
# PEP 517 specifies that only `sys.executable` points to the correct
# python interpreter
command = ["maturin", "pep517", "build-wheel", "-i", sys.executable]
command.extend(get_config_options())

print("Running `{}`".format(" ".join(command)))
sys.stdout.flush()
Expand Down Expand Up @@ -140,7 +111,6 @@ def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
"--interpreter",
sys.executable,
]
command.extend(get_config_options())

print("Running `{}`".format(" ".join(command)))
try:
Expand Down
68 changes: 60 additions & 8 deletions src/build_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::python_interpreter::InterpreterKind;
use crate::BuildContext;
use crate::CargoToml;
use crate::Metadata21;
use crate::PyProjectToml;
use crate::PythonInterpreter;
use crate::Target;
use anyhow::{bail, format_err, Context, Result};
Expand Down Expand Up @@ -106,9 +107,6 @@ impl Default for BuildOptions {
impl BuildOptions {
/// Tries to fill the missing metadata for a BuildContext by querying cargo and python
pub fn into_build_context(self, release: bool, strip: bool) -> Result<BuildContext> {
if self.platform_tag == Some(PlatformTag::manylinux1()) {
eprintln!("⚠ Warning: manylinux1 is unsupported by the Rust compiler.");
}
let manifest_file = &self.manifest_path;
if !manifest_file.exists() {
let current_dir =
Expand All @@ -130,6 +128,12 @@ impl BuildOptions {

let cargo_toml = CargoToml::from_path(&manifest_file)?;
let manifest_dir = manifest_file.parent().unwrap();
let pyproject: Option<PyProjectToml> = if manifest_dir.join("pyproject.toml").is_file() {
Some(PyProjectToml::new(manifest_dir).context("pyproject.toml is invalid")?)
} else {
None
};
let pyproject = pyproject.as_ref();
let metadata21 = Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir)
.context("Failed to parse Cargo.toml into python metadata")?;
let extra_metadata = cargo_toml.remaining_core_metadata();
Expand Down Expand Up @@ -158,7 +162,16 @@ impl BuildOptions {
extra_metadata.python_source.as_deref(),
)?;

let mut cargo_extra_args = split_extra_args(&self.cargo_extra_args)?;
let mut args_from_pyproject = Vec::new();
let mut cargo_extra_args = self.cargo_extra_args.clone();
if cargo_extra_args.is_empty() {
// if not supplied on command line, try pyproject.toml
if let Some(args) = pyproject.and_then(|x| x.cargo_extra_args()) {
cargo_extra_args.push(args.to_string());
args_from_pyproject.push("cargo-extra-args");
}
}
cargo_extra_args = split_extra_args(&cargo_extra_args)?;
if let Some(ref target) = self.target {
cargo_extra_args.extend(vec!["--target".to_string(), target.clone()]);
}
Expand All @@ -183,7 +196,17 @@ impl BuildOptions {
}
};

let bridge = find_bridge(&cargo_metadata, self.bindings.as_deref())?;
let bridge = find_bridge(
&cargo_metadata,
self.bindings.as_deref().or_else(|| {
pyproject.and_then(|x| {
if x.bindings().is_some() {
args_from_pyproject.push("bindings");
}
x.bindings()
})
}),
)?;

if bridge != BridgeModel::Bin && module_name.contains('-') {
bail!(
Expand All @@ -208,7 +231,15 @@ impl BuildOptions {
None => find_interpreter(&bridge, &[], &target, get_min_python_minor(&metadata21))?,
};

let rustc_extra_args = split_extra_args(&self.rustc_extra_args)?;
let mut rustc_extra_args = self.rustc_extra_args.clone();
if rustc_extra_args.is_empty() {
// if not supplied on command line, try pyproject.toml
if let Some(args) = pyproject.and_then(|x| x.rustc_extra_args()) {
rustc_extra_args.push(args.to_string());
args_from_pyproject.push("rustc-extra-args");
}
}
rustc_extra_args = split_extra_args(&rustc_extra_args)?;

let mut universal2 = self.universal2;
// Also try to determine universal2 from ARCHFLAGS environment variable
Expand All @@ -228,6 +259,27 @@ impl BuildOptions {
universal2 = true;
}
};
let strip = pyproject.map(|x| x.strip()).unwrap_or_default() || strip;
let skip_auditwheel =
pyproject.map(|x| x.skip_auditwheel()).unwrap_or_default() || self.skip_auditwheel;
let platform_tag = self.platform_tag.or_else(|| {
pyproject.and_then(|x| {
if x.compatibility().is_some() {
args_from_pyproject.push("compatibility");
}
x.compatibility()
})
});
if platform_tag == Some(PlatformTag::manylinux1()) {
eprintln!("⚠ Warning: manylinux1 is unsupported by the Rust compiler.");
}

if !args_from_pyproject.is_empty() {
eprintln!(
"📡 Using build options {} from pyproject.toml",
args_from_pyproject.join(", ")
);
}

Ok(BuildContext {
target,
Expand All @@ -240,8 +292,8 @@ impl BuildOptions {
out: wheel_dir,
release,
strip,
skip_auditwheel: self.skip_auditwheel,
platform_tag: self.platform_tag,
skip_auditwheel,
platform_tag,
cargo_extra_args,
rustc_extra_args,
interpreter,
Expand Down
60 changes: 59 additions & 1 deletion src/pyproject_toml.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::PlatformTag;
use anyhow::{format_err, Context, Result};
use pyproject_toml::PyProjectToml as ProjectToml;
use serde::{Deserialize, Serialize};
Expand All @@ -16,6 +17,15 @@ pub struct Tool {
#[serde(rename_all = "kebab-case")]
pub struct ToolMaturin {
sdist_include: Option<Vec<String>>,
bindings: Option<String>,
cargo_extra_args: Option<String>,
#[serde(alias = "manylinux")]
compatibility: Option<PlatformTag>,
rustc_extra_args: Option<String>,
#[serde(default)]
skip_auditwheel: bool,
#[serde(default)]
strip: bool,
}

/// A pyproject.toml as specified in PEP 517
Expand Down Expand Up @@ -57,11 +67,59 @@ impl PyProjectToml {
Ok(pyproject)
}

/// Returns the value of `[maturin.sdist-include]` in pyproject.toml
/// Returns the value of `[tool.maturin.sdist-include]` in pyproject.toml
pub fn sdist_include(&self) -> Option<&Vec<String>> {
self.tool.as_ref()?.maturin.as_ref()?.sdist_include.as_ref()
}

/// Returns the value of `[tool.maturin.bindings]` in pyproject.toml
pub fn bindings(&self) -> Option<&str> {
self.tool.as_ref()?.maturin.as_ref()?.bindings.as_deref()
}

/// Returns the value of `[tool.maturin.cargo-extra-args]` in pyproject.toml
pub fn cargo_extra_args(&self) -> Option<&str> {
self.tool
.as_ref()?
.maturin
.as_ref()?
.cargo_extra_args
.as_deref()
}

/// Returns the value of `[tool.maturin.compatibility]` in pyproject.toml
pub fn compatibility(&self) -> Option<PlatformTag> {
self.tool.as_ref()?.maturin.as_ref()?.compatibility
}

/// Returns the value of `[tool.maturin.rustc-extra-args]` in pyproject.toml
pub fn rustc_extra_args(&self) -> Option<&str> {
self.tool
.as_ref()?
.maturin
.as_ref()?
.rustc_extra_args
.as_deref()
}

/// Returns the value of `[tool.maturin.skip-auditwheel]` in pyproject.toml
pub fn skip_auditwheel(&self) -> bool {
self.tool
.as_ref()
.and_then(|tool| tool.maturin.as_ref())
.map(|maturin| maturin.skip_auditwheel)
.unwrap_or_default()
}

/// Returns the value of `[tool.maturin.strip]` in pyproject.toml
pub fn strip(&self) -> bool {
self.tool
.as_ref()
.and_then(|tool| tool.maturin.as_ref())
.map(|maturin| maturin.strip)
.unwrap_or_default()
}

/// Having a pyproject.toml without a version constraint is a bad idea
/// because at some point we'll have to do breaking changes and then source
/// distributions would break
Expand Down
3 changes: 3 additions & 0 deletions test-crates/pyo3-pure/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
requires = ["maturin>=0.11,<0.12"]
build-backend = "maturin"

[tool.maturin]
bindings = "pyo3"

[project]
name = "pyo3-pure"
classifiers = [
Expand Down

0 comments on commit 9ab2099

Please sign in to comment.