diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index 4c9a5936ff4..4564c957adb 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -23,7 +23,7 @@ use crate::util::errors::{CargoResult, ManifestError}; use crate::util::interning::InternedString; use crate::util::lev_distance; use crate::util::toml::{ - read_manifest, StringOrBool, TomlDependency, TomlProfiles, VecStringOrBool, + read_manifest, readme_for_project, StringOrBool, TomlDependency, TomlProfiles, VecStringOrBool, }; use crate::util::{config::ConfigRelativePath, Config, Filesystem, IntoUrl}; use cargo_util::paths; @@ -1754,12 +1754,15 @@ impl InheritableFields { ) } - pub fn readme(&self) -> CargoResult { - self.readme - .clone() - .map_or(Err(anyhow!("`workspace.readme` was not defined")), |d| { - Ok(d) - }) + pub fn readme(&self, package_root: &Path) -> CargoResult { + readme_for_project(self.ws_root.as_path(), self.readme.clone()).map_or( + Err(anyhow!("`workspace.readme` was not defined")), + |readme| { + let rel_path = + resolve_relative_path("readme", &self.ws_root, package_root, &readme)?; + Ok(StringOrBool::String(rel_path)) + }, + ) } pub fn keywords(&self) -> CargoResult> { diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index a0cd28f0789..742e68bc7eb 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -286,6 +286,13 @@ fn build_ar_list( ))?; } } + if let Some(readme) = &pkg.manifest().metadata().readme { + let readme_path = Path::new(readme); + let abs_file_path = paths::normalize_path(&pkg.root().join(readme_path)); + if abs_file_path.exists() { + check_for_file_and_add("readme", readme_path, abs_file_path, pkg, &mut result, ws)?; + } + } result.sort_unstable_by(|a, b| a.rel_path.cmp(&b.rel_path)); Ok(result) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 0d1c5b9bfd8..78553de8972 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1073,7 +1073,7 @@ pub struct TomlProject { description: Option>, homepage: Option>, documentation: Option>, - readme: Option, + readme: Option>, keywords: Option>>, categories: Option>>, license: Option>, @@ -1175,6 +1175,31 @@ impl TomlManifest { )); } } + + if let Some(readme) = &package.readme { + let readme = readme + .as_defined() + .context("readme should have been resolved before `prepare_for_publish()`")?; + match readme { + StringOrBool::String(readme) => { + let readme_path = Path::new(&readme); + let abs_readme_path = paths::normalize_path(&package_root.join(readme_path)); + if abs_readme_path.strip_prefix(package_root).is_err() { + // This path points outside of the package root. `cargo package` + // will copy it into the root, so adjust the path to this location. + package.readme = Some(MaybeWorkspace::Defined(StringOrBool::String( + readme_path + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(), + ))); + } + } + StringOrBool::Bool(_) => {} + } + } let all = |_d: &TomlDependency| true; return Ok(TomlManifest { package: Some(package), @@ -1693,7 +1718,19 @@ impl TomlManifest { }) }) .transpose()?, - readme: readme_for_project(package_root, project), + readme: readme_for_project( + package_root, + project + .readme + .clone() + .map(|mw| { + mw.resolve(&features, "readme", || { + get_ws(config, resolved_path.clone(), workspace_config.clone())? + .readme(package_root) + }) + }) + .transpose()?, + ), authors: project .authors .clone() @@ -1778,6 +1815,10 @@ impl TomlManifest { .documentation .clone() .map(|documentation| MaybeWorkspace::Defined(documentation)); + project.readme = metadata + .readme + .clone() + .map(|readme| MaybeWorkspace::Defined(StringOrBool::String(readme))); project.authors = project .authors .as_ref() @@ -2164,8 +2205,8 @@ fn inheritable_from_path( } /// Returns the name of the README file for a `TomlProject`. -fn readme_for_project(package_root: &Path, project: &TomlProject) -> Option { - match &project.readme { +pub fn readme_for_project(package_root: &Path, readme: Option) -> Option { + match &readme { None => default_readme_from_package_root(package_root), Some(value) => match value { StringOrBool::Bool(false) => None, diff --git a/tests/testsuite/inheritable_workspace_fields.rs b/tests/testsuite/inheritable_workspace_fields.rs index 75e1edf432e..1b68e17116d 100644 --- a/tests/testsuite/inheritable_workspace_fields.rs +++ b/tests/testsuite/inheritable_workspace_fields.rs @@ -581,6 +581,7 @@ fn inherit_workspace_fields() { authors = ["Rustaceans"] description = "This is a crate" documentation = "https://www.rust-lang.org/learn" + readme = "README.md" homepage = "https://www.rust-lang.org" repository = "https://github.com/example/example" license = "MIT" @@ -606,6 +607,7 @@ fn inherit_workspace_fields() { authors = { workspace = true } description = { workspace = true } documentation = { workspace = true } + readme = { workspace = true } homepage = { workspace = true } repository = { workspace = true } license = { workspace = true } @@ -617,6 +619,7 @@ fn inherit_workspace_fields() { "#, ) .file("LICENSE", "license") + .file("README.md", "README.md") .file("bar/src/main.rs", "fn main() {}") .build(); @@ -642,8 +645,8 @@ fn inherit_workspace_fields() { "license_file": "../LICENSE", "links": null, "name": "bar", - "readme": null, - "readme_file": null, + "readme": "README.md", + "readme_file": "../README.md", "repository": "https://github.com/example/example", "vers": "1.2.3" } @@ -654,6 +657,7 @@ fn inherit_workspace_fields() { "Cargo.toml", "Cargo.toml.orig", "src/main.rs", + "README.md", "LICENSE", ".cargo_vcs_info.json", ], @@ -672,6 +676,7 @@ publish = true description = "This is a crate" homepage = "https://www.rust-lang.org" documentation = "https://www.rust-lang.org/learn" +readme = "README.md" keywords = ["cli"] categories = ["development-tools"] license = "MIT"