Skip to content

Commit 231777f

Browse files
committed
Auto merge of #8048 - ehuss:fix-features-for-host, r=alexcrichton
Fix -Zfeatures=itarget with certain host dependencies In some cases, `-Zfeatures=itarget` can panic because an entry is missing in the activation map. This happens because the way "for_host" was tracked when building the activation map. Previously "for_host" was only set when `-Zfeatures=host_dep` was specified. However, if you don't specify `host_dep`, then "for_host" was always false. This is a problem because `itarget` needs to also be able to detect if something is enabled for the host or target. If you have a proc-macro ("for_host"), and it has a dependency with a `[target]` specification that matched the host, then the activation map would fail to include it (because the "for_host" flag was not "sticky" and didn't get passed down). The solution is to always carry the "for_host" setting around while building the activation map. Only when inserting a feature into the map will it check if `opts.decouple_host_deps` is set. This allows it to handle the "for_host" setting correctly, even if the `host_dep` option isn't enabled. This was discovered at #7914 (comment) where a dependency on `hashbrown` would fail if you pass `--target something_not_unix` because it has a unix-only dependency for `libc`. (Sorry, long-winded explanation. Please ask if that is confusing.)
2 parents 1e2f8db + 86ee8ce commit 231777f

File tree

2 files changed

+109
-6
lines changed

2 files changed

+109
-6
lines changed

src/cargo/core/resolver/features.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
298298
let member_features = self.ws.members_with_features(specs, requested_features)?;
299299
for (member, requested_features) in &member_features {
300300
let fvs = self.fvs_from_requested(member.package_id(), requested_features);
301-
let for_host = self.opts.decouple_host_deps && self.is_proc_macro(member.package_id());
301+
let for_host = self.is_proc_macro(member.package_id());
302302
self.activate_pkg(member.package_id(), &fvs, for_host)?;
303303
if for_host {
304304
// Also activate without for_host. This is needed if the
@@ -324,7 +324,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
324324
// finding bugs where the resolver missed something it should have visited.
325325
// Remove this in the future if `activated_features` uses an empty default.
326326
self.activated_features
327-
.entry((pkg_id, for_host))
327+
.entry((pkg_id, self.opts.decouple_host_deps && for_host))
328328
.or_insert_with(BTreeSet::new);
329329
for fv in fvs {
330330
self.activate_fv(pkg_id, fv, for_host)?;
@@ -418,7 +418,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
418418
) -> CargoResult<()> {
419419
let enabled = self
420420
.activated_features
421-
.entry((pkg_id, for_host))
421+
.entry((pkg_id, self.opts.decouple_host_deps && for_host))
422422
.or_insert_with(BTreeSet::new);
423423
if !enabled.insert(feature_to_enable) {
424424
// Already enabled.
@@ -541,8 +541,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
541541
true
542542
})
543543
.map(|dep| {
544-
let dep_for_host = for_host
545-
|| (self.opts.decouple_host_deps && (dep.is_build() || is_proc_macro));
544+
let dep_for_host = for_host || dep.is_build() || is_proc_macro;
546545
(dep, dep_for_host)
547546
})
548547
.collect::<Vec<_>>();

tests/testsuite/features2.rs

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
//! Tests for the new feature resolver.
22
3+
use cargo_test_support::cross_compile::{self, alternate};
34
use cargo_test_support::paths::CargoPathExt;
45
use cargo_test_support::registry::{Dependency, Package};
5-
use cargo_test_support::{basic_manifest, project};
6+
use cargo_test_support::{basic_manifest, project, rustc_host};
67

78
#[cargo_test]
89
fn inactivate_targets() {
@@ -183,6 +184,49 @@ fn inactive_target_optional() {
183184
.run();
184185
}
185186

187+
#[cargo_test]
188+
fn itarget_proc_macro() {
189+
// itarget inside a proc-macro while cross-compiling
190+
if cross_compile::disabled() {
191+
return;
192+
}
193+
Package::new("hostdep", "1.0.0").publish();
194+
Package::new("pm", "1.0.0")
195+
.proc_macro(true)
196+
.target_dep("hostdep", "1.0", &rustc_host())
197+
.file("src/lib.rs", "extern crate hostdep;")
198+
.publish();
199+
let p = project()
200+
.file(
201+
"Cargo.toml",
202+
r#"
203+
[package]
204+
name = "foo"
205+
version = "0.1.0"
206+
207+
[dependencies]
208+
pm = "1.0"
209+
"#,
210+
)
211+
.file("src/lib.rs", "")
212+
.build();
213+
214+
p.cargo("check").run();
215+
p.cargo("check -Zfeatures=itarget")
216+
.masquerade_as_nightly_cargo()
217+
.run();
218+
p.cargo("check --target").arg(alternate()).run();
219+
p.cargo("check -Zfeatures=itarget --target")
220+
.arg(alternate())
221+
.masquerade_as_nightly_cargo()
222+
.run();
223+
// For good measure, just make sure things don't break.
224+
p.cargo("check -Zfeatures=all --target")
225+
.arg(alternate())
226+
.masquerade_as_nightly_cargo()
227+
.run();
228+
}
229+
186230
#[cargo_test]
187231
fn decouple_host_deps() {
188232
// Basic test for `host_dep` decouple.
@@ -1185,3 +1229,63 @@ fn has_dev_dep_for_test() {
11851229
)
11861230
.run();
11871231
}
1232+
1233+
#[cargo_test]
1234+
fn build_dep_activated() {
1235+
// Build dependencies always match the host for [target.*.build-dependencies].
1236+
if cross_compile::disabled() {
1237+
return;
1238+
}
1239+
Package::new("somedep", "1.0.0")
1240+
.file("src/lib.rs", "")
1241+
.publish();
1242+
Package::new("targetdep", "1.0.0").publish();
1243+
Package::new("hostdep", "1.0.0")
1244+
// Check that "for_host" is sticky.
1245+
.target_dep("somedep", "1.0", &rustc_host())
1246+
.feature("feat1", &[])
1247+
.file(
1248+
"src/lib.rs",
1249+
r#"
1250+
extern crate somedep;
1251+
1252+
#[cfg(not(feature="feat1"))]
1253+
compile_error!{"feat1 missing"}
1254+
"#,
1255+
)
1256+
.publish();
1257+
1258+
let p = project()
1259+
.file(
1260+
"Cargo.toml",
1261+
&format!(
1262+
r#"
1263+
[package]
1264+
name = "foo"
1265+
version = "0.1.0"
1266+
1267+
# This should never be selected.
1268+
[target.'{}'.build-dependencies]
1269+
targetdep = "1.0"
1270+
1271+
[target.'{}'.build-dependencies]
1272+
hostdep = {{version="1.0", features=["feat1"]}}
1273+
"#,
1274+
alternate(),
1275+
rustc_host()
1276+
),
1277+
)
1278+
.file("src/lib.rs", "")
1279+
.file("build.rs", "fn main() {}")
1280+
.build();
1281+
1282+
p.cargo("check").run();
1283+
p.cargo("check -Zfeatures=all")
1284+
.masquerade_as_nightly_cargo()
1285+
.run();
1286+
p.cargo("check --target").arg(alternate()).run();
1287+
p.cargo("check -Zfeatures=all --target")
1288+
.arg(alternate())
1289+
.masquerade_as_nightly_cargo()
1290+
.run();
1291+
}

0 commit comments

Comments
 (0)