Skip to content

Commit

Permalink
Merge pull request #1131 from messense/workspace-inheritance-metadata
Browse files Browse the repository at this point in the history
Add support for workspace metadata inheritance
  • Loading branch information
messense authored Sep 25, 2022
2 parents d02cf9c + cd7efc7 commit ca4042a
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 84 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
13 changes: 9 additions & 4 deletions src/build_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,6 @@ impl BuildOptions {
let ProjectResolver {
project_layout,
cargo_toml_path,
cargo_toml,
pyproject_toml_path,
pyproject_toml,
module_name,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Up @@ -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>,
}

Expand Down
157 changes: 99 additions & 58 deletions src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}",
Expand Down Expand Up @@ -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());
}

Expand All @@ -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]`
Expand Down Expand Up @@ -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();

Expand All @@ -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]
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand All @@ -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"]
Expand All @@ -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(),
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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(),
Expand All @@ -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)
Expand Down
10 changes: 4 additions & 6 deletions src/project_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit ca4042a

Please sign in to comment.