Skip to content

Commit 96a8229

Browse files
committed
Clean up workspace dependencies after cargo remove
1 parent b592ba4 commit 96a8229

File tree

12 files changed

+150
-2
lines changed

12 files changed

+150
-2
lines changed

src/bin/cargo/commands/remove.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
8282
section,
8383
dry_run,
8484
};
85-
remove(&options)?;
85+
remove(&workspace, &options)?;
8686

8787
if !dry_run {
8888
// Reload the workspace since we've changed dependencies

src/cargo/ops/cargo_remove.rs

+66-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Core of cargo-remove command
22
33
use crate::core::Package;
4+
use crate::core::Workspace;
45
use crate::util::toml_mut::manifest::DepTable;
56
use crate::util::toml_mut::manifest::LocalManifest;
67
use crate::CargoResult;
@@ -22,7 +23,7 @@ pub struct RemoveOptions<'a> {
2223
}
2324

2425
/// Remove dependencies from a manifest
25-
pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
26+
pub fn remove(workspace: &Workspace<'_>, options: &RemoveOptions<'_>) -> CargoResult<()> {
2627
let dep_table = options
2728
.section
2829
.to_table()
@@ -33,7 +34,17 @@ pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
3334
let manifest_path = options.spec.manifest_path().to_path_buf();
3435
let mut manifest = LocalManifest::try_new(&manifest_path)?;
3536

37+
let mut workspace_manifest = if workspace.is_virtual() {
38+
let data = cargo_util::paths::read(workspace.root_manifest())?;
39+
let manifest: toml_edit::Document = data.parse()?;
40+
Some(manifest)
41+
} else {
42+
None
43+
};
44+
3645
for dep in &options.dependencies {
46+
let is_workspace_dep = dependency_is_workspace(dep, &dep_table, &manifest);
47+
3748
let section = if dep_table.len() >= 3 {
3849
format!("{} for target `{}`", &dep_table[2], &dep_table[1])
3950
} else {
@@ -50,6 +61,21 @@ pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
5061
// crate, then we need to drop any explicitly activated features on
5162
// that crate.
5263
manifest.gc_dep(dep);
64+
65+
// If this was the last instance of a workspace dependency, remove it
66+
// from the workspace dependencies.
67+
if is_workspace_dep {
68+
if let Some(workspace_manifest) = &mut workspace_manifest {
69+
if !dependency_in_workspace(dep, options.spec, workspace) {
70+
remove_dependency_from_workspace(dep, workspace_manifest);
71+
72+
options
73+
.config
74+
.shell()
75+
.status("Removing", format!("{dep} from workspace dependencies"))?;
76+
}
77+
}
78+
}
5379
}
5480

5581
if options.dry_run {
@@ -59,7 +85,46 @@ pub fn remove(options: &RemoveOptions<'_>) -> CargoResult<()> {
5985
.warn("aborting remove due to dry run")?;
6086
} else {
6187
manifest.write()?;
88+
89+
if let Some(workspace_manifest) = workspace_manifest {
90+
cargo_util::paths::write(
91+
workspace.root_manifest(),
92+
workspace_manifest.to_string().as_bytes(),
93+
)?;
94+
}
6295
}
6396

6497
Ok(())
6598
}
99+
100+
/// Get whether or not a dependency is marked as `workspace`.
101+
fn dependency_is_workspace(dep: &str, dep_table: &[String], manifest: &LocalManifest) -> bool {
102+
if let Ok(toml_edit::Item::Table(table)) = manifest.get_table(dep_table) {
103+
let value = table.get(dep).and_then(|i| i.get("workspace"));
104+
if let Some(toml_edit::Item::Value(value)) = value {
105+
return value.as_bool() == Some(true);
106+
}
107+
}
108+
109+
false
110+
}
111+
112+
/// Get whether or not a dependency is depended upon in a workspace.
113+
fn dependency_in_workspace(dep: &str, exclude: &Package, workspace: &Workspace<'_>) -> bool {
114+
workspace.members().filter(|p| *p != exclude).any(|p| {
115+
p.dependencies()
116+
.iter()
117+
.any(|d| d.package_name().as_str() == dep)
118+
})
119+
}
120+
121+
/// Remove a dependency from a virtual workspace.
122+
fn remove_dependency_from_workspace(dep: &str, virtual_manifest: &mut toml_edit::Document) {
123+
if let Some(toml_edit::Item::Table(table)) = virtual_manifest
124+
.get_mut("workspace")
125+
.and_then(|t| t.get_mut("dependencies"))
126+
{
127+
table.set_implicit(true);
128+
table.remove(dep);
129+
}
130+
}

tests/testsuite/cargo_remove/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod target;
2222
mod target_build;
2323
mod target_dev;
2424
mod update_lock_file;
25+
mod workspace;
2526

2627
fn init_registry() {
2728
cargo_test_support::registry::init();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[workspace]
2+
members = [ "my-package" ]
3+
4+
[workspace.dependencies]
5+
semver = "0.1.0"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "my-package"
3+
version = "0.1.0"
4+
5+
[[bin]]
6+
name = "main"
7+
path = "src/main.rs"
8+
9+
[build-dependencies]
10+
semver = { workspace = true }
11+
12+
[dependencies]
13+
docopt = "0.6"
14+
rustc-serialize = "0.4"
15+
semver = "0.1"
16+
toml = "0.1"
17+
clippy = "0.4"
18+
19+
[dev-dependencies]
20+
regex = "0.1.1"
21+
serde = "1.0.90"
22+
23+
[features]
24+
std = ["serde/std", "semver/std"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use cargo_test_support::compare::assert_ui;
2+
use cargo_test_support::curr_dir;
3+
use cargo_test_support::CargoCommand;
4+
use cargo_test_support::Project;
5+
6+
use crate::cargo_remove::init_registry;
7+
8+
#[cargo_test]
9+
fn case() {
10+
init_registry();
11+
let project = Project::from_template(curr_dir!().join("in"));
12+
let project_root = project.root();
13+
let cwd = &project_root;
14+
15+
snapbox::cmd::Command::cargo_ui()
16+
.arg("remove")
17+
.args(["--package", "my-package", "--build", "semver"])
18+
.current_dir(cwd)
19+
.assert()
20+
.success()
21+
.stdout_matches_path(curr_dir!().join("stdout.log"))
22+
.stderr_matches_path(curr_dir!().join("stderr.log"));
23+
24+
assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[workspace]
2+
members = [ "my-package" ]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "my-package"
3+
version = "0.1.0"
4+
5+
[[bin]]
6+
name = "main"
7+
path = "src/main.rs"
8+
9+
[dependencies]
10+
docopt = "0.6"
11+
rustc-serialize = "0.4"
12+
semver = "0.1"
13+
toml = "0.1"
14+
clippy = "0.4"
15+
16+
[dev-dependencies]
17+
regex = "0.1.1"
18+
serde = "1.0.90"
19+
20+
[features]
21+
std = ["serde/std", "semver/std"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Removing semver from build-dependencies
2+
Removing semver from workspace dependencies
3+
Updating `dummy-registry` index

tests/testsuite/cargo_remove/workspace/stdout.log

Whitespace-only changes.

0 commit comments

Comments
 (0)