Skip to content

Commit ffd37c4

Browse files
committed
Implement path-bases (RFC 3529) 2/n: cargo add support
RFC: rust-lang/rfcs#3529 Tracking Issue: #14355 This PR adds the `--base` option to `cargo add` to allow adding a path dependency with a path base.
1 parent 9e152bb commit ffd37c4

File tree

46 files changed

+559
-45
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+559
-45
lines changed

src/bin/cargo/commands/add.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ Example uses:
101101
.help("Filesystem path to local crate to add")
102102
.group("selected")
103103
.conflicts_with("git"),
104+
clap::Arg::new("base")
105+
.long("base")
106+
.action(ArgAction::Set)
107+
.value_name("BASE")
108+
.help("The path base to use when adding from a local crate (unstable).")
109+
.requires("path"),
104110
clap::Arg::new("git")
105111
.long("git")
106112
.action(ArgAction::Set)
@@ -224,6 +230,7 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
224230

225231
fn parse_dependencies(gctx: &GlobalContext, matches: &ArgMatches) -> CargoResult<Vec<DepOp>> {
226232
let path = matches.get_one::<String>("path");
233+
let base = matches.get_one::<String>("base");
227234
let git = matches.get_one::<String>("git");
228235
let branch = matches.get_one::<String>("branch");
229236
let rev = matches.get_one::<String>("rev");
@@ -329,6 +336,7 @@ fn parse_dependencies(gctx: &GlobalContext, matches: &ArgMatches) -> CargoResult
329336
public,
330337
registry: registry.clone(),
331338
path: path.map(String::from),
339+
base: base.map(String::from),
332340
git: git.map(String::from),
333341
branch: branch.map(String::from),
334342
rev: rev.map(String::from),

src/cargo/ops/cargo_add/mod.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::str::FromStr;
1212
use anyhow::Context as _;
1313
use cargo_util::paths;
1414
use cargo_util_schemas::core::PartialVersion;
15+
use cargo_util_schemas::manifest::PathBaseName;
1516
use cargo_util_schemas::manifest::RustVersion;
1617
use indexmap::IndexSet;
1718
use itertools::Itertools;
@@ -20,6 +21,7 @@ use toml_edit::Item as TomlItem;
2021
use crate::core::dependency::DepKind;
2122
use crate::core::registry::PackageRegistry;
2223
use crate::core::FeatureValue;
24+
use crate::core::Features;
2325
use crate::core::Package;
2426
use crate::core::Registry;
2527
use crate::core::Shell;
@@ -28,6 +30,7 @@ use crate::core::Workspace;
2830
use crate::sources::source::QueryKind;
2931
use crate::util::cache_lock::CacheLockMode;
3032
use crate::util::style;
33+
use crate::util::toml::lookup_path_base;
3134
use crate::util::toml_mut::dependency::Dependency;
3235
use crate::util::toml_mut::dependency::GitSource;
3336
use crate::util::toml_mut::dependency::MaybeWorkspace;
@@ -270,8 +273,11 @@ pub struct DepOp {
270273
/// Registry for looking up dependency version
271274
pub registry: Option<String>,
272275

273-
/// Git repo for dependency
276+
/// File system path for dependency
274277
pub path: Option<String>,
278+
/// Specify a named base for a path dependency
279+
pub base: Option<String>,
280+
275281
/// Git repo for dependency
276282
pub git: Option<String>,
277283
/// Specify an alternative git branch
@@ -331,10 +337,41 @@ fn resolve_dependency(
331337
};
332338
selected
333339
} else if let Some(raw_path) = &arg.path {
340+
let base_name_and_value = if let Some(base_name) = &arg.base {
341+
let workspace_root = ws.root_manifest().parent().unwrap().to_path_buf();
342+
let workspace_root = || Ok(&workspace_root);
343+
344+
let features = {
345+
let empty = Vec::new();
346+
let mut warnings = Vec::new();
347+
let cargo_features = spec
348+
.manifest()
349+
.original_toml()
350+
.cargo_features
351+
.as_ref()
352+
.unwrap_or(&empty);
353+
let features = Features::new(cargo_features, gctx, &mut warnings, true)?;
354+
if !warnings.is_empty() {
355+
for warning in warnings {
356+
gctx.shell().warn(warning)?;
357+
}
358+
}
359+
features
360+
};
361+
let base_value = lookup_path_base(
362+
&PathBaseName::new(base_name.clone())?,
363+
gctx,
364+
&workspace_root,
365+
&features,
366+
)?;
367+
Some((base_name.clone(), base_value))
368+
} else {
369+
None
370+
};
334371
let path = paths::normalize_path(&std::env::current_dir()?.join(raw_path));
335-
let src = PathSource::new(&path);
372+
let src = PathSource::new(path);
336373

337-
let selected = if let Some(crate_spec) = &crate_spec {
374+
let mut selected = if let Some(crate_spec) = &crate_spec {
338375
if let Some(v) = crate_spec.version_req() {
339376
// crate specifier includes a version (e.g. `docopt@0.8`)
340377
anyhow::bail!("cannot specify a path (`{raw_path}`) with a version (`{v}`).");
@@ -349,10 +386,15 @@ fn resolve_dependency(
349386
}
350387
selected
351388
} else {
352-
let mut source = crate::sources::PathSource::new(&path, src.source_id()?, gctx);
389+
let mut source = crate::sources::PathSource::new(&src.path, src.source_id()?, gctx);
353390
let package = source.root_package()?;
354391
Dependency::from(package.summary())
355392
};
393+
if let Some(selected_source) = selected.source.as_mut() {
394+
if let Source::Path(selected_source) = selected_source {
395+
selected_source.base_name_and_value = base_name_and_value;
396+
}
397+
}
356398
selected
357399
} else if let Some(crate_spec) = &crate_spec {
358400
crate_spec.to_dependency()?

src/cargo/util/toml_mut/dependency.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -412,11 +412,15 @@ impl Dependency {
412412
table.insert("version", src.version.as_str().into());
413413
}
414414
Some(Source::Path(src)) => {
415-
let relpath = path_field(crate_root, &src.path);
416415
if let Some(r) = src.version.as_deref() {
417416
table.insert("version", r.into());
418417
}
419-
table.insert("path", relpath.into());
418+
if let Some((base_name, base_value)) = src.base_name_and_value.as_ref() {
419+
table.insert("base", base_name.into());
420+
table.insert("path", path_field(base_value, &src.path).into());
421+
} else {
422+
table.insert("path", path_field(crate_root, &src.path).into());
423+
}
420424
}
421425
Some(Source::Git(src)) => {
422426
table.insert("git", src.git.as_str().into());
@@ -493,13 +497,18 @@ impl Dependency {
493497
Some(Source::Registry(src)) => {
494498
overwrite_value(table, "version", src.version.as_str());
495499

496-
for key in ["path", "git", "branch", "tag", "rev", "workspace"] {
500+
for key in ["path", "git", "branch", "tag", "rev", "workspace", "base"] {
497501
table.remove(key);
498502
}
499503
}
500504
Some(Source::Path(src)) => {
501-
let relpath = path_field(crate_root, &src.path);
502-
overwrite_value(table, "path", relpath);
505+
if let Some((base_name, base_value)) = src.base_name_and_value.as_ref() {
506+
overwrite_value(table, "base", base_name);
507+
overwrite_value(table, "path", path_field(base_value, &src.path));
508+
} else {
509+
table.remove("base");
510+
overwrite_value(table, "path", path_field(crate_root, &src.path));
511+
}
503512
if let Some(r) = src.version.as_deref() {
504513
overwrite_value(table, "version", r);
505514
} else {
@@ -533,7 +542,7 @@ impl Dependency {
533542
table.remove("version");
534543
}
535544

536-
for key in ["path", "workspace"] {
545+
for key in ["path", "workspace", "base"] {
537546
table.remove(key);
538547
}
539548
}
@@ -552,6 +561,7 @@ impl Dependency {
552561
"rev",
553562
"package",
554563
"default-features",
564+
"base",
555565
] {
556566
table.remove(key);
557567
}
@@ -812,6 +822,8 @@ impl std::fmt::Display for RegistrySource {
812822
pub struct PathSource {
813823
/// Local, absolute path.
814824
pub path: PathBuf,
825+
/// If using a named base, the base and relative path.
826+
pub base_name_and_value: Option<(String, PathBuf)>,
815827
/// Version requirement for when published.
816828
pub version: Option<String>,
817829
}
@@ -821,6 +833,7 @@ impl PathSource {
821833
pub fn new(path: impl Into<PathBuf>) -> Self {
822834
Self {
823835
path: path.into(),
836+
base_name_and_value: None,
824837
version: None,
825838
}
826839
}
@@ -843,7 +856,11 @@ impl PathSource {
843856

844857
impl std::fmt::Display for PathSource {
845858
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
846-
self.path.display().fmt(f)
859+
if let Some((base_name, _)) = &self.base_name_and_value {
860+
write!(f, "{{{base_name}}}/{}", self.path.display())
861+
} else {
862+
self.path.display().fmt(f)
863+
}
847864
}
848865
}
849866

src/doc/man/cargo-add.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ Specific commit to use when adding from git.
6363
[Filesystem path](../reference/specifying-dependencies.html#specifying-path-dependencies) to local crate to add.
6464
{{/option}}
6565

66+
{{#option "`--base` _base_" }}
67+
The [path base](../reference/unstable.html#path-bases) to use when adding a local crate.
68+
69+
[Unstable (nightly-only)](../reference/unstable.html#path-bases)
70+
{{/option}}
71+
6672
{{> options-registry }}
6773

6874
{{/options}}

src/doc/man/generated_txt/cargo-add.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ OPTIONS
5656
<https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#specifying-path-dependencies>
5757
to local crate to add.
5858

59+
--base base
60+
The path base
61+
<https://doc.rust-lang.org/cargo/reference/unstable.html#path-bases>
62+
to use when adding a local crate.
63+
64+
Unstable (nightly-only)
65+
<https://doc.rust-lang.org/cargo/reference/unstable.html#path-bases>
66+
5967
--registry registry
6068
Name of the registry to use. Registry names are defined in Cargo
6169
config files

src/doc/src/commands/cargo-add.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ dependency will be listed in the command's output.
5959
<dd class="option-desc"><a href="../reference/specifying-dependencies.html#specifying-path-dependencies">Filesystem path</a> to local crate to add.</dd>
6060

6161

62+
<dt class="option-term" id="option-cargo-add---base"><a class="option-anchor" href="#option-cargo-add---base"></a><code>--base</code> <em>base</em></dt>
63+
<dd class="option-desc">The <a href="../reference/unstable.html#path-bases">path base</a> to use when adding a local crate.</p>
64+
<p><a href="../reference/unstable.html#path-bases">Unstable (nightly-only)</a></dd>
65+
66+
6267
<dt class="option-term" id="option-cargo-add---registry"><a class="option-anchor" href="#option-cargo-add---registry"></a><code>--registry</code> <em>registry</em></dt>
6368
<dd class="option-desc">Name of the registry to use. Registry names are defined in <a href="../reference/config.html">Cargo config
6469
files</a>. If not specified, the default registry is used,

src/etc/man/cargo-add.1

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ Specific commit to use when adding from git.
7474
\fIFilesystem path\fR <https://doc.rust\-lang.org/cargo/reference/specifying\-dependencies.html#specifying\-path\-dependencies> to local crate to add.
7575
.RE
7676
.sp
77+
\fB\-\-base\fR \fIbase\fR
78+
.RS 4
79+
The \fIpath base\fR <https://doc.rust\-lang.org/cargo/reference/unstable.html#path\-bases> to use when adding a local crate.
80+
.sp
81+
\fIUnstable (nightly\-only)\fR <https://doc.rust\-lang.org/cargo/reference/unstable.html#path\-bases>
82+
.RE
83+
.sp
7784
\fB\-\-registry\fR \fIregistry\fR
7885
.RS 4
7986
Name of the registry to use. Registry names are defined in \fICargo config

tests/testsuite/cargo_add/help/stdout.term.svg

Lines changed: 40 additions & 34 deletions
Loading

0 commit comments

Comments
 (0)