Skip to content

Commit e28f5cb

Browse files
committed
Auto merge of #3879 - jbendig:issue_3867, r=alexcrichton
Fix `cargo run` panic when required-features not satisfied This PR fixes #3867 which is made up of two parts. The first part involves `cargo run` triggering an assertion after compiling. This is triggered by the single binary selected for compilation being filtered out when required-features is specified and said features are not enabled. The cleanest approach to me involves just sticking a flag into `CompileFilter::Everything`. The flag then triggers the already existing error message when required-features is not satisfied. I think this works best because it localizes what is really a `cargo run` quirk without requiring any boilerplate or duplicate code. The second part shows `cargo run` bailing when two binaries exist, both with required-features, but only one is resolved to be compiled due to default features. I feel like the current approach is correct because it's consistent with what normally happens when there are multiple binaries. I'm open to changing this, but for now, I've added a test to enforce this behavior. cc @BenWiederhake: I took a quick peek at your branch to fix #3112 and I noticed that it probably won't merge cleanly with this PR. Just an FYI in case it makes sense to have this merged.
2 parents afeed88 + 25e50b5 commit e28f5cb

File tree

6 files changed

+87
-12
lines changed

6 files changed

+87
-12
lines changed

src/bin/run.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ pub fn execute(options: Options, config: &Config) -> CliResult {
9292
release: options.flag_release,
9393
mode: ops::CompileMode::Build,
9494
filter: if examples.is_empty() && bins.is_empty() {
95-
ops::CompileFilter::Everything
95+
ops::CompileFilter::Everything { required_features_filterable: false, }
9696
} else {
9797
ops::CompileFilter::Only {
9898
lib: false, tests: &[], benches: &[],

src/cargo/ops/cargo_compile.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,10 @@ impl<'a> Packages<'a> {
126126
}
127127

128128
pub enum CompileFilter<'a> {
129-
Everything,
129+
Everything {
130+
/// Flag whether targets can be safely skipped when required-features are not satisfied.
131+
required_features_filterable: bool,
132+
},
130133
Only {
131134
lib: bool,
132135
bins: &'a [String],
@@ -311,13 +314,15 @@ impl<'a> CompileFilter<'a> {
311314
tests: tests,
312315
}
313316
} else {
314-
CompileFilter::Everything
317+
CompileFilter::Everything {
318+
required_features_filterable: true,
319+
}
315320
}
316321
}
317322

318323
pub fn matches(&self, target: &Target) -> bool {
319324
match *self {
320-
CompileFilter::Everything => true,
325+
CompileFilter::Everything { .. } => true,
321326
CompileFilter::Only { lib, bins, examples, tests, benches } => {
322327
let list = match *target.kind() {
323328
TargetKind::Bin => bins,
@@ -354,7 +359,7 @@ fn generate_targets<'a>(pkg: &'a Package,
354359
CompileMode::Doctest => &profiles.doctest,
355360
};
356361
let mut targets = match *filter {
357-
CompileFilter::Everything => {
362+
CompileFilter::Everything { .. } => {
358363
match mode {
359364
CompileMode::Bench => {
360365
pkg.targets().iter().filter(|t| t.benched()).map(|t| {
@@ -462,7 +467,11 @@ fn generate_targets<'a>(pkg: &'a Package,
462467
continue;
463468
}
464469

465-
if let CompileFilter::Only { .. } = *filter {
470+
if match *filter {
471+
CompileFilter::Everything { required_features_filterable } =>
472+
!required_features_filterable,
473+
CompileFilter::Only { .. } => true,
474+
} {
466475
let required_features = target.required_features().unwrap();
467476
let quoted_required_features: Vec<String> = required_features.iter()
468477
.map(|s| format!("`{}`",s))

src/cargo/ops/cargo_install.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ fn check_overwrites(dst: &Path,
358358
filter: &ops::CompileFilter,
359359
prev: &CrateListingV1,
360360
force: bool) -> CargoResult<BTreeMap<String, Option<PackageId>>> {
361-
if let CompileFilter::Everything = *filter {
361+
if let CompileFilter::Everything { .. } = *filter {
362362
// If explicit --bin or --example flags were passed then those'll
363363
// get checked during cargo_compile, we only care about the "build
364364
// everything" case here
@@ -399,7 +399,7 @@ fn find_duplicates(dst: &Path,
399399
}
400400
};
401401
match *filter {
402-
CompileFilter::Everything => {
402+
CompileFilter::Everything { .. } => {
403403
pkg.targets().iter()
404404
.filter(|t| t.is_bin())
405405
.filter_map(|t| check(t.name()))

src/cargo/ops/cargo_package.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ fn run_verify(ws: &Workspace, tar: &File, opts: &PackageOpts) -> CargoResult<()>
293293
no_default_features: false,
294294
all_features: false,
295295
spec: ops::Packages::Packages(&[]),
296-
filter: ops::CompileFilter::Everything,
296+
filter: ops::CompileFilter::Everything { required_features_filterable: true },
297297
release: false,
298298
message_format: ops::MessageFormat::Human,
299299
mode: ops::CompileMode::Build,

src/cargo/ops/cargo_run.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ pub fn run(ws: &Workspace,
2424

2525
let mut bins = pkg.manifest().targets().iter().filter(|a| {
2626
!a.is_lib() && !a.is_custom_build() && match options.filter {
27-
CompileFilter::Everything => a.is_bin(),
27+
CompileFilter::Everything { .. } => a.is_bin(),
2828
CompileFilter::Only { .. } => options.filter.matches(a),
2929
}
3030
});
3131
if bins.next().is_none() {
3232
match options.filter {
33-
CompileFilter::Everything => {
33+
CompileFilter::Everything { .. } => {
3434
bail!("a bin target must be available for `cargo run`")
3535
}
3636
CompileFilter::Only { .. } => {
@@ -40,7 +40,7 @@ pub fn run(ws: &Workspace,
4040
}
4141
if bins.next().is_some() {
4242
match options.filter {
43-
CompileFilter::Everything => {
43+
CompileFilter::Everything { .. } => {
4444
bail!("`cargo run` requires that a project only have one \
4545
executable; use the `--bin` option to specify which one \
4646
to run")

tests/required-features.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,3 +1038,69 @@ test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
10381038
error[E0463]: can't find crate for `bar`", p.url())));
10391039
}
10401040
}
1041+
1042+
#[test]
1043+
fn run_default() {
1044+
let p = project("foo")
1045+
.file("Cargo.toml", r#"
1046+
[project]
1047+
name = "foo"
1048+
version = "0.0.1"
1049+
authors = []
1050+
1051+
[features]
1052+
default = []
1053+
a = []
1054+
1055+
[[bin]]
1056+
name = "foo"
1057+
required-features = ["a"]
1058+
"#)
1059+
.file("src/lib.rs", "")
1060+
.file("src/main.rs", "extern crate foo; fn main() {}");
1061+
p.build();
1062+
1063+
assert_that(p.cargo("run"),
1064+
execs().with_status(101).with_stderr("\
1065+
error: target `foo` requires the features: `a`
1066+
Consider enabling them by passing e.g. `--features=\"a\"`
1067+
"));
1068+
1069+
assert_that(p.cargo("run").arg("--features").arg("a"),
1070+
execs().with_status(0));
1071+
}
1072+
1073+
#[test]
1074+
fn run_default_multiple_required_features() {
1075+
let p = project("foo")
1076+
.file("Cargo.toml", r#"
1077+
[project]
1078+
name = "foo"
1079+
version = "0.0.1"
1080+
authors = []
1081+
1082+
[features]
1083+
default = ["a"]
1084+
a = []
1085+
b = []
1086+
1087+
[[bin]]
1088+
name = "foo1"
1089+
path = "src/foo1.rs"
1090+
required-features = ["a"]
1091+
1092+
[[bin]]
1093+
name = "foo2"
1094+
path = "src/foo2.rs"
1095+
required-features = ["b"]
1096+
"#)
1097+
.file("src/lib.rs", "")
1098+
.file("src/foo1.rs", "extern crate foo; fn main() {}")
1099+
.file("src/foo2.rs", "extern crate foo; fn main() {}");
1100+
p.build();
1101+
1102+
assert_that(p.cargo("run"),
1103+
execs().with_status(101).with_stderr("\
1104+
error: `cargo run` requires that a project only have one executable; \
1105+
use the `--bin` option to specify which one to run"));
1106+
}

0 commit comments

Comments
 (0)