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 workspace metadata inheritance #1131

Merged
merged 2 commits into from
Sep 25, 2022
Merged
Changes from 1 commit
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
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Fix `maturin develop` on Windows when using Python installed from msys2 in [#1112](https://github.com/PyO3/maturin/pull/1112)
* Fix duplicated `Cargo.toml` of local dependencies in sdist in [#1114](https://github.com/PyO3/maturin/pull/1114)
* Add support for Cargo workspace dependencies inheritance in [#1123](https://github.com/PyO3/maturin/pull/1123)
* Add support for Cargo workspace metadata inheritance in [#1131](https://github.com/PyO3/maturin/pull/1131)

## [0.13.3] - 2022-09-15

13 changes: 9 additions & 4 deletions src/build_options.rs
Original file line number Diff line number Diff line change
@@ -488,7 +488,6 @@ impl BuildOptions {
let ProjectResolver {
project_layout,
cargo_toml_path,
cargo_toml,
pyproject_toml_path,
pyproject_toml,
module_name,
@@ -685,7 +684,7 @@ impl BuildOptions {
.target_dir
.clone()
.unwrap_or_else(|| cargo_metadata.target_directory.clone().into_std_path_buf());
let crate_name = cargo_toml.package.name;
let crate_name = metadata21.name.clone();

Ok(BuildContext {
target,
@@ -1339,9 +1338,15 @@ mod test {
use crate::CargoToml;

// Nothing specified
let cargo_toml = CargoToml::from_path("test-crates/pyo3-pure/Cargo.toml").unwrap();
let manifest_path = "test-crates/pyo3-pure/Cargo.toml";
let cargo_toml = CargoToml::from_path(manifest_path).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_path)
.exec()
.unwrap();
let metadata21 =
Metadata21::from_cargo_toml(&cargo_toml, &"test-crates/pyo3-pure").unwrap();
Metadata21::from_cargo_toml(&cargo_toml, &"test-crates/pyo3-pure", &cargo_metadata)
.unwrap();
assert_eq!(get_min_python_minor(&metadata21), None);
}
}
14 changes: 0 additions & 14 deletions src/cargo_toml.rs
Original file line number Diff line number Diff line change
@@ -16,20 +16,6 @@ pub(crate) struct CargoTomlLib {
#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub(crate) struct CargoTomlPackage {
// Those three fields are mandatory
// https://doc.rust-lang.org/cargo/reference/manifest.html#the-package-section
pub(crate) name: String,
pub(crate) version: String,
// All other fields are optional
pub(crate) authors: Option<Vec<String>>,
pub(crate) description: Option<String>,
pub(crate) documentation: Option<String>,
pub(crate) homepage: Option<String>,
pub(crate) repository: Option<String>,
pub(crate) readme: Option<String>,
pub(crate) keywords: Option<Vec<String>>,
pub(crate) categories: Option<Vec<String>>,
pub(crate) license: Option<String>,
metadata: Option<CargoTomlMetadata>,
}

157 changes: 99 additions & 58 deletions src/metadata.rs
Original file line number Diff line number Diff line change
@@ -283,32 +283,30 @@ impl Metadata21 {
pub fn from_cargo_toml(
cargo_toml: &CargoToml,
manifest_path: impl AsRef<Path>,
cargo_metadata: &cargo_metadata::Metadata,
) -> Result<Metadata21> {
let authors = cargo_toml
.package
.authors
.as_ref()
.map(|authors| authors.join(", "));
let package = cargo_metadata
.root_package()
.context("Expected cargo to return metadata with root_package")?;
let authors = package.authors.join(", ");

let classifiers = cargo_toml.classifiers();

let author_email = authors.as_ref().and_then(|authors| {
if authors.contains('@') {
Some(authors.clone())
} else {
None
}
});
let author_email = if authors.contains('@') {
Some(authors.clone())
} else {
None
};

let extra_metadata = cargo_toml.remaining_core_metadata();

let mut description: Option<String> = None;
let mut description_content_type: Option<String> = None;
// See https://packaging.python.org/specifications/core-metadata/#description
// and https://doc.rust-lang.org/cargo/reference/manifest.html#the-readme-field
if cargo_toml.package.readme == Some("false".to_string()) {
if package.readme == Some("false".into()) {
// > You can suppress this behavior by setting this field to false
} else if let Some(ref readme) = cargo_toml.package.readme {
} else if let Some(ref readme) = package.readme {
let readme_path = manifest_path.as_ref().join(readme);
description = Some(fs::read_to_string(&readme_path).context(format!(
"Failed to read Readme specified in Cargo.toml, which should be at {}",
@@ -345,9 +343,9 @@ impl Metadata21 {
name.clone()
}
})
.unwrap_or_else(|| cargo_toml.package.name.clone());
.unwrap_or_else(|| package.name.clone());
let mut project_url = extra_metadata.project_url.unwrap_or_default();
if let Some(repository) = cargo_toml.package.repository.as_ref() {
if let Some(repository) = package.repository.as_ref() {
project_url.insert("Source Code".to_string(), repository.clone());
}

@@ -356,21 +354,25 @@ impl Metadata21 {

// Mapped from cargo metadata
name,
version: cargo_toml.package.version.clone(),
summary: cargo_toml.package.description.clone(),
version: package.version.to_string(),
summary: package.description.clone(),
description,
description_content_type,
keywords: cargo_toml
.package
.keywords
.clone()
.map(|keywords| keywords.join(",")),
home_page: cargo_toml.package.homepage.clone(),
keywords: if package.keywords.is_empty() {
None
} else {
Some(package.keywords.join(","))
},
home_page: package.homepage.clone(),
download_url: None,
// Cargo.toml has no distinction between author and author email
author: authors,
author: if package.authors.is_empty() {
None
} else {
Some(authors)
},
author_email,
license: cargo_toml.package.license.clone(),
license: package.license.clone(),
license_files: Vec::new(),

// Values provided through `[project.metadata.maturin]`
@@ -554,28 +556,41 @@ fn fold_header(text: &str) -> String {
#[cfg(test)]
mod test {
use super::*;
use cargo_metadata::MetadataCommand;
use indoc::indoc;
use pretty_assertions::assert_eq;
use std::io::Write;

fn assert_metadata_from_cargo_toml(readme: &str, cargo_toml: &str, expected: &str) {
let mut readme_md = tempfile::NamedTempFile::new().unwrap();
fn assert_metadata_from_cargo_toml(
readme: &str,
cargo_toml: &str,
expected: &str,
) -> Metadata21 {
let crate_dir = tempfile::tempdir().unwrap();
let crate_path = crate_dir.path();
let manifest_path = crate_path.join("Cargo.toml");
fs::create_dir(crate_path.join("src")).unwrap();
fs::write(crate_path.join("src/lib.rs"), "").unwrap();

let readme_path = crate_path.join("README.md");
fs::write(&readme_path, readme.as_bytes()).unwrap();

let readme_path = if cfg!(windows) {
readme_md.path().to_str().unwrap().replace('\\', "/")
readme_path.to_str().unwrap().replace('\\', "/")
} else {
readme_md.path().to_str().unwrap().to_string()
readme_path.to_str().unwrap().to_string()
};

readme_md.write_all(readme.as_bytes()).unwrap();

let toml_with_path = cargo_toml.replace("REPLACE_README_PATH", &readme_path);
fs::write(&manifest_path, &toml_with_path).unwrap();

let cargo_toml_struct: CargoToml = toml_edit::easy::from_str(&toml_with_path).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_path)
.exec()
.unwrap();

let metadata =
Metadata21::from_cargo_toml(&cargo_toml_struct, &readme_md.path().parent().unwrap())
.unwrap();
Metadata21::from_cargo_toml(&cargo_toml_struct, crate_path, &cargo_metadata).unwrap();

let actual = metadata.to_file_contents().unwrap();

@@ -593,11 +608,15 @@ mod test {
&& cargo_toml.contains("version = \"0.1.0\""),
"cargo_toml name and version string do not match hardcoded values, test will fail",
);
assert_eq!(
metadata.get_dist_info_dir(),
PathBuf::from("info_project-0.1.0.dist-info"),
"Dist info dir differed from expected"
);

if cargo_toml_struct.remaining_core_metadata().name.is_none() {
assert_eq!(
metadata.get_dist_info_dir(),
PathBuf::from("info_project-0.1.0.dist-info"),
"Dist info dir differed from expected"
);
}
metadata
}

#[test]
@@ -648,7 +667,7 @@ mod test {
Home-Page: https://example.org
Author: konstin <[email protected]>
Author-email: konstin <[email protected]>
Description-Content-Type: text/plain; charset=UTF-8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Bug Tracker, http://bitbucket.org/tarek/distribute/issues/

# Some test package
@@ -721,6 +740,13 @@ mod test {

#[test]
fn test_metadata_from_cargo_toml_name_override() {
let readme = indoc!(
r#"
Some test package
=================
"#
);

let cargo_toml = indoc!(
r#"
[package]
@@ -729,6 +755,7 @@ mod test {
version = "0.1.0"
description = "A test project"
homepage = "https://example.org"
readme = "REPLACE_README_PATH"

[lib]
crate-type = ["cdylib"]
@@ -754,21 +781,14 @@ mod test {
Home-Page: https://example.org
Author: konstin <[email protected]>
Author-email: konstin <[email protected]>
Description-Content-Type: text/x-rst

Some test package
=================
"#
);

let cargo_toml_struct: CargoToml = toml_edit::easy::from_str(cargo_toml).unwrap();
let metadata =
Metadata21::from_cargo_toml(&cargo_toml_struct, "/not/exist/manifest/path").unwrap();
let actual = metadata.to_file_contents().unwrap();

assert_eq!(
actual.trim(),
expected.trim(),
"Actual metadata differed from expected\nEXPECTED:\n{}\n\nGOT:\n{}",
expected,
actual
);
let metadata = assert_metadata_from_cargo_toml(readme, cargo_toml, expected);

assert_eq!(
metadata.get_dist_info_dir(),
@@ -804,7 +824,12 @@ mod test {
let manifest_dir = PathBuf::from("test-crates").join("pyo3-pure");
let cargo_toml_str = fs_err::read_to_string(manifest_dir.join("Cargo.toml")).unwrap();
let cargo_toml: CargoToml = toml_edit::easy::from_str(&cargo_toml_str).unwrap();
let mut metadata = Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_dir.join("Cargo.toml"))
.exec()
.unwrap();
let mut metadata =
Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir, &cargo_metadata).unwrap();
let pyproject_toml = PyProjectToml::new(manifest_dir.join("pyproject.toml")).unwrap();
metadata
.merge_pyproject_toml(&manifest_dir, &pyproject_toml)
@@ -850,7 +875,12 @@ mod test {
let manifest_dir = PathBuf::from("test-crates").join("pyo3-mixed-py-subdir");
let cargo_toml_str = fs_err::read_to_string(manifest_dir.join("Cargo.toml")).unwrap();
let cargo_toml: CargoToml = toml_edit::easy::from_str(&cargo_toml_str).unwrap();
let mut metadata = Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_dir.join("Cargo.toml"))
.exec()
.unwrap();
let mut metadata =
Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir, &cargo_metadata).unwrap();
let pyproject_toml = PyProjectToml::new(manifest_dir.join("pyproject.toml")).unwrap();
metadata
.merge_pyproject_toml(&manifest_dir, &pyproject_toml)
@@ -866,9 +896,15 @@ mod test {

#[test]
fn test_implicit_readme() {
let cargo_toml_str = fs_err::read_to_string("test-crates/pyo3-mixed/Cargo.toml").unwrap();
let manifest_dir = PathBuf::from("test-crates").join("pyo3-mixed");
let cargo_toml_str = fs_err::read_to_string(manifest_dir.join("Cargo.toml")).unwrap();
let cargo_toml = toml_edit::easy::from_str(&cargo_toml_str).unwrap();
let metadata = Metadata21::from_cargo_toml(&cargo_toml, "test-crates/pyo3-mixed").unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_dir.join("Cargo.toml"))
.exec()
.unwrap();
let metadata =
Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir, &cargo_metadata).unwrap();
assert!(metadata.description.unwrap().starts_with("# pyo3-mixed"));
assert_eq!(
metadata.description_content_type.unwrap(),
@@ -881,7 +917,12 @@ mod test {
let manifest_dir = PathBuf::from("test-crates").join("license-test");
let cargo_toml_str = fs_err::read_to_string(&manifest_dir.join("Cargo.toml")).unwrap();
let cargo_toml: CargoToml = toml_edit::easy::from_str(&cargo_toml_str).unwrap();
let mut metadata = Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir).unwrap();
let cargo_metadata = MetadataCommand::new()
.manifest_path(manifest_dir.join("Cargo.toml"))
.exec()
.unwrap();
let mut metadata =
Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir, &cargo_metadata).unwrap();
let pyproject_toml = PyProjectToml::new(manifest_dir.join("pyproject.toml")).unwrap();
metadata
.merge_pyproject_toml(&manifest_dir, &pyproject_toml)
10 changes: 4 additions & 6 deletions src/project_layout.rs
Original file line number Diff line number Diff line change
@@ -31,8 +31,6 @@ pub struct ProjectResolver {
pub project_layout: ProjectLayout,
/// Cargo.toml path
pub cargo_toml_path: PathBuf,
/// Parsed Cargo.toml
pub cargo_toml: CargoToml,
/// pyproject.toml path
pub pyproject_toml_path: PathBuf,
/// Parsed pyproject.toml
@@ -87,15 +85,16 @@ impl ProjectResolver {

let cargo_metadata = Self::resolve_cargo_metadata(&manifest_file, &cargo_options)?;

let mut metadata21 = Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir)
.context("Failed to parse Cargo.toml into python metadata")?;
let mut metadata21 =
Metadata21::from_cargo_toml(&cargo_toml, &manifest_dir, &cargo_metadata)
.context("Failed to parse Cargo.toml into python metadata")?;
if let Some(pyproject) = pyproject {
let pyproject_dir = pyproject_file.parent().unwrap();
metadata21.merge_pyproject_toml(&pyproject_dir, pyproject)?;
}
let extra_metadata = cargo_toml.remaining_core_metadata();

let crate_name = &cargo_toml.package.name;
let crate_name = &metadata21.name;

// If the package name contains minuses, you must declare a module with
// underscores as lib name
@@ -146,7 +145,6 @@ impl ProjectResolver {
Ok(Self {
project_layout,
cargo_toml_path: manifest_file,
cargo_toml,
pyproject_toml_path: pyproject_file,
pyproject_toml,
module_name,
3 changes: 3 additions & 0 deletions test-crates/workspace-inheritance/Cargo.toml
Original file line number Diff line number Diff line change
@@ -4,6 +4,9 @@ members = [
"python"
]

[workspace.package]
version = "0.1.0"

[workspace.dependencies]
libc = { version = "0.2", features = ["std"] }
generic_lib = { path = "generic_lib" }
2 changes: 1 addition & 1 deletion test-crates/workspace-inheritance/python/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "workspace-inheritance"
version = "0.1.0"
version.workspace = true
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html