Skip to content

Commit 78109c0

Browse files
committed
Auto merge of #13071 - LuuuXXX:issue-11010, r=epage
Have cargo add --optional <dep> create a <dep> = "dep:<dep> feature ### What does this PR try to resolve? `cargo add --optional <dep>` would create a `<dep> = "dep:<dep>` feature iff - `rust-version` is unset or is new enough for the syntax - `dep:<dep>` doesn't already exist Fixes #11010 ### How should we test and review this PR? As the `dep:` syntax is only available starting with Rust 1.60. this pr maintains the previous usage convention in the earlier version. run ```shell cargo add --optional <dep> ``` with different rust-version to verify.
2 parents 60e65ee + 036a037 commit 78109c0

File tree

21 files changed

+139
-1
lines changed

21 files changed

+139
-1
lines changed

src/cargo/ops/cargo_add/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::collections::BTreeSet;
77
use std::collections::VecDeque;
88
use std::fmt::Write;
99
use std::path::Path;
10+
use std::str::FromStr;
1011

1112
use anyhow::Context as _;
1213
use cargo_util::paths;
@@ -196,6 +197,20 @@ pub fn add(workspace: &Workspace<'_>, options: &AddOptions<'_>) -> CargoResult<(
196197
print_dep_table_msg(&mut options.config.shell(), &dep)?;
197198

198199
manifest.insert_into_table(&dep_table, &dep)?;
200+
if dep.optional == Some(true) {
201+
let is_namespaced_features_supported =
202+
check_rust_version_for_optional_dependency(options.spec.rust_version())?;
203+
if is_namespaced_features_supported {
204+
let dep_key = dep.toml_key();
205+
if !manifest.is_explicit_dep_activation(dep_key) {
206+
let table = manifest.get_table_mut(&[String::from("features")])?;
207+
let dep_name = dep.rename.as_deref().unwrap_or(&dep.name);
208+
let new_feature: toml_edit::Value =
209+
[format!("dep:{dep_name}")].iter().collect();
210+
table[dep_key] = toml_edit::value(new_feature);
211+
}
212+
}
213+
}
199214
manifest.gc_dep(dep.toml_key());
200215
}
201216

@@ -472,6 +487,26 @@ fn check_invalid_ws_keys(toml_key: &str, arg: &DepOp) -> CargoResult<()> {
472487
Ok(())
473488
}
474489

490+
/// When the `--optional` option is added using `cargo add`, we need to
491+
/// check the current rust-version. As the `dep:` syntax is only avaliable
492+
/// starting with Rust 1.60.0
493+
///
494+
/// `true` means that the rust-version is None or the rust-version is higher
495+
/// than the version needed.
496+
///
497+
/// Note: Previous versions can only use the implicit feature name.
498+
fn check_rust_version_for_optional_dependency(
499+
rust_version: Option<&RustVersion>,
500+
) -> CargoResult<bool> {
501+
match rust_version {
502+
Some(version) => {
503+
let syntax_support_version = RustVersion::from_str("1.60.0")?;
504+
Ok(&syntax_support_version <= version)
505+
}
506+
None => Ok(true),
507+
}
508+
}
509+
475510
/// Provide the existing dependency for the target table
476511
///
477512
/// If it doesn't exist but exists in another table, let's use that as most likely users

src/cargo/util/toml_mut/manifest.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ impl LocalManifest {
420420
}
421421
}
422422

423-
fn is_explicit_dep_activation(&self, dep_key: &str) -> bool {
423+
pub fn is_explicit_dep_activation(&self, dep_key: &str) -> bool {
424424
if let Some(toml_edit::Item::Table(feature_table)) = self.data.as_table().get("features") {
425425
for values in feature_table
426426
.iter()

tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
some-package = { package = "my-package2", version = "99999.0.0", optional = true }
9+
10+
[features]
11+
some-package = ["dep:some-package"]

tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ version = "0.0.0"
44

55
[dependencies]
66
foo = { workspace = true, optional = true }
7+
8+
[features]
9+
foo = ["dep:foo"]

tests/testsuite/cargo_add/optional/out/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ version = "0.0.0"
77
[dependencies]
88
my-package1 = { version = "99999.0.0", optional = true }
99
my-package2 = { version = "0.4.1", optional = true }
10+
11+
[features]
12+
my-package1 = ["dep:my-package1"]
13+
my-package2 = ["dep:my-package2"]

tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
cargo-list-test-fixture-dependency = { optional = true, path = "../dependency", version = "0.0.0" }
9+
10+
[features]
11+
cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]

tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ version = "0.0.0"
44

55
[dependencies]
66
foo = { workspace = true, optional = true }
7+
8+
[features]
9+
foo = ["dep:foo"]

tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ version = "0.0.0"
77

88
[dependencies]
99
your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" }
10+
11+
[features]
12+
your-face = ["dep:your-face"]

tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ version = "0.0.0"
77
[dependencies]
88
my-package1 = { version = "99999.0.0", optional = true }
99
my-package2 = { version = "0.4.1", optional = true }
10+
11+
[features]
12+
my-package1 = ["dep:my-package1"]
13+
my-package2 = ["dep:my-package2"]

tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ version = "0.0.0"
77
[dependencies]
88
my-package1 = { version = "99999.0.0", optional = true }
99
my-package2 = { version = "0.4.1", optional = true }
10+
11+
[features]
12+
my-package1 = ["dep:my-package1"]
13+
my-package2 = ["dep:my-package2"]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[workspace]
2+
3+
[package]
4+
name = "cargo-list-test-fixture"
5+
version = "0.0.0"
6+
7+
[dependencies]
8+
my-package1 = { version = "99999.0.0", optional = true }
9+
10+
[features]
11+
default = ["dep:my-package1"]

tests/testsuite/cargo_add/overwrite_optional_with_optional/in/src/lib.rs

Whitespace-only changes.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use cargo_test_support::compare::assert_ui;
2+
use cargo_test_support::prelude::*;
3+
use cargo_test_support::Project;
4+
5+
use cargo_test_support::curr_dir;
6+
7+
#[cargo_test]
8+
fn case() {
9+
cargo_test_support::registry::init();
10+
for ver in [
11+
"0.1.1+my-package",
12+
"0.2.0+my-package",
13+
"0.2.3+my-package",
14+
"0.4.1+my-package",
15+
"20.0.0+my-package",
16+
"99999.0.0+my-package",
17+
"99999.0.0-alpha.1+my-package",
18+
] {
19+
cargo_test_support::registry::Package::new("my-package1", ver).publish();
20+
}
21+
22+
let project = Project::from_template(curr_dir!().join("in"));
23+
let project_root = project.root();
24+
let cwd = &project_root;
25+
26+
snapbox::cmd::Command::cargo_ui()
27+
.arg("add")
28+
.arg_line("my-package1 --optional")
29+
.current_dir(cwd)
30+
.assert()
31+
.success()
32+
.stdout_matches_path(curr_dir!().join("stdout.log"))
33+
.stderr_matches_path(curr_dir!().join("stderr.log"));
34+
35+
assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
36+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[workspace]
2+
3+
[package]
4+
name = "cargo-list-test-fixture"
5+
version = "0.0.0"
6+
7+
[dependencies]
8+
my-package1 = { version = "99999.0.0", optional = true }
9+
10+
[features]
11+
default = ["dep:my-package1"]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Updating `dummy-registry` index
2+
Adding my-package1 v99999.0.0 to optional dependencies.
3+
Adding my-package2 v0.4.1 to optional dependencies.

tests/testsuite/cargo_add/overwrite_optional_with_optional/stdout.log

Whitespace-only changes.

tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@ version = "0.0.0"
77

88
[dependencies]
99
your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" }
10+
11+
[features]
12+
your-face = ["dep:your-face"]

tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
cargo-list-test-fixture-dependency = { optional = true, version = "20.0" }
9+
10+
[features]
11+
cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]

tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
a1 = { package = "versioned-package", version = "0.1.1", optional = true }
9+
10+
[features]
11+
a1 = ["dep:a1"]

tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
versioned-package = { version = "0.3.0", optional = true, git = "[ROOTURL]/versioned-package" }
9+
10+
[features]
11+
versioned-package = ["dep:versioned-package"]

tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ version = "0.0.0"
66

77
[dependencies]
88
cargo-list-test-fixture-dependency = { version = "0.0.0", optional = true, path = "../dependency" }
9+
10+
[features]
11+
cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]

0 commit comments

Comments
 (0)