Skip to content

Commit a0bf1fb

Browse files
committed
Implement base paths (RFC 3529) 1/n: path dep and patch support
1 parent 403bc5b commit a0bf1fb

File tree

6 files changed

+466
-22
lines changed

6 files changed

+466
-22
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ pub struct TomlDetailedDependency<P: Clone = String> {
776776
// `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
777777
// that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
778778
pub path: Option<P>,
779+
pub base: Option<String>,
779780
pub git: Option<String>,
780781
pub branch: Option<String>,
781782
pub tag: Option<String>,
@@ -815,6 +816,7 @@ impl<P: Clone> Default for TomlDetailedDependency<P> {
815816
registry: Default::default(),
816817
registry_index: Default::default(),
817818
path: Default::default(),
819+
base: Default::default(),
818820
git: Default::default(),
819821
branch: Default::default(),
820822
tag: Default::default(),

src/cargo/core/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,7 @@ unstable_cli_options!(
776776
no_index_update: bool = ("Do not update the registry index even if the cache is outdated"),
777777
package_workspace: bool = ("Handle intra-workspace dependencies when packaging"),
778778
panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"),
779+
path_bases: bool = ("Allow paths that resolve relatively to a base specified in the config"),
779780
profile_rustflags: bool = ("Enable the `rustflags` option in profiles in .cargo/config.toml file"),
780781
public_dependency: bool = ("Respect a dependency's `public` field in Cargo.toml to control public/private dependencies"),
781782
publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"),
@@ -1280,6 +1281,7 @@ impl CliUnstable {
12801281
"package-workspace" => self.package_workspace= parse_empty(k, v)?,
12811282
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
12821283
"public-dependency" => self.public_dependency = parse_empty(k, v)?,
1284+
"path-bases" => self.path_bases = parse_empty(k, v)?,
12831285
"profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?,
12841286
"trim-paths" => self.trim_paths = parse_empty(k, v)?,
12851287
"publish-timeout" => self.publish_timeout = parse_empty(k, v)?,

src/cargo/util/toml/mod.rs

Lines changed: 56 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -901,13 +901,17 @@ impl InheritableFields {
901901
};
902902
let mut dep = dep.clone();
903903
if let manifest::TomlDependency::Detailed(detailed) = &mut dep {
904-
if let Some(rel_path) = &detailed.path {
905-
detailed.path = Some(resolve_relative_path(
906-
name,
907-
self.ws_root(),
908-
package_root,
909-
rel_path,
910-
)?);
904+
if detailed.base.is_none() {
905+
// If this is a path dependency without a base, then update the path to be relative
906+
// to the workspace root instead.
907+
if let Some(rel_path) = &detailed.path {
908+
detailed.path = Some(resolve_relative_path(
909+
name,
910+
self.ws_root(),
911+
package_root,
912+
rel_path,
913+
)?);
914+
}
911915
}
912916
}
913917
Ok(dep)
@@ -2135,7 +2139,16 @@ fn to_dependency_source_id<P: ResolveToPath + Clone>(
21352139
// always end up hashing to the same value no matter where it's
21362140
// built from.
21372141
if manifest_ctx.source_id.is_path() {
2138-
let path = manifest_ctx.root.join(path);
2142+
let path = if let Some(base) = orig.base.as_ref() {
2143+
lookup_path_base(&base, manifest_ctx.gctx, manifest_ctx.root)
2144+
.with_context(|| {
2145+
format!("resolving path base for dependency ({name_in_toml})")
2146+
})?
2147+
.join(path)
2148+
} else {
2149+
// This is a standard path with no prefix.
2150+
manifest_ctx.root.join(path)
2151+
};
21392152
let path = paths::normalize_path(&path);
21402153
SourceId::for_path(&path)
21412154
} else {
@@ -2151,6 +2164,40 @@ fn to_dependency_source_id<P: ResolveToPath + Clone>(
21512164
}
21522165
}
21532166

2167+
pub(crate) fn lookup_path_base(
2168+
base: &str,
2169+
gctx: &GlobalContext,
2170+
manifest_root: &Path,
2171+
) -> CargoResult<PathBuf> {
2172+
if !gctx.cli_unstable().path_bases {
2173+
bail!("usage of path bases requires `-Z path-bases`");
2174+
}
2175+
2176+
// Look up the relevant base in the Config and use that as the root.
2177+
if let Some(path_bases) =
2178+
gctx.get::<Option<ConfigRelativePath>>(&format!("path-bases.{base}"))?
2179+
{
2180+
Ok(path_bases.resolve_path(gctx))
2181+
} else {
2182+
// Otherwise, check the built-in bases.
2183+
match base {
2184+
"workspace" => {
2185+
if let Some(workspace_root) = find_workspace_root(manifest_root, gctx)? {
2186+
Ok(workspace_root.parent().unwrap().to_path_buf())
2187+
} else {
2188+
bail!(
2189+
"the `workspace` built-in path base cannot be used outside of a workspace."
2190+
)
2191+
}
2192+
}
2193+
_ => bail!(
2194+
"path base `{base}` is undefined. \
2195+
You must add an entry for `{base}` in the Cargo configuration [path-bases] table."
2196+
),
2197+
}
2198+
}
2199+
}
2200+
21542201
pub trait ResolveToPath {
21552202
fn resolve(&self, gctx: &GlobalContext) -> PathBuf;
21562203
}
@@ -2865,6 +2912,7 @@ fn prepare_toml_for_publish(
28652912
let mut d = d.clone();
28662913
// Path dependencies become crates.io deps.
28672914
d.path.take();
2915+
d.base.take();
28682916
// Same with git dependencies.
28692917
d.git.take();
28702918
d.branch.take();

tests/testsuite/cargo/z_help/stdout.term.svg

Lines changed: 16 additions & 14 deletions
Loading

tests/testsuite/patch.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3028,3 +3028,45 @@ foo v0.0.0 ([ROOT]/foo)
30283028

30293029
assert_eq!(p.read_file("Cargo.lock"), p.read_file("Cargo.lock.orig"));
30303030
}
3031+
3032+
#[cargo_test]
3033+
fn patch_with_base() {
3034+
let bar = project()
3035+
.at("bar")
3036+
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
3037+
.file("src/lib.rs", "pub fn hello() {}")
3038+
.build();
3039+
Package::new("bar", "0.5.0").publish();
3040+
3041+
let p = project()
3042+
.file(
3043+
".cargo/config.toml",
3044+
&format!(
3045+
"[path-bases]\ntest = '{}'",
3046+
bar.root().parent().unwrap().display()
3047+
),
3048+
)
3049+
.file(
3050+
"Cargo.toml",
3051+
r#"
3052+
[package]
3053+
name = "foo"
3054+
version = "0.5.0"
3055+
authors = ["[email protected]"]
3056+
edition = "2018"
3057+
3058+
[dependencies]
3059+
bar = "0.5.0"
3060+
3061+
[patch.crates-io.bar]
3062+
path = 'bar'
3063+
base = 'test'
3064+
"#,
3065+
)
3066+
.file("src/lib.rs", "use bar::hello as _;")
3067+
.build();
3068+
3069+
p.cargo("build -v -Zpath-bases")
3070+
.masquerade_as_nightly_cargo(&["path-bases"])
3071+
.run();
3072+
}

0 commit comments

Comments
 (0)