Skip to content

Commit f8a73f7

Browse files
committed
Auto merge of #13729 - epage:slash, r=Muscraft
fix(package): Normalize paths in `Cargo.toml` ### What does this PR try to resolve? On the surface, this resolves problems that aren't too big of a deal - Clean up redundant information in paths (e.g. `.////.//foo.rs` being `foo.rs`) which is just visual - Switch paths with `\` in them to `/` However, this is prep for #13713 where these will be a much bigger deal - If the user does `./foo.rs`, we might fail to compare that with the list of files included in the package - We'll generate paths with `\` and need to make sure the packages can still be used on Windows ### How should we test and review this PR? ### Additional information
2 parents f99d24f + 8b593e5 commit f8a73f7

File tree

2 files changed

+218
-8
lines changed

2 files changed

+218
-8
lines changed

src/cargo/util/toml/mod.rs

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::str::{self, FromStr};
88
use crate::AlreadyPrintedError;
99
use anyhow::{anyhow, bail, Context as _};
1010
use cargo_platform::Platform;
11-
use cargo_util::paths;
11+
use cargo_util::paths::{self, normalize_path};
1212
use cargo_util_schemas::manifest::{self, TomlManifest};
1313
use cargo_util_schemas::manifest::{RustVersion, StringOrBool};
1414
use itertools::Itertools;
@@ -2336,6 +2336,14 @@ fn prepare_toml_for_publish(
23362336

23372337
let mut package = me.package().unwrap().clone();
23382338
package.workspace = None;
2339+
if let Some(StringOrBool::String(path)) = &package.build {
2340+
let path = paths::normalize_path(Path::new(path));
2341+
let path = path
2342+
.into_os_string()
2343+
.into_string()
2344+
.map_err(|_err| anyhow::format_err!("non-UTF8 `package.build`"))?;
2345+
package.build = Some(StringOrBool::String(normalize_path_string_sep(path)));
2346+
}
23392347
let current_resolver = package
23402348
.resolver
23412349
.as_ref()
@@ -2362,7 +2370,16 @@ fn prepare_toml_for_publish(
23622370
.context("license file should have been resolved before `prepare_for_publish()`")?;
23632371
let license_path = Path::new(&license_file);
23642372
let abs_license_path = paths::normalize_path(&package_root.join(license_path));
2365-
if abs_license_path.strip_prefix(package_root).is_err() {
2373+
if let Ok(license_file) = abs_license_path.strip_prefix(package_root) {
2374+
package.license_file = Some(manifest::InheritableField::Value(
2375+
normalize_path_string_sep(
2376+
license_file
2377+
.to_str()
2378+
.ok_or_else(|| anyhow::format_err!("non-UTF8 `package.license-file`"))?
2379+
.to_owned(),
2380+
),
2381+
));
2382+
} else {
23662383
// This path points outside of the package root. `cargo package`
23672384
// will copy it into the root, so adjust the path to this location.
23682385
package.license_file = Some(manifest::InheritableField::Value(
@@ -2384,7 +2401,18 @@ fn prepare_toml_for_publish(
23842401
manifest::StringOrBool::String(readme) => {
23852402
let readme_path = Path::new(&readme);
23862403
let abs_readme_path = paths::normalize_path(&package_root.join(readme_path));
2387-
if abs_readme_path.strip_prefix(package_root).is_err() {
2404+
if let Ok(readme_path) = abs_readme_path.strip_prefix(package_root) {
2405+
package.readme = Some(manifest::InheritableField::Value(StringOrBool::String(
2406+
normalize_path_string_sep(
2407+
readme_path
2408+
.to_str()
2409+
.ok_or_else(|| {
2410+
anyhow::format_err!("non-UTF8 `package.license-file`")
2411+
})?
2412+
.to_owned(),
2413+
),
2414+
)));
2415+
} else {
23882416
// This path points outside of the package root. `cargo package`
23892417
// will copy it into the root, so adjust the path to this location.
23902418
package.readme = Some(manifest::InheritableField::Value(
@@ -2402,16 +2430,27 @@ fn prepare_toml_for_publish(
24022430
manifest::StringOrBool::Bool(_) => {}
24032431
}
24042432
}
2433+
2434+
let lib = if let Some(target) = &me.lib {
2435+
Some(prepare_target_for_publish(target, "library")?)
2436+
} else {
2437+
None
2438+
};
2439+
let bin = prepare_targets_for_publish(me.bin.as_ref(), "binary")?;
2440+
let example = prepare_targets_for_publish(me.example.as_ref(), "example")?;
2441+
let test = prepare_targets_for_publish(me.test.as_ref(), "test")?;
2442+
let bench = prepare_targets_for_publish(me.bench.as_ref(), "benchmark")?;
2443+
24052444
let all = |_d: &manifest::TomlDependency| true;
24062445
let mut manifest = manifest::TomlManifest {
24072446
package: Some(package),
24082447
project: None,
24092448
profile: me.profile.clone(),
2410-
lib: me.lib.clone(),
2411-
bin: me.bin.clone(),
2412-
example: me.example.clone(),
2413-
test: me.test.clone(),
2414-
bench: me.bench.clone(),
2449+
lib,
2450+
bin,
2451+
example,
2452+
test,
2453+
bench,
24152454
dependencies: map_deps(gctx, me.dependencies.as_ref(), all)?,
24162455
dev_dependencies: map_deps(
24172456
gctx,
@@ -2555,3 +2594,49 @@ fn prepare_toml_for_publish(
25552594
.map(manifest::InheritableDependency::Value)
25562595
}
25572596
}
2597+
2598+
fn prepare_targets_for_publish(
2599+
targets: Option<&Vec<manifest::TomlTarget>>,
2600+
context: &str,
2601+
) -> CargoResult<Option<Vec<manifest::TomlTarget>>> {
2602+
let Some(targets) = targets else {
2603+
return Ok(None);
2604+
};
2605+
2606+
let mut prepared = Vec::with_capacity(targets.len());
2607+
for target in targets {
2608+
let target = prepare_target_for_publish(target, context)?;
2609+
prepared.push(target);
2610+
}
2611+
2612+
Ok(Some(prepared))
2613+
}
2614+
2615+
fn prepare_target_for_publish(
2616+
target: &manifest::TomlTarget,
2617+
context: &str,
2618+
) -> CargoResult<manifest::TomlTarget> {
2619+
let mut target = target.clone();
2620+
if let Some(path) = target.path {
2621+
let path = normalize_path(&path.0);
2622+
target.path = Some(manifest::PathValue(normalize_path_sep(path, context)?));
2623+
}
2624+
Ok(target)
2625+
}
2626+
2627+
fn normalize_path_sep(path: PathBuf, context: &str) -> CargoResult<PathBuf> {
2628+
let path = path
2629+
.into_os_string()
2630+
.into_string()
2631+
.map_err(|_err| anyhow::format_err!("non-UTF8 path for {context}"))?;
2632+
let path = normalize_path_string_sep(path);
2633+
Ok(path.into())
2634+
}
2635+
2636+
fn normalize_path_string_sep(path: String) -> String {
2637+
if std::path::MAIN_SEPARATOR != '/' {
2638+
path.replace(std::path::MAIN_SEPARATOR, "/")
2639+
} else {
2640+
path
2641+
}
2642+
}

tests/testsuite/package.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3541,3 +3541,128 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for
35413541
)
35423542
.run()
35433543
}
3544+
3545+
#[cargo_test]
3546+
#[cfg(windows)]
3547+
fn normalize_paths() {
3548+
let p = project()
3549+
.file(
3550+
"Cargo.toml",
3551+
r#"
3552+
[package]
3553+
name = "foo"
3554+
version = "0.0.1"
3555+
edition = "2015"
3556+
description = "foo"
3557+
documentation = "docs.rs/foo"
3558+
authors = []
3559+
readme = ".\\docs\\README.md"
3560+
license-file = ".\\docs\\LICENSE"
3561+
build = ".\\src\\build.rs"
3562+
3563+
[lib]
3564+
path = ".\\src\\lib.rs"
3565+
3566+
[[bin]]
3567+
name = "foo"
3568+
path = ".\\src\\bin\\foo\\main.rs"
3569+
3570+
[[example]]
3571+
name = "example_foo"
3572+
path = ".\\examples\\example_foo.rs"
3573+
3574+
[[test]]
3575+
name = "test_foo"
3576+
path = ".\\tests\\test_foo.rs"
3577+
3578+
[[bench]]
3579+
name = "bench_foo"
3580+
path = ".\\benches\\bench_foo.rs"
3581+
"#,
3582+
)
3583+
.file("src/lib.rs", "")
3584+
.file("docs/README.md", "")
3585+
.file("docs/LICENSE", "")
3586+
.file("src/build.rs", "fn main() {}")
3587+
.file("src/bin/foo/main.rs", "fn main() {}")
3588+
.file("examples/example_foo.rs", "fn main() {}")
3589+
.file("tests/test_foo.rs", "fn main() {}")
3590+
.file("benches/bench_foo.rs", "fn main() {}")
3591+
.build();
3592+
3593+
p.cargo("package")
3594+
.with_stdout("")
3595+
.with_stderr(
3596+
"\
3597+
[PACKAGING] foo v0.0.1 ([CWD])
3598+
[VERIFYING] foo v0.0.1 ([CWD])
3599+
[COMPILING] foo v0.0.1 ([CWD][..])
3600+
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [..]
3601+
[PACKAGED] 11 files, [..] ([..] compressed)
3602+
",
3603+
)
3604+
.run();
3605+
3606+
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
3607+
validate_crate_contents(
3608+
f,
3609+
"foo-0.0.1.crate",
3610+
&[
3611+
"Cargo.lock",
3612+
"Cargo.toml",
3613+
"Cargo.toml.orig",
3614+
"src/lib.rs",
3615+
"docs/README.md",
3616+
"docs/LICENSE",
3617+
"src/build.rs",
3618+
"src/bin/foo/main.rs",
3619+
"examples/example_foo.rs",
3620+
"tests/test_foo.rs",
3621+
"benches/bench_foo.rs",
3622+
],
3623+
&[(
3624+
"Cargo.toml",
3625+
r#"# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
3626+
#
3627+
# When uploading crates to the registry Cargo will automatically
3628+
# "normalize" Cargo.toml files for maximal compatibility
3629+
# with all versions of Cargo and also rewrite `path` dependencies
3630+
# to registry (e.g., crates.io) dependencies.
3631+
#
3632+
# If you are reading this file be aware that the original Cargo.toml
3633+
# will likely look very different (and much more reasonable).
3634+
# See Cargo.toml.orig for the original contents.
3635+
3636+
[package]
3637+
edition = "2015"
3638+
name = "foo"
3639+
version = "0.0.1"
3640+
authors = []
3641+
build = "src/build.rs"
3642+
description = "foo"
3643+
documentation = "docs.rs/foo"
3644+
readme = "docs/README.md"
3645+
license-file = "docs/LICENSE"
3646+
3647+
[lib]
3648+
path = "src/lib.rs"
3649+
3650+
[[bin]]
3651+
name = "foo"
3652+
path = "src/bin/foo/main.rs"
3653+
3654+
[[example]]
3655+
name = "example_foo"
3656+
path = "examples/example_foo.rs"
3657+
3658+
[[test]]
3659+
name = "test_foo"
3660+
path = "tests/test_foo.rs"
3661+
3662+
[[bench]]
3663+
name = "bench_foo"
3664+
path = "benches/bench_foo.rs"
3665+
"#,
3666+
)],
3667+
);
3668+
}

0 commit comments

Comments
 (0)