From 036a03709b6554b6f8023ffe5f3f20cd45f85f45 Mon Sep 17 00:00:00 2001 From: LuuuX Date: Wed, 29 Nov 2023 11:42:50 +0800 Subject: [PATCH] Fix issue-11010 --- src/cargo/ops/cargo_add/mod.rs | 35 ++++++++++++++++++ src/cargo/util/toml_mut/manifest.rs | 2 +- .../change_rename_target/out/Cargo.toml | 3 ++ .../out/primary/Cargo.toml | 3 ++ .../cargo_add/optional/out/Cargo.toml | 4 +++ .../out/primary/Cargo.toml | 3 ++ .../out/primary/Cargo.toml | 3 ++ .../overwrite_name_noop/out/Cargo.toml | 3 ++ .../out/Cargo.toml | 4 +++ .../overwrite_optional/out/Cargo.toml | 4 +++ .../in/Cargo.toml | 11 ++++++ .../in/src/lib.rs | 0 .../overwrite_optional_with_optional/mod.rs | 36 +++++++++++++++++++ .../out/Cargo.toml | 11 ++++++ .../stderr.log | 3 ++ .../stdout.log | 0 .../overwrite_path_noop/out/Cargo.toml | 3 ++ .../out/primary/Cargo.toml | 3 ++ .../out/Cargo.toml | 3 ++ .../overwrite_version_with_git/out/Cargo.toml | 3 ++ .../out/primary/Cargo.toml | 3 ++ 21 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_optional/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_optional/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_optional/mod.rs create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_optional/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_optional/stderr.log create mode 100644 tests/testsuite/cargo_add/overwrite_optional_with_optional/stdout.log diff --git a/src/cargo/ops/cargo_add/mod.rs b/src/cargo/ops/cargo_add/mod.rs index 5afc8d0c9d8..97d7b4d7524 100644 --- a/src/cargo/ops/cargo_add/mod.rs +++ b/src/cargo/ops/cargo_add/mod.rs @@ -7,6 +7,7 @@ use std::collections::BTreeSet; use std::collections::VecDeque; use std::fmt::Write; use std::path::Path; +use std::str::FromStr; use anyhow::Context as _; use cargo_util::paths; @@ -196,6 +197,20 @@ pub fn add(workspace: &Workspace<'_>, options: &AddOptions<'_>) -> CargoResult<( print_dep_table_msg(&mut options.config.shell(), &dep)?; manifest.insert_into_table(&dep_table, &dep)?; + if dep.optional == Some(true) { + let is_namespaced_features_supported = + check_rust_version_for_optional_dependency(options.spec.rust_version())?; + if is_namespaced_features_supported { + let dep_key = dep.toml_key(); + if !manifest.is_explicit_dep_activation(dep_key) { + let table = manifest.get_table_mut(&[String::from("features")])?; + let dep_name = dep.rename.as_deref().unwrap_or(&dep.name); + let new_feature: toml_edit::Value = + [format!("dep:{dep_name}")].iter().collect(); + table[dep_key] = toml_edit::value(new_feature); + } + } + } manifest.gc_dep(dep.toml_key()); } @@ -469,6 +484,26 @@ fn check_invalid_ws_keys(toml_key: &str, arg: &DepOp) -> CargoResult<()> { Ok(()) } +/// When the `--optional` option is added using `cargo add`, we need to +/// check the current rust-version. As the `dep:` syntax is only avaliable +/// starting with Rust 1.60.0 +/// +/// `true` means that the rust-version is None or the rust-version is higher +/// than the version needed. +/// +/// Note: Previous versions can only use the implicit feature name. +fn check_rust_version_for_optional_dependency( + rust_version: Option<&RustVersion>, +) -> CargoResult { + match rust_version { + Some(version) => { + let syntax_support_version = RustVersion::from_str("1.60.0")?; + Ok(&syntax_support_version <= version) + } + None => Ok(true), + } +} + /// Provide the existing dependency for the target table /// /// If it doesn't exist but exists in another table, let's use that as most likely users diff --git a/src/cargo/util/toml_mut/manifest.rs b/src/cargo/util/toml_mut/manifest.rs index e859af2153a..3e3b4e69ae3 100644 --- a/src/cargo/util/toml_mut/manifest.rs +++ b/src/cargo/util/toml_mut/manifest.rs @@ -420,7 +420,7 @@ impl LocalManifest { } } - fn is_explicit_dep_activation(&self, dep_key: &str) -> bool { + pub fn is_explicit_dep_activation(&self, dep_key: &str) -> bool { if let Some(toml_edit::Item::Table(feature_table)) = self.data.as_table().get("features") { for values in feature_table .iter() diff --git a/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml b/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml index 70cd318268c..bc29fac8ebd 100644 --- a/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml +++ b/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml @@ -6,3 +6,6 @@ version = "0.0.0" [dependencies] some-package = { package = "my-package2", version = "99999.0.0", optional = true } + +[features] +some-package = ["dep:some-package"] diff --git a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml index 6dd7fb6d65d..38ff36eb38b 100644 --- a/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml +++ b/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml @@ -4,3 +4,6 @@ version = "0.0.0" [dependencies] foo = { workspace = true, optional = true } + +[features] +foo = ["dep:foo"] diff --git a/tests/testsuite/cargo_add/optional/out/Cargo.toml b/tests/testsuite/cargo_add/optional/out/Cargo.toml index eda5445c50f..5ed3b820ef4 100644 --- a/tests/testsuite/cargo_add/optional/out/Cargo.toml +++ b/tests/testsuite/cargo_add/optional/out/Cargo.toml @@ -7,3 +7,7 @@ version = "0.0.0" [dependencies] my-package1 = { version = "99999.0.0", optional = true } my-package2 = { version = "0.4.1", optional = true } + +[features] +my-package1 = ["dep:my-package1"] +my-package2 = ["dep:my-package2"] diff --git a/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml index ad120548123..27e6e175d6f 100644 --- a/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml +++ b/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml @@ -6,3 +6,6 @@ version = "0.0.0" [dependencies] cargo-list-test-fixture-dependency = { optional = true, path = "../dependency", version = "0.0.0" } + +[features] +cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"] diff --git a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml index 6dd7fb6d65d..38ff36eb38b 100644 --- a/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml +++ b/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml @@ -4,3 +4,6 @@ version = "0.0.0" [dependencies] foo = { workspace = true, optional = true } + +[features] +foo = ["dep:foo"] diff --git a/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml index bbaf4f5521c..7172521917c 100644 --- a/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml +++ b/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml @@ -7,3 +7,6 @@ version = "0.0.0" [dependencies] your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" } + +[features] +your-face = ["dep:your-face"] diff --git a/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml index eda5445c50f..5ed3b820ef4 100644 --- a/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml +++ b/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml @@ -7,3 +7,7 @@ version = "0.0.0" [dependencies] my-package1 = { version = "99999.0.0", optional = true } my-package2 = { version = "0.4.1", optional = true } + +[features] +my-package1 = ["dep:my-package1"] +my-package2 = ["dep:my-package2"] diff --git a/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml index eda5445c50f..5ed3b820ef4 100644 --- a/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml +++ b/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml @@ -7,3 +7,7 @@ version = "0.0.0" [dependencies] my-package1 = { version = "99999.0.0", optional = true } my-package2 = { version = "0.4.1", optional = true } + +[features] +my-package1 = ["dep:my-package1"] +my-package2 = ["dep:my-package2"] diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/Cargo.toml new file mode 100644 index 00000000000..e446eca38e0 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/Cargo.toml @@ -0,0 +1,11 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", optional = true } + +[features] +default = ["dep:my-package1"] \ No newline at end of file diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/src/lib.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_optional/mod.rs b/tests/testsuite/cargo_add/overwrite_optional_with_optional/mod.rs new file mode 100644 index 00000000000..3b498c326de --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional_with_optional/mod.rs @@ -0,0 +1,36 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + cargo_test_support::registry::init(); + for ver in [ + "0.1.1+my-package", + "0.2.0+my-package", + "0.2.3+my-package", + "0.4.1+my-package", + "20.0.0+my-package", + "99999.0.0+my-package", + "99999.0.0-alpha.1+my-package", + ] { + cargo_test_support::registry::Package::new("my-package1", ver).publish(); + } + + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("my-package1 --optional") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_optional/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_optional_with_optional/out/Cargo.toml new file mode 100644 index 00000000000..e68fcdb3cc3 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional_with_optional/out/Cargo.toml @@ -0,0 +1,11 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies] +my-package1 = { version = "99999.0.0", optional = true } + +[features] +default = ["dep:my-package1"] diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_optional/stderr.log b/tests/testsuite/cargo_add/overwrite_optional_with_optional/stderr.log new file mode 100644 index 00000000000..8cf4812cf17 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_optional_with_optional/stderr.log @@ -0,0 +1,3 @@ + Updating `dummy-registry` index + Adding my-package1 v99999.0.0 to optional dependencies. + Adding my-package2 v0.4.1 to optional dependencies. diff --git a/tests/testsuite/cargo_add/overwrite_optional_with_optional/stdout.log b/tests/testsuite/cargo_add/overwrite_optional_with_optional/stdout.log new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml index bbaf4f5521c..7172521917c 100644 --- a/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml +++ b/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml @@ -7,3 +7,6 @@ version = "0.0.0" [dependencies] your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" } + +[features] +your-face = ["dep:your-face"] diff --git a/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml index a20f2095db9..c45f794912e 100644 --- a/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml +++ b/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml @@ -6,3 +6,6 @@ version = "0.0.0" [dependencies] cargo-list-test-fixture-dependency = { optional = true, version = "20.0" } + +[features] +cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"] diff --git a/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml index 4502292454a..0217d417602 100644 --- a/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml +++ b/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml @@ -6,3 +6,6 @@ version = "0.0.0" [dependencies] a1 = { package = "versioned-package", version = "0.1.1", optional = true } + +[features] +a1 = ["dep:a1"] diff --git a/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml index 26001402414..a3eabd06559 100644 --- a/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml +++ b/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml @@ -6,3 +6,6 @@ version = "0.0.0" [dependencies] versioned-package = { version = "0.3.0", optional = true, git = "[ROOTURL]/versioned-package" } + +[features] +versioned-package = ["dep:versioned-package"] diff --git a/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml b/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml index 07253670afe..bd460a11b21 100644 --- a/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml +++ b/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml @@ -6,3 +6,6 @@ version = "0.0.0" [dependencies] cargo-list-test-fixture-dependency = { version = "0.0.0", optional = true, path = "../dependency" } + +[features] +cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]