From 832c6b84ede7b7683b29cee5ef866df583be4974 Mon Sep 17 00:00:00 2001 From: messense Date: Fri, 6 Aug 2021 23:45:07 +0800 Subject: [PATCH 1/3] Use `[tool.maturin]` options from `pyproject.toml` in build command --- Readme.md | 2 +- src/build_options.rs | 48 ++++++++++++++++++++++++++++------ src/pyproject_toml.rs | 60 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 10 deletions(-) diff --git a/Readme.md b/Readme.md index a4d109a9d..4e006cbca 100644 --- a/Readme.md +++ b/Readme.md @@ -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 diff --git a/src/build_options.rs b/src/build_options.rs index efe62863f..34d6e35e6 100644 --- a/src/build_options.rs +++ b/src/build_options.rs @@ -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}; @@ -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 { - 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 = @@ -130,6 +128,12 @@ impl BuildOptions { let cargo_toml = CargoToml::from_path(&manifest_file)?; let manifest_dir = manifest_file.parent().unwrap(); + let pyproject: Option = 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(); @@ -158,7 +162,14 @@ impl BuildOptions { extra_metadata.python_source.as_deref(), )?; - let mut cargo_extra_args = split_extra_args(&self.cargo_extra_args)?; + 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()); + } + } + 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()]); } @@ -183,7 +194,12 @@ 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| x.bindings())), + )?; if bridge != BridgeModel::Bin && module_name.contains('-') { bail!( @@ -208,7 +224,14 @@ 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()); + } + } + rustc_extra_args = split_extra_args(&rustc_extra_args)?; let mut universal2 = self.universal2; // Also try to determine universal2 from ARCHFLAGS environment variable @@ -228,6 +251,15 @@ 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| x.compatibility())); + if platform_tag == Some(PlatformTag::manylinux1()) { + eprintln!("⚠ Warning: manylinux1 is unsupported by the Rust compiler."); + } Ok(BuildContext { target, @@ -240,8 +272,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, diff --git a/src/pyproject_toml.rs b/src/pyproject_toml.rs index 983387b85..caf363e71 100644 --- a/src/pyproject_toml.rs +++ b/src/pyproject_toml.rs @@ -1,3 +1,4 @@ +use crate::PlatformTag; use anyhow::{format_err, Context, Result}; use pyproject_toml::PyProjectToml as ProjectToml; use serde::{Deserialize, Serialize}; @@ -16,6 +17,15 @@ pub struct Tool { #[serde(rename_all = "kebab-case")] pub struct ToolMaturin { sdist_include: Option>, + bindings: Option, + cargo_extra_args: Option, + #[serde(alias = "manylinux")] + compatibility: Option, + rustc_extra_args: Option, + #[serde(default)] + skip_auditwheel: bool, + #[serde(default)] + strip: bool, } /// A pyproject.toml as specified in PEP 517 @@ -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> { 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 { + 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 From c8cb02906c959786c73e5cbd11f064fe7a4573f9 Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 11 Aug 2021 21:04:06 +0800 Subject: [PATCH 2/3] Inform user about using build options from pyproject.toml --- src/build_options.rs | 32 ++++++++++++++++++++++------ test-crates/pyo3-pure/pyproject.toml | 3 +++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/build_options.rs b/src/build_options.rs index 34d6e35e6..582421ea1 100644 --- a/src/build_options.rs +++ b/src/build_options.rs @@ -162,11 +162,13 @@ impl BuildOptions { extra_metadata.python_source.as_deref(), )?; + 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)?; @@ -196,9 +198,14 @@ impl BuildOptions { let bridge = find_bridge( &cargo_metadata, - self.bindings - .as_deref() - .or_else(|| pyproject.and_then(|x| x.bindings())), + 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('-') { @@ -229,6 +236,7 @@ impl BuildOptions { // 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)?; @@ -254,13 +262,25 @@ impl BuildOptions { 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| x.compatibility())); + 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, bridge, diff --git a/test-crates/pyo3-pure/pyproject.toml b/test-crates/pyo3-pure/pyproject.toml index 9b830af52..71aae6ecf 100644 --- a/test-crates/pyo3-pure/pyproject.toml +++ b/test-crates/pyo3-pure/pyproject.toml @@ -2,6 +2,9 @@ requires = ["maturin>=0.11,<0.12"] build-backend = "maturin" +[tool.maturin] +bindings = "pyo3" + [project] name = "pyo3-pure" classifiers = [ From 4d21a0adbf4280315ba69eb6ae5e159bf02e87aa Mon Sep 17 00:00:00 2001 From: messense Date: Tue, 10 Aug 2021 14:02:49 +0800 Subject: [PATCH 3/3] Remove `[tool.maturin]` options from Python PEP 517 side --- maturin/__init__.py | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/maturin/__init__.py b/maturin/__init__.py index dc2da449d..3d7a11834 100644 --- a/maturin/__init__.py +++ b/maturin/__init__.py @@ -14,25 +14,10 @@ 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: @@ -40,25 +25,11 @@ def get_config() -> Dict[str, str]: 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() @@ -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: