Skip to content

Commit

Permalink
Implement base paths (RFC 3529) 1/n: path dep and patch support
Browse files Browse the repository at this point in the history
  • Loading branch information
dpaoliello committed Aug 8, 2024
1 parent 403bc5b commit 0736fa6
Show file tree
Hide file tree
Showing 7 changed files with 515 additions and 22 deletions.
2 changes: 2 additions & 0 deletions crates/cargo-util-schemas/src/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ pub struct TomlDetailedDependency<P: Clone = String> {
// `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
// that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
pub path: Option<P>,
pub base: Option<String>,
pub git: Option<String>,
pub branch: Option<String>,
pub tag: Option<String>,
Expand Down Expand Up @@ -815,6 +816,7 @@ impl<P: Clone> Default for TomlDetailedDependency<P> {
registry: Default::default(),
registry_index: Default::default(),
path: Default::default(),
base: Default::default(),
git: Default::default(),
branch: Default::default(),
tag: Default::default(),
Expand Down
2 changes: 2 additions & 0 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,7 @@ unstable_cli_options!(
no_index_update: bool = ("Do not update the registry index even if the cache is outdated"),
package_workspace: bool = ("Handle intra-workspace dependencies when packaging"),
panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"),
path_bases: bool = ("Allow paths that resolve relatively to a base specified in the config"),
profile_rustflags: bool = ("Enable the `rustflags` option in profiles in .cargo/config.toml file"),
public_dependency: bool = ("Respect a dependency's `public` field in Cargo.toml to control public/private dependencies"),
publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"),
Expand Down Expand Up @@ -1280,6 +1281,7 @@ impl CliUnstable {
"package-workspace" => self.package_workspace= parse_empty(k, v)?,
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
"public-dependency" => self.public_dependency = parse_empty(k, v)?,
"path-bases" => self.path_bases = parse_empty(k, v)?,
"profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?,
"trim-paths" => self.trim_paths = parse_empty(k, v)?,
"publish-timeout" => self.publish_timeout = parse_empty(k, v)?,
Expand Down
64 changes: 56 additions & 8 deletions src/cargo/util/toml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -901,13 +901,17 @@ impl InheritableFields {
};
let mut dep = dep.clone();
if let manifest::TomlDependency::Detailed(detailed) = &mut dep {
if let Some(rel_path) = &detailed.path {
detailed.path = Some(resolve_relative_path(
name,
self.ws_root(),
package_root,
rel_path,
)?);
if detailed.base.is_none() {
// If this is a path dependency without a base, then update the path to be relative
// to the workspace root instead.
if let Some(rel_path) = &detailed.path {
detailed.path = Some(resolve_relative_path(
name,
self.ws_root(),
package_root,
rel_path,
)?);
}
}
}
Ok(dep)
Expand Down Expand Up @@ -2135,7 +2139,16 @@ fn to_dependency_source_id<P: ResolveToPath + Clone>(
// always end up hashing to the same value no matter where it's
// built from.
if manifest_ctx.source_id.is_path() {
let path = manifest_ctx.root.join(path);
let path = if let Some(base) = orig.base.as_ref() {
lookup_path_base(&base, manifest_ctx.gctx, manifest_ctx.root)
.with_context(|| {
format!("resolving path base for dependency ({name_in_toml})")
})?
.join(path)
} else {
// This is a standard path with no prefix.
manifest_ctx.root.join(path)
};
let path = paths::normalize_path(&path);
SourceId::for_path(&path)
} else {
Expand All @@ -2151,6 +2164,40 @@ fn to_dependency_source_id<P: ResolveToPath + Clone>(
}
}

pub(crate) fn lookup_path_base(
base: &str,
gctx: &GlobalContext,
manifest_root: &Path,
) -> CargoResult<PathBuf> {
if !gctx.cli_unstable().path_bases {
bail!("usage of path bases requires `-Z path-bases`");
}

// Look up the relevant base in the Config and use that as the root.
if let Some(path_bases) =
gctx.get::<Option<ConfigRelativePath>>(&format!("path-bases.{base}"))?
{
Ok(path_bases.resolve_path(gctx))
} else {
// Otherwise, check the built-in bases.
match base {
"workspace" => {
if let Some(workspace_root) = find_workspace_root(manifest_root, gctx)? {
Ok(workspace_root.parent().unwrap().to_path_buf())
} else {
bail!(
"the `workspace` built-in path base cannot be used outside of a workspace."
)
}
}
_ => bail!(
"path base `{base}` is undefined. \
You must add an entry for `{base}` in the Cargo configuration [path-bases] table."
),
}
}
}

pub trait ResolveToPath {
fn resolve(&self, gctx: &GlobalContext) -> PathBuf;
}
Expand Down Expand Up @@ -2865,6 +2912,7 @@ fn prepare_toml_for_publish(
let mut d = d.clone();
// Path dependencies become crates.io deps.
d.path.take();
d.base.take();
// Same with git dependencies.
d.git.take();
d.branch.take();
Expand Down
49 changes: 49 additions & 0 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ Each new feature described below should explain how to use it.
* [Edition 2024](#edition-2024) — Adds support for the 2024 Edition.
* [Profile `trim-paths` option](#profile-trim-paths-option) --- Control the sanitization of file paths in build outputs.
* [`[lints.cargo]`](#lintscargo) --- Allows configuring lints for Cargo.
* [path bases](#path-bases) --- Named base directories for path dependencies.
* Information and metadata
* [Build-plan](#build-plan) --- Emits JSON information on which commands will be run.
* [unit-graph](#unit-graph) --- Emits JSON for Cargo's internal graph structure.
Expand Down Expand Up @@ -1570,6 +1571,54 @@ implicit-features = "warn"
workspace = true
```

## Path Bases

* Tracking Issue: [#14355](https://github.com/rust-lang/cargo/issues/14355)

A `path` dependency may optionally specify a base by setting the `base` key to
the name of a path base from the `[path-bases]` table in either the
[configuration](config.md) or one of the [built-in path bases](#built-in-path-bases).
The value of that path base is prepended to the `path` value (along with a path
separator if necessary) to produce the actual location where Cargo will look for
the dependency.

For example, if the `Cargo.toml` contains:

```toml
[dependencies]
foo = { path = "foo", base = "dev" }
```

Given a `[path-bases]` table in the configuration that contains:

```toml
[path-bases]
dev = "/home/user/dev/rust/libraries/"
```

This will produce a `path` dependency `foo` located at
`/home/user/dev/rust/libraries/foo`.

Path bases can be either absolute or relative. Relative path bases are relative
to the parent directory of the configuration file that declared that path base.

If the name of path base used in a dependency is neither in the configuration
nor one of the built-in path base, then Cargo will raise an error.

#### Built-in path bases

Cargo provides implicit path bases that can be used without the need to specify
them in a `[path-bases]` table.

* `workspace` - If a project is [a workspace or workspace member](workspaces.md)
then this path base is defined as the parent directory of the root `Cargo.toml`
of the workspace.

If a built-in path base name is also declared in the configuration, then Cargo
will prefer the value in the configuration. The allows Cargo to add new built-in
path bases without compatibility issues (as existing uses will shadow the
built-in name).

# Stabilized and removed features

## Compile progress
Expand Down
30 changes: 16 additions & 14 deletions tests/testsuite/cargo/z_help/stdout.term.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions tests/testsuite/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3028,3 +3028,45 @@ foo v0.0.0 ([ROOT]/foo)

assert_eq!(p.read_file("Cargo.lock"), p.read_file("Cargo.lock.orig"));
}

#[cargo_test]
fn patch_with_base() {
let bar = project()
.at("bar")
.file("Cargo.toml", &basic_manifest("bar", "0.5.0"))
.file("src/lib.rs", "pub fn hello() {}")
.build();
Package::new("bar", "0.5.0").publish();

let p = project()
.file(
".cargo/config.toml",
&format!(
"[path-bases]\ntest = '{}'",
bar.root().parent().unwrap().display()
),
)
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.5.0"
authors = ["[email protected]"]
edition = "2018"
[dependencies]
bar = "0.5.0"
[patch.crates-io.bar]
path = 'bar'
base = 'test'
"#,
)
.file("src/lib.rs", "use bar::hello as _;")
.build();

p.cargo("build -v -Zpath-bases")
.masquerade_as_nightly_cargo(&["path-bases"])
.run();
}
Loading

0 comments on commit 0736fa6

Please sign in to comment.