Skip to content

Commit fc5035d

Browse files
committed
Auto merge of #10548 - Muscraft:rfc2906-part4, r=epage
Part 4 of RFC2906 - Add support for inheriting `readme` Tracking issue: #8415 RFC: rust-lang/rfcs#2906 [Part 1](#10497) [Part 2](#10517) [Part 3](#10538) This PR focuses on adding support for inheriting `readme`: - Added adjusting of a `readme` path when it is outside of the `package_root` - Copied from `license-file`'s implementation in `toml::prepare_for_publish()` - Added copying of a `readme` file when it is outside of the `package_root` for publishing - Copied from `license-file`'s implementation in `cargo_package::build_ar_list()` - Merged copying of a file outside of a `package_root` to reduce duplicated code and allow for other files in the future to be copied in a similar way Remaining implementation work for the RFC - Path dependencies infer version directive - Lock workspace dependencies and warn when unused - Optimizations, as needed - Evaluate any new fields for being inheritable (e.g. `rust-version`)
2 parents b36cc6e + 3bbb781 commit fc5035d

File tree

4 files changed

+130
-53
lines changed

4 files changed

+130
-53
lines changed

src/cargo/core/workspace.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::util::errors::{CargoResult, ManifestError};
2323
use crate::util::interning::InternedString;
2424
use crate::util::lev_distance;
2525
use crate::util::toml::{
26-
read_manifest, StringOrBool, TomlDependency, TomlProfiles, VecStringOrBool,
26+
read_manifest, readme_for_project, StringOrBool, TomlDependency, TomlProfiles, VecStringOrBool,
2727
};
2828
use crate::util::{config::ConfigRelativePath, Config, Filesystem, IntoUrl};
2929
use cargo_util::paths;
@@ -1754,12 +1754,15 @@ impl InheritableFields {
17541754
)
17551755
}
17561756

1757-
pub fn readme(&self) -> CargoResult<StringOrBool> {
1758-
self.readme
1759-
.clone()
1760-
.map_or(Err(anyhow!("`workspace.readme` was not defined")), |d| {
1761-
Ok(d)
1762-
})
1757+
pub fn readme(&self, package_root: &Path) -> CargoResult<StringOrBool> {
1758+
readme_for_project(self.ws_root.as_path(), self.readme.clone()).map_or(
1759+
Err(anyhow!("`workspace.readme` was not defined")),
1760+
|readme| {
1761+
let rel_path =
1762+
resolve_relative_path("readme", &self.ws_root, package_root, &readme)?;
1763+
Ok(StringOrBool::String(rel_path))
1764+
},
1765+
)
17631766
}
17641767

17651768
pub fn keywords(&self) -> CargoResult<Vec<String>> {

src/cargo/ops/cargo_package.rs

Lines changed: 68 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -260,46 +260,16 @@ fn build_ar_list(
260260
}
261261
if let Some(license_file) = &pkg.manifest().metadata().license_file {
262262
let license_path = Path::new(license_file);
263-
let abs_license_path = paths::normalize_path(&pkg.root().join(license_path));
264-
if abs_license_path.exists() {
265-
match abs_license_path.strip_prefix(&pkg.root()) {
266-
Ok(rel_license_path) => {
267-
if !result.iter().any(|ar| ar.rel_path == rel_license_path) {
268-
result.push(ArchiveFile {
269-
rel_path: rel_license_path.to_path_buf(),
270-
rel_str: rel_license_path
271-
.to_str()
272-
.expect("everything was utf8")
273-
.to_string(),
274-
contents: FileContents::OnDisk(abs_license_path),
275-
});
276-
}
277-
}
278-
Err(_) => {
279-
// The license exists somewhere outside of the package.
280-
let license_name = license_path.file_name().unwrap();
281-
if result
282-
.iter()
283-
.any(|ar| ar.rel_path.file_name().unwrap() == license_name)
284-
{
285-
ws.config().shell().warn(&format!(
286-
"license-file `{}` appears to be a path outside of the package, \
287-
but there is already a file named `{}` in the root of the package. \
288-
The archived crate will contain the copy in the root of the package. \
289-
Update the license-file to point to the path relative \
290-
to the root of the package to remove this warning.",
291-
license_file,
292-
license_name.to_str().unwrap()
293-
))?;
294-
} else {
295-
result.push(ArchiveFile {
296-
rel_path: PathBuf::from(license_name),
297-
rel_str: license_name.to_str().unwrap().to_string(),
298-
contents: FileContents::OnDisk(abs_license_path),
299-
});
300-
}
301-
}
302-
}
263+
let abs_file_path = paths::normalize_path(&pkg.root().join(license_path));
264+
if abs_file_path.exists() {
265+
check_for_file_and_add(
266+
"license-file",
267+
license_path,
268+
abs_file_path,
269+
pkg,
270+
&mut result,
271+
ws,
272+
)?;
303273
} else {
304274
let rel_msg = if license_path.is_absolute() {
305275
"".to_string()
@@ -316,11 +286,69 @@ fn build_ar_list(
316286
))?;
317287
}
318288
}
289+
if let Some(readme) = &pkg.manifest().metadata().readme {
290+
let readme_path = Path::new(readme);
291+
let abs_file_path = paths::normalize_path(&pkg.root().join(readme_path));
292+
if abs_file_path.exists() {
293+
check_for_file_and_add("readme", readme_path, abs_file_path, pkg, &mut result, ws)?;
294+
}
295+
}
319296
result.sort_unstable_by(|a, b| a.rel_path.cmp(&b.rel_path));
320297

321298
Ok(result)
322299
}
323300

301+
fn check_for_file_and_add(
302+
label: &str,
303+
file_path: &Path,
304+
abs_file_path: PathBuf,
305+
pkg: &Package,
306+
result: &mut Vec<ArchiveFile>,
307+
ws: &Workspace<'_>,
308+
) -> CargoResult<()> {
309+
match abs_file_path.strip_prefix(&pkg.root()) {
310+
Ok(rel_file_path) => {
311+
if !result.iter().any(|ar| ar.rel_path == rel_file_path) {
312+
result.push(ArchiveFile {
313+
rel_path: rel_file_path.to_path_buf(),
314+
rel_str: rel_file_path
315+
.to_str()
316+
.expect("everything was utf8")
317+
.to_string(),
318+
contents: FileContents::OnDisk(abs_file_path),
319+
})
320+
}
321+
}
322+
Err(_) => {
323+
// The file exists somewhere outside of the package.
324+
let file_name = file_path.file_name().unwrap();
325+
if result
326+
.iter()
327+
.any(|ar| ar.rel_path.file_name().unwrap() == file_name)
328+
{
329+
ws.config().shell().warn(&format!(
330+
"{} `{}` appears to be a path outside of the package, \
331+
but there is already a file named `{}` in the root of the package. \
332+
The archived crate will contain the copy in the root of the package. \
333+
Update the {} to point to the path relative \
334+
to the root of the package to remove this warning.",
335+
label,
336+
file_path.display(),
337+
file_name.to_str().unwrap(),
338+
label,
339+
))?;
340+
} else {
341+
result.push(ArchiveFile {
342+
rel_path: PathBuf::from(file_name),
343+
rel_str: file_name.to_str().unwrap().to_string(),
344+
contents: FileContents::OnDisk(abs_file_path),
345+
})
346+
}
347+
}
348+
}
349+
Ok(())
350+
}
351+
324352
/// Construct `Cargo.lock` for the package to be published.
325353
fn build_lock(ws: &Workspace<'_>, orig_pkg: &Package) -> CargoResult<String> {
326354
let config = ws.config();

src/cargo/util/toml/mod.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,7 @@ pub struct TomlProject {
10731073
description: Option<MaybeWorkspace<String>>,
10741074
homepage: Option<MaybeWorkspace<String>>,
10751075
documentation: Option<MaybeWorkspace<String>>,
1076-
readme: Option<StringOrBool>,
1076+
readme: Option<MaybeWorkspace<StringOrBool>>,
10771077
keywords: Option<MaybeWorkspace<Vec<String>>>,
10781078
categories: Option<MaybeWorkspace<Vec<String>>>,
10791079
license: Option<MaybeWorkspace<String>>,
@@ -1175,6 +1175,31 @@ impl TomlManifest {
11751175
));
11761176
}
11771177
}
1178+
1179+
if let Some(readme) = &package.readme {
1180+
let readme = readme
1181+
.as_defined()
1182+
.context("readme should have been resolved before `prepare_for_publish()`")?;
1183+
match readme {
1184+
StringOrBool::String(readme) => {
1185+
let readme_path = Path::new(&readme);
1186+
let abs_readme_path = paths::normalize_path(&package_root.join(readme_path));
1187+
if abs_readme_path.strip_prefix(package_root).is_err() {
1188+
// This path points outside of the package root. `cargo package`
1189+
// will copy it into the root, so adjust the path to this location.
1190+
package.readme = Some(MaybeWorkspace::Defined(StringOrBool::String(
1191+
readme_path
1192+
.file_name()
1193+
.unwrap()
1194+
.to_str()
1195+
.unwrap()
1196+
.to_string(),
1197+
)));
1198+
}
1199+
}
1200+
StringOrBool::Bool(_) => {}
1201+
}
1202+
}
11781203
let all = |_d: &TomlDependency| true;
11791204
return Ok(TomlManifest {
11801205
package: Some(package),
@@ -1693,7 +1718,19 @@ impl TomlManifest {
16931718
})
16941719
})
16951720
.transpose()?,
1696-
readme: readme_for_project(package_root, project),
1721+
readme: readme_for_project(
1722+
package_root,
1723+
project
1724+
.readme
1725+
.clone()
1726+
.map(|mw| {
1727+
mw.resolve(&features, "readme", || {
1728+
get_ws(config, resolved_path.clone(), workspace_config.clone())?
1729+
.readme(package_root)
1730+
})
1731+
})
1732+
.transpose()?,
1733+
),
16971734
authors: project
16981735
.authors
16991736
.clone()
@@ -1778,6 +1815,10 @@ impl TomlManifest {
17781815
.documentation
17791816
.clone()
17801817
.map(|documentation| MaybeWorkspace::Defined(documentation));
1818+
project.readme = metadata
1819+
.readme
1820+
.clone()
1821+
.map(|readme| MaybeWorkspace::Defined(StringOrBool::String(readme)));
17811822
project.authors = project
17821823
.authors
17831824
.as_ref()
@@ -2164,8 +2205,8 @@ fn inheritable_from_path(
21642205
}
21652206

21662207
/// Returns the name of the README file for a `TomlProject`.
2167-
fn readme_for_project(package_root: &Path, project: &TomlProject) -> Option<String> {
2168-
match &project.readme {
2208+
pub fn readme_for_project(package_root: &Path, readme: Option<StringOrBool>) -> Option<String> {
2209+
match &readme {
21692210
None => default_readme_from_package_root(package_root),
21702211
Some(value) => match value {
21712212
StringOrBool::Bool(false) => None,

tests/testsuite/inheritable_workspace_fields.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,7 @@ fn inherit_workspace_fields() {
581581
authors = ["Rustaceans"]
582582
description = "This is a crate"
583583
documentation = "https://www.rust-lang.org/learn"
584+
readme = "README.md"
584585
homepage = "https://www.rust-lang.org"
585586
repository = "https://github.com/example/example"
586587
license = "MIT"
@@ -606,6 +607,7 @@ fn inherit_workspace_fields() {
606607
authors = { workspace = true }
607608
description = { workspace = true }
608609
documentation = { workspace = true }
610+
readme = { workspace = true }
609611
homepage = { workspace = true }
610612
repository = { workspace = true }
611613
license = { workspace = true }
@@ -617,6 +619,7 @@ fn inherit_workspace_fields() {
617619
"#,
618620
)
619621
.file("LICENSE", "license")
622+
.file("README.md", "README.md")
620623
.file("bar/src/main.rs", "fn main() {}")
621624
.build();
622625

@@ -642,8 +645,8 @@ fn inherit_workspace_fields() {
642645
"license_file": "../LICENSE",
643646
"links": null,
644647
"name": "bar",
645-
"readme": null,
646-
"readme_file": null,
648+
"readme": "README.md",
649+
"readme_file": "../README.md",
647650
"repository": "https://github.com/example/example",
648651
"vers": "1.2.3"
649652
}
@@ -654,6 +657,7 @@ fn inherit_workspace_fields() {
654657
"Cargo.toml",
655658
"Cargo.toml.orig",
656659
"src/main.rs",
660+
"README.md",
657661
"LICENSE",
658662
".cargo_vcs_info.json",
659663
],
@@ -672,6 +676,7 @@ publish = true
672676
description = "This is a crate"
673677
homepage = "https://www.rust-lang.org"
674678
documentation = "https://www.rust-lang.org/learn"
679+
readme = "README.md"
675680
keywords = ["cli"]
676681
categories = ["development-tools"]
677682
license = "MIT"

0 commit comments

Comments
 (0)