Skip to content

Commit a0d901e

Browse files
committed
Implement support for base paths
1 parent f9946d1 commit a0d901e

File tree

4 files changed

+264
-8
lines changed

4 files changed

+264
-8
lines changed

crates/cargo-util-schemas/src/manifest.rs

+2
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ pub struct TomlDetailedDependency<P: Clone = String> {
585585
// `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
586586
// that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
587587
pub path: Option<P>,
588+
pub base: Option<String>,
588589
pub git: Option<String>,
589590
pub branch: Option<String>,
590591
pub tag: Option<String>,
@@ -624,6 +625,7 @@ impl<P: Clone> Default for TomlDetailedDependency<P> {
624625
registry: Default::default(),
625626
registry_index: Default::default(),
626627
path: Default::default(),
628+
base: Default::default(),
627629
git: Default::default(),
628630
branch: Default::default(),
629631
tag: Default::default(),

src/cargo/core/features.rs

+2
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,7 @@ unstable_cli_options!(
751751
next_lockfile_bump: bool = (HIDDEN),
752752
no_index_update: bool = ("Do not update the registry index even if the cache is outdated"),
753753
panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"),
754+
path_bases: bool = ("Allow paths that resolve relatively to a base specified in the config"),
754755
profile_rustflags: bool = ("Enable the `rustflags` option in profiles in .cargo/config.toml file"),
755756
publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"),
756757
rustdoc_map: bool = ("Allow passing external documentation mappings to rustdoc"),
@@ -1094,6 +1095,7 @@ impl CliUnstable {
10941095
"mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,
10951096
"no-index-update" => self.no_index_update = parse_empty(k, v)?,
10961097
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
1098+
"path-bases" => self.path_bases = parse_empty(k, v)?,
10971099
"profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?,
10981100
"trim-paths" => self.trim_paths = parse_empty(k, v)?,
10991101
"publish-timeout" => self.publish_timeout = parse_empty(k, v)?,

src/cargo/util/toml/mod.rs

+34-8
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ pub fn prepare_for_publish(
334334
let mut d = d.clone();
335335
// Path dependencies become crates.io deps.
336336
d.path.take();
337+
d.base.take();
337338
// Same with git dependencies.
338339
d.git.take();
339340
d.branch.take();
@@ -1504,13 +1505,17 @@ impl InheritableFields {
15041505
};
15051506
let mut dep = dep.clone();
15061507
if let manifest::TomlDependency::Detailed(detailed) = &mut dep {
1507-
if let Some(rel_path) = &detailed.path {
1508-
detailed.path = Some(resolve_relative_path(
1509-
name,
1510-
self.ws_root(),
1511-
package_root,
1512-
rel_path,
1513-
)?);
1508+
if detailed.base.is_none() {
1509+
// If this is a path dependency without a base, then update the path to be relative
1510+
// to the workspace root instead.
1511+
if let Some(rel_path) = &detailed.path {
1512+
detailed.path = Some(resolve_relative_path(
1513+
name,
1514+
self.ws_root(),
1515+
package_root,
1516+
rel_path,
1517+
)?);
1518+
}
15141519
}
15151520
}
15161521
Ok(dep)
@@ -1877,7 +1882,28 @@ fn detailed_dep_to_dependency<P: ResolveToPath + Clone>(
18771882
// always end up hashing to the same value no matter where it's
18781883
// built from.
18791884
if cx.source_id.is_path() {
1880-
let path = cx.root.join(path);
1885+
let path = if let Some(base) = orig.base.as_ref() {
1886+
if !cx.config.cli_unstable().path_bases {
1887+
bail!("usage of path bases requires `-Z path-bases`");
1888+
}
1889+
1890+
// Look up the relevant base in the Config and use that as the root.
1891+
if let Some(base_path) = cx
1892+
.config
1893+
.get::<Option<ConfigRelativePath>>(&format!("base_path.{base}"))?
1894+
{
1895+
let base_path = base_path.resolve_path(cx.config);
1896+
base_path.join(path)
1897+
} else {
1898+
bail!(
1899+
"dependency ({name_in_toml}) uses an undefined base path `{base}`. \
1900+
You must add an entry for `{base}` in the Cargo configuration [base_path] table."
1901+
);
1902+
}
1903+
} else {
1904+
// This is a standard path with no prefix.
1905+
cx.root.join(path)
1906+
};
18811907
let path = paths::normalize_path(&path);
18821908
SourceId::for_path(&path)?
18831909
} else {

tests/testsuite/path.rs

+226
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,232 @@ Caused by:
534534
.run();
535535
}
536536

537+
#[cargo_test]
538+
fn path_bases_not_stable() {
539+
let bar = project()
540+
.at("bar")
541+
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
542+
.file("src/lib.rs", "")
543+
.build();
544+
545+
fs::create_dir(&paths::root().join(".cargo")).unwrap();
546+
fs::write(
547+
&paths::root().join(".cargo/config"),
548+
&format!(
549+
"[base_path]\ntest = '{}'",
550+
bar.root().parent().unwrap().display()
551+
),
552+
)
553+
.unwrap();
554+
555+
let p = project()
556+
.file(
557+
"Cargo.toml",
558+
r#"
559+
[package]
560+
561+
name = "foo"
562+
version = "0.5.0"
563+
authors = ["[email protected]"]
564+
565+
[dependencies.bar]
566+
path = 'bar'
567+
base = 'test'
568+
"#,
569+
)
570+
.file("src/lib.rs", "")
571+
.build();
572+
573+
p.cargo("build")
574+
.with_status(101)
575+
.with_stderr(
576+
"\
577+
error: failed to parse manifest at `[..]/foo/Cargo.toml`
578+
579+
Caused by:
580+
usage of path bases requires `-Z path-bases`
581+
",
582+
)
583+
.run();
584+
}
585+
586+
#[cargo_test]
587+
fn patch_with_base() {
588+
let bar = project()
589+
.at("bar")
590+
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
591+
.file("src/lib.rs", "pub fn hello() {}")
592+
.build();
593+
Package::new("bar", "0.5.0").publish();
594+
595+
fs::create_dir(&paths::root().join(".cargo")).unwrap();
596+
fs::write(
597+
&paths::root().join(".cargo/config"),
598+
&format!(
599+
"[base_path]\ntest = '{}'",
600+
bar.root().parent().unwrap().display()
601+
),
602+
)
603+
.unwrap();
604+
605+
let p = project()
606+
.file(
607+
"Cargo.toml",
608+
r#"
609+
[package]
610+
name = "foo"
611+
version = "0.5.0"
612+
authors = ["[email protected]"]
613+
edition = "2018"
614+
615+
[dependencies.bar]
616+
bar = "0.5.0"
617+
618+
[patch.crates-io.bar]
619+
path = 'bar'
620+
base = 'test'
621+
"#,
622+
)
623+
.file("src/lib.rs", "use bar::hello as _;")
624+
.build();
625+
626+
p.cargo("build -v -Zpath-bases")
627+
.masquerade_as_nightly_cargo(&["path-bases"])
628+
.run();
629+
}
630+
631+
#[cargo_test]
632+
fn path_with_base() {
633+
let bar = project()
634+
.at("bar")
635+
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
636+
.file("src/lib.rs", "")
637+
.build();
638+
639+
fs::create_dir(&paths::root().join(".cargo")).unwrap();
640+
fs::write(
641+
&paths::root().join(".cargo/config"),
642+
&format!(
643+
"[base_path]\ntest = '{}'",
644+
bar.root().parent().unwrap().display()
645+
),
646+
)
647+
.unwrap();
648+
649+
let p = project()
650+
.file(
651+
"Cargo.toml",
652+
r#"
653+
[package]
654+
655+
name = "foo"
656+
version = "0.5.0"
657+
authors = ["[email protected]"]
658+
659+
[dependencies.bar]
660+
path = 'bar'
661+
base = 'test'
662+
"#,
663+
)
664+
.file("src/lib.rs", "")
665+
.build();
666+
667+
p.cargo("build -v -Zpath-bases")
668+
.masquerade_as_nightly_cargo(&["path-bases"])
669+
.run();
670+
}
671+
672+
#[cargo_test]
673+
fn workspace_with_base() {
674+
let bar = project()
675+
.at("dep_with_base")
676+
.file("Cargo.toml", &basic_manifest("dep_with_base", "0.5.0"))
677+
.file("src/lib.rs", "")
678+
.build();
679+
680+
fs::create_dir(&paths::root().join(".cargo")).unwrap();
681+
fs::write(
682+
&paths::root().join(".cargo/config"),
683+
&format!(
684+
"[base_path]\ntest = '{}'",
685+
bar.root().parent().unwrap().display()
686+
),
687+
)
688+
.unwrap();
689+
690+
let p = project()
691+
.file(
692+
"Cargo.toml",
693+
r#"
694+
[package]
695+
name = "parent"
696+
version = "0.1.0"
697+
authors = []
698+
699+
[workspace]
700+
members = ["child"]
701+
702+
[workspace.dependencies.dep_with_base]
703+
path = 'dep_with_base'
704+
base = 'test'
705+
"#,
706+
)
707+
.file("src/main.rs", "fn main() {}")
708+
.file(
709+
"child/Cargo.toml",
710+
r#"
711+
[package]
712+
name = "child"
713+
version = "0.1.0"
714+
authors = []
715+
workspace = ".."
716+
717+
[dependencies.dep_with_base]
718+
workspace = true
719+
"#,
720+
)
721+
.file("child/src/main.rs", "fn main() {}");
722+
let p = p.build();
723+
724+
p.cargo("build -v -Zpath-bases")
725+
.masquerade_as_nightly_cargo(&["path-bases"])
726+
.run();
727+
}
728+
729+
#[cargo_test]
730+
fn unknown_base() {
731+
let p = project()
732+
.file(
733+
"Cargo.toml",
734+
r#"
735+
[package]
736+
737+
name = "foo"
738+
version = "0.5.0"
739+
authors = ["[email protected]"]
740+
741+
[dependencies.bar]
742+
path = 'bar'
743+
base = 'test'
744+
"#,
745+
)
746+
.file("src/lib.rs", "")
747+
.build();
748+
749+
p.cargo("build -Zpath-bases")
750+
.masquerade_as_nightly_cargo(&["path-bases"])
751+
.with_status(101)
752+
.with_stderr(
753+
"\
754+
error: failed to parse manifest at `[..]/foo/Cargo.toml`
755+
756+
Caused by:
757+
dependency (bar) uses an undefined base path `test`. You must add an entry for `test` in the Cargo configuration [base_path] table.
758+
",
759+
)
760+
.run();
761+
}
762+
537763
#[cargo_test]
538764
fn override_relative() {
539765
let bar = project()

0 commit comments

Comments
 (0)