Skip to content

Commit fb5d0b4

Browse files
committed
cargo-run-deps: Refactor identifying eligible deps
Relates to #2267.
1 parent f5114a4 commit fb5d0b4

File tree

4 files changed

+121
-84
lines changed

4 files changed

+121
-84
lines changed

src/bin/cargo/commands/run.rs

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -104,39 +104,22 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
104104
}
105105

106106
fn get_default_runs(ws: &Workspace<'_>, compile_opts: &CompileOptions) -> CargoResult<Vec<String>> {
107-
const ERROR: &'static str =
108-
"`cargo run` cannot find pkgid either in the workspace or among the workspace dependencies";
107+
const ERROR_NOTFOUND: &'static str = "`cargo run` cannot find pkgid either in the workspace or among direct build or development dependencies";
108+
let matching_dependencies = cargo::ops::packages_eligible_to_run(ws, &compile_opts.spec);
109+
match matching_dependencies {
110+
Ok(packages) => {
111+
if packages.is_empty() {
112+
anyhow::bail!(ERROR_NOTFOUND);
113+
}
109114

110-
let workspace_packages = compile_opts.spec.get_packages(ws);
111-
112-
let default_runs = if let Ok(packages) = workspace_packages {
113-
// Package is workspace member
114-
packages
115-
.iter()
116-
.filter_map(|pkg| pkg.manifest().default_run())
117-
.map(str::to_owned)
118-
.collect()
119-
} else if let Packages::Packages(ref pkg_names) = compile_opts.spec {
120-
// Search dependencies
121-
let (package_set, resolver) = ops::resolve_ws(ws)?;
122-
let deps: Vec<_> = pkg_names
123-
.iter()
124-
.flat_map(|name| resolver.query(name))
125-
.collect();
126-
127-
if deps.is_empty() {
128-
anyhow::bail!(ERROR);
115+
let default_runs = packages
116+
.into_iter()
117+
.filter_map(|pkg| pkg.manifest().default_run().map(|s| s.to_owned()))
118+
.collect();
119+
Ok(default_runs)
129120
}
130-
131-
package_set
132-
.get_many(deps)?
133-
.iter()
134-
.filter_map(|pkg| pkg.manifest().default_run())
135-
.map(str::to_owned)
136-
.collect()
137-
} else {
138-
anyhow::bail!(ERROR);
139-
};
140-
141-
Ok(default_runs)
121+
Err(_) => {
122+
anyhow::bail!(ERROR_NOTFOUND);
123+
}
124+
}
142125
}

src/cargo/ops/cargo_run.rs

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
use std::collections::HashSet;
12
use std::ffi::OsString;
23
use std::iter;
34
use std::path::Path;
45

56
use crate::core::compiler::UnitOutput;
6-
use crate::core::{TargetKind, Workspace};
7-
use crate::ops;
7+
use crate::core::dependency::DepKind;
8+
use crate::core::resolver::Resolve;
9+
use crate::core::{Package, PackageIdSpec, PackageSet, TargetKind, Workspace};
10+
use crate::ops::{self, Packages};
811
use crate::util::CargoResult;
912

1013
pub fn run(
@@ -20,19 +23,10 @@ pub fn run(
2023

2124
// We compute the `bins` here *just for diagnosis*. The actual set of
2225
// packages to be run is determined by the `ops::compile` call below.
23-
let (package_set, resolver) = ops::resolve_ws(ws)?;
24-
let packages = if let ops::Packages::Packages(ref pkg_names) = options.spec {
25-
let pkgids: Vec<_> = pkg_names
26-
.iter()
27-
.flat_map(|name| resolver.query(name))
28-
.collect();
29-
package_set.get_many(pkgids)?
30-
} else {
31-
options.spec.get_packages(ws)?
32-
};
26+
let packages = packages_eligible_to_run(ws, &options.spec)?;
3327

3428
let bins: Vec<_> = packages
35-
.into_iter()
29+
.iter()
3630
.flat_map(|pkg| {
3731
iter::repeat(pkg).zip(pkg.manifest().targets().iter().filter(|target| {
3832
!target.is_lib()
@@ -101,11 +95,51 @@ pub fn run(
10195
Ok(path) => path.to_path_buf(),
10296
Err(_) => path.to_path_buf(),
10397
};
104-
let pkg = bins[0].0;
98+
let pkg = &bins[0].0;
10599
let mut process = compile.target_process(exe, unit.kind, pkg, *script_meta)?;
106100
process.args(args).cwd(config.cwd());
107101

108102
config.shell().status("Running", process.to_string())?;
109103

110104
process.exec_replace()
111105
}
106+
107+
pub fn packages_eligible_to_run<'a>(
108+
ws: &Workspace<'a>,
109+
request: &Packages,
110+
) -> CargoResult<Vec<Package>> {
111+
let matching_dependencies = if let ops::Packages::Packages(ref pkg_names) = request {
112+
let specs: HashSet<_> = pkg_names
113+
.into_iter()
114+
.flat_map(|s| PackageIdSpec::parse(s))
115+
.collect();
116+
117+
let (package_set, resolver): (PackageSet<'a>, Resolve) = ops::resolve_ws(ws)?;
118+
119+
// Restrict all direct dependencies only to build and development ones.
120+
// Cargo wouldn't be able to run anything after installation, so
121+
// normal dependencies are out.
122+
let direct_dependencies: Vec<_> = ws
123+
.members()
124+
.flat_map(|pkg| resolver.deps(pkg.package_id()))
125+
.filter(|(_, manifest_deps)| {
126+
manifest_deps.into_iter().any(|dep| match dep.kind() {
127+
DepKind::Development | DepKind::Build => true,
128+
DepKind::Normal => false,
129+
})
130+
})
131+
.collect();
132+
133+
specs.into_iter().filter_map(|pkgidspec|
134+
// Either a workspace match…
135+
ws.members().find(|pkg| pkgidspec.matches(pkg.package_id()))
136+
.or_else(|| { // …or a direct dependency as fallback
137+
let maybe_dep = direct_dependencies.iter().find(|(dep_pkgid, _)| pkgidspec.matches(*dep_pkgid));
138+
maybe_dep.map(|(dep_pkgid, _)| package_set.get_one(*dep_pkgid).unwrap())
139+
})).cloned().collect()
140+
} else {
141+
request.get_packages(ws)?.into_iter().cloned().collect()
142+
};
143+
144+
Ok(matching_dependencies)
145+
}

src/cargo/ops/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub use self::cargo_output_metadata::{output_metadata, ExportInfo, OutputMetadat
1616
pub use self::cargo_package::{package, package_one, PackageOpts};
1717
pub use self::cargo_pkgid::pkgid;
1818
pub use self::cargo_read_manifest::{read_package, read_packages};
19-
pub use self::cargo_run::run;
19+
pub use self::cargo_run::{packages_eligible_to_run, run};
2020
pub use self::cargo_test::{run_benches, run_tests, TestOptions};
2121
pub use self::cargo_uninstall::uninstall;
2222
pub use self::fix::{fix, fix_maybe_exec_rustc, FixOptions};

tests/testsuite/run.rs

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,8 +1142,10 @@ fn run_multiple_packages() {
11421142
11431143
[workspace]
11441144
1145-
[dependencies]
1146-
d1 = { path = "d1" }
1145+
[build-dependencies]
1146+
b1 = { path = "b1" }
1147+
1148+
[dev-dependencies]
11471149
d2 = { path = "d2" }
11481150
d3 = { path = "../d3" } # outside of the workspace
11491151
@@ -1152,9 +1154,9 @@ fn run_multiple_packages() {
11521154
"#,
11531155
)
11541156
.file("foo/src/foo.rs", "fn main() { println!(\"foo\"); }")
1155-
.file("foo/d1/Cargo.toml", &basic_bin_manifest("d1"))
1156-
.file("foo/d1/src/lib.rs", "")
1157-
.file("foo/d1/src/main.rs", "fn main() { println!(\"d1\"); }")
1157+
.file("foo/b1/Cargo.toml", &basic_bin_manifest("b1"))
1158+
.file("foo/b1/src/lib.rs", "")
1159+
.file("foo/b1/src/main.rs", "fn main() { println!(\"b1\"); }")
11581160
.file("foo/d2/Cargo.toml", &basic_bin_manifest("d2"))
11591161
.file("foo/d2/src/main.rs", "fn main() { println!(\"d2\"); }")
11601162
.file("d3/Cargo.toml", &basic_bin_manifest("d3"))
@@ -1167,7 +1169,7 @@ fn run_multiple_packages() {
11671169
process_builder
11681170
};
11691171

1170-
cargo().arg("-p").arg("d1").with_stdout("d1").run();
1172+
cargo().arg("-p").arg("b1").with_stdout("b1").run();
11711173

11721174
cargo()
11731175
.arg("-p")
@@ -1179,7 +1181,7 @@ fn run_multiple_packages() {
11791181

11801182
cargo().with_stdout("foo").run();
11811183

1182-
cargo().arg("-p").arg("d1").arg("-p").arg("d2")
1184+
cargo().arg("-p").arg("b1").arg("-p").arg("d2")
11831185
.with_status(1)
11841186
.with_stderr_contains("error: The argument '--package <SPEC>' was provided more than once, but cannot be used multiple times").run();
11851187

@@ -1197,37 +1199,47 @@ fn run_multiple_packages() {
11971199

11981200
#[cargo_test]
11991201
fn run_dependency_package() {
1200-
Package::new("bdep", "0.1.0")
1202+
for i in 1..=3 {
1203+
Package::new(&format!("bdep{}", i), "0.1.1")
1204+
.file(
1205+
"Cargo.toml",
1206+
&format!(
1207+
r#"
1208+
[package]
1209+
name = "bdep{}"
1210+
version = "0.1.1"
1211+
authors = ["[email protected]"]
1212+
1213+
[[bin]]
1214+
name = "bdep{}"
1215+
"#,
1216+
i, i
1217+
),
1218+
)
1219+
.file(
1220+
"src/main.rs",
1221+
&format!("fn main() {{ println!(\"bdep{} 0.1.1\"); }}", i),
1222+
)
1223+
.publish();
1224+
}
1225+
1226+
Package::new("bdep1", "0.1.0") // older version
12011227
.file(
12021228
"Cargo.toml",
12031229
r#"
12041230
[package]
1205-
name = "bdep"
1231+
name = "bdep1"
12061232
version = "0.1.0"
12071233
authors = ["[email protected]"]
12081234
12091235
[[bin]]
1210-
name = "bdep"
1236+
name = "bdep1"
12111237
"#,
12121238
)
1213-
.file("src/main.rs", "fn main() { println!(\"bdep 0.1.0\"); }")
1239+
.file("src/main.rs", "fn main() { println!(\"bdep1 0.1.0\"); }")
12141240
.publish();
1215-
Package::new("bdep", "0.1.1")
1216-
.file(
1217-
"Cargo.toml",
1218-
r#"
1219-
[package]
1220-
name = "bdep"
1221-
version = "0.1.1"
1222-
authors = ["[email protected]"]
12231241

1224-
[[bin]]
1225-
name = "bdep"
1226-
"#,
1227-
)
1228-
.file("src/main.rs", "fn main() { println!(\"bdep 0.1.1\"); }")
1229-
.publish();
1230-
Package::new("libdep", "0.1.0")
1242+
Package::new("libdep", "0.1.0") // library (not runnable)
12311243
.file(
12321244
"Cargo.toml",
12331245
r#"
@@ -1252,20 +1264,27 @@ fn run_dependency_package() {
12521264
edition = "2018"
12531265
12541266
[build-dependencies]
1255-
bdep = "0.1"
1267+
bdep1 = "0.1"
12561268
libdep = "0.1"
1269+
1270+
[dev-dependencies]
1271+
bdep2 = "0.1.1"
1272+
1273+
[dependencies]
1274+
bdep3 = "0"
12571275
"#,
12581276
)
12591277
.file("src/main.rs", "fn main() { println!(\"foo\"); }")
12601278
.build();
12611279

12621280
let testcases = [
1263-
("bdep", "bdep 0.1.1"),
1264-
("bdep:0.1.1", "bdep 0.1.1"),
1281+
("bdep1", "bdep1 0.1.1"),
1282+
("bdep1:0.1.1", "bdep1 0.1.1"),
12651283
(
1266-
"https://github.com/rust-lang/crates.io-index#bdep",
1267-
"bdep 0.1.1",
1284+
"https://github.com/rust-lang/crates.io-index#bdep1",
1285+
"bdep1 0.1.1",
12681286
),
1287+
("bdep2", "bdep2 0.1.1"),
12691288
];
12701289
for (pkgid, expected_output) in testcases {
12711290
p.cargo("run")
@@ -1283,18 +1302,19 @@ fn run_dependency_package() {
12831302
.run();
12841303

12851304
let invalid_pkgids = [
1286-
"bdep:0.1.0",
1287-
"https://github.com/rust-lang/crates.io-index#bdep:0.1.0",
1288-
"bdep:0.2.0",
1289-
"https://github.com/rust-lang/crates.io-index#bdep:0.2.0",
1305+
"bdep1:0.1.0",
1306+
"https://github.com/rust-lang/crates.io-index#bdep1:0.1.0",
1307+
"bdep1:0.2.0",
1308+
"https://github.com/rust-lang/crates.io-index#bdep1:0.2.0",
1309+
"bdep3",
12901310
];
12911311
for pkgid in invalid_pkgids {
12921312
p.cargo("run")
12931313
.arg("-p")
12941314
.arg(pkgid)
12951315
.with_status(101)
12961316
.with_stderr_contains(
1297-
"[ERROR] `cargo run` cannot find pkgid either in the workspace or among the workspace dependencies",
1317+
"[ERROR] `cargo run` cannot find pkgid either in the workspace or among direct build or development dependencies",
12981318
)
12991319
.run();
13001320
}

0 commit comments

Comments
 (0)