Skip to content

Commit d6fa260

Browse files
committed
Auto merge of #7820 - ehuss:features2-split, r=alexcrichton
Add new feature resolver. This adds a new resolver which handles feature unification independently of the main resolver. This can be enabled with the `-Zfeatures` flag which takes a comma-separated list of options to enable new behaviors. See `unstable.md` docs for details. There are two significant behavior changes: 1. Ignore targets that are not enabled. 2. Do not unify features between build_deps, dev_deps, and normal deps. The "forks" in the unit graph are handled by adding `DepKind` to `UnitFor`. The feature resolver tracks features independently for the different dependency kinds. Unfortunately this currently does not support decoupling proc_macro dependencies. This is because at resolve time it does not know which dependencies are proc_macros. Moving feature resolution to after the packages are downloaded would require massive changes, and would make the unit computation much more complex. Nobody to my knowledge has requested this capability, presumably because proc_macros are relatively new, and they tend not to have very many dependencies, and those dependencies tend to be proc-macro specific (like syn/quote). I'd like to lean towards adding proc-macro to the index so that it can be known during resolve time, which would be much easier to implement, but with the downside of needing to add a new field to the index. I did not update `cargo metadata`, yet. It's not really clear how it should behave. I think I'll need to investigate how people are currently using the feature information and figure out how it should work. Perhaps adding features to "dep_kinds" will be the solution, but I'm not sure. The goal is to try to gather feedback about how well this new resolver works. There are two important things to check: whether it breaks a project, and how much does it increases compile time (since packages can be built multiple times with different features). I'd like to stabilize it one piece at a time assuming the disruption is not too great. If a project breaks or builds slower, the user can implement a backwards-compatible workaround of sprinkling additional features into `Cargo.toml` dependencies. I think `itarget` is a good candidate to try to stabilize first, since it is less likely to break things or change how things are built. If it does cause too much disruption, then I think we'll need to consider making it optional, enabled *somehow*. There is an environment variable that can be set which forces Cargo to use the new feature resolver. This can be used in Cargo's own testsuite to explore which tests behave differently with the different features set.
2 parents 369991b + 4d0fda7 commit d6fa260

36 files changed

+2304
-416
lines changed
Lines changed: 19 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
use crate::core::compiler::unit::UnitInterner;
2-
use crate::core::compiler::CompileTarget;
32
use crate::core::compiler::{BuildConfig, BuildOutput, CompileKind, Unit};
43
use crate::core::profiles::Profiles;
5-
use crate::core::{Dependency, InternedString, Workspace};
4+
use crate::core::{InternedString, Workspace};
65
use crate::core::{PackageId, PackageSet};
7-
use crate::util::config::{Config, TargetConfig};
6+
use crate::util::config::Config;
87
use crate::util::errors::CargoResult;
98
use crate::util::Rustc;
10-
use cargo_platform::Cfg;
119
use std::collections::HashMap;
1210
use std::path::PathBuf;
1311
use std::str;
1412

1513
mod target_info;
16-
pub use self::target_info::{FileFlavor, TargetInfo};
14+
pub use self::target_info::{FileFlavor, RustcTargetData, TargetInfo};
1715

1816
/// The build context, containing all information about a build task.
1917
///
@@ -30,26 +28,14 @@ pub struct BuildContext<'a, 'cfg> {
3028
pub build_config: &'a BuildConfig,
3129
/// Extra compiler args for either `rustc` or `rustdoc`.
3230
pub extra_compiler_args: HashMap<Unit<'a>, Vec<String>>,
31+
/// Package downloader.
3332
pub packages: &'a PackageSet<'cfg>,
3433

3534
/// Source of interning new units as they're created.
3635
pub units: &'a UnitInterner<'a>,
3736

38-
/// Information about the compiler that we've detected on the local system.
39-
pub rustc: Rustc,
40-
41-
/// Build information for the "host", which is information about when
42-
/// `rustc` is invoked without a `--target` flag. This is used for
43-
/// procedural macros, build scripts, etc.
44-
host_config: TargetConfig,
45-
host_info: TargetInfo,
46-
47-
/// Build information for targets that we're building for. This will be
48-
/// empty if the `--target` flag is not passed, and currently also only ever
49-
/// has at most one entry, but eventually we'd like to support multi-target
50-
/// builds with Cargo.
51-
target_config: HashMap<CompileTarget, TargetConfig>,
52-
target_info: HashMap<CompileTarget, TargetInfo>,
37+
/// Information about rustc and the target platform.
38+
pub target_data: RustcTargetData,
5339
}
5440

5541
impl<'a, 'cfg> BuildContext<'a, 'cfg> {
@@ -61,90 +47,41 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> {
6147
profiles: Profiles,
6248
units: &'a UnitInterner<'a>,
6349
extra_compiler_args: HashMap<Unit<'a>, Vec<String>>,
50+
target_data: RustcTargetData,
6451
) -> CargoResult<BuildContext<'a, 'cfg>> {
65-
let rustc = config.load_global_rustc(Some(ws))?;
66-
67-
let host_config = config.target_cfg_triple(&rustc.host)?;
68-
let host_info = TargetInfo::new(
69-
config,
70-
build_config.requested_kind,
71-
&rustc,
72-
CompileKind::Host,
73-
)?;
74-
let mut target_config = HashMap::new();
75-
let mut target_info = HashMap::new();
76-
if let CompileKind::Target(target) = build_config.requested_kind {
77-
let tcfg = config.target_cfg_triple(target.short_name())?;
78-
target_config.insert(target, tcfg);
79-
target_info.insert(
80-
target,
81-
TargetInfo::new(
82-
config,
83-
build_config.requested_kind,
84-
&rustc,
85-
CompileKind::Target(target),
86-
)?,
87-
);
88-
}
89-
9052
Ok(BuildContext {
9153
ws,
9254
packages,
9355
config,
94-
rustc,
95-
target_config,
96-
target_info,
97-
host_config,
98-
host_info,
9956
build_config,
10057
profiles,
10158
extra_compiler_args,
10259
units,
60+
target_data,
10361
})
10462
}
10563

106-
/// Whether a dependency should be compiled for the host or target platform,
107-
/// specified by `CompileKind`.
108-
pub fn dep_platform_activated(&self, dep: &Dependency, kind: CompileKind) -> bool {
109-
// If this dependency is only available for certain platforms,
110-
// make sure we're only enabling it for that platform.
111-
let platform = match dep.platform() {
112-
Some(p) => p,
113-
None => return true,
114-
};
115-
let name = kind.short_name(self);
116-
platform.matches(name, self.cfg(kind))
64+
pub fn rustc(&self) -> &Rustc {
65+
&self.target_data.rustc
11766
}
11867

11968
/// Gets the user-specified linker for a particular host or target.
12069
pub fn linker(&self, kind: CompileKind) -> Option<PathBuf> {
121-
self.target_config(kind)
70+
self.target_data
71+
.target_config(kind)
12272
.linker
12373
.as_ref()
12474
.map(|l| l.val.clone().resolve_program(self.config))
12575
}
12676

127-
/// Gets the list of `cfg`s printed out from the compiler for the specified kind.
128-
pub fn cfg(&self, kind: CompileKind) -> &[Cfg] {
129-
self.info(kind).cfg()
130-
}
131-
13277
/// Gets the host architecture triple.
13378
///
13479
/// For example, x86_64-unknown-linux-gnu, would be
13580
/// - machine: x86_64,
13681
/// - hardware-platform: unknown,
13782
/// - operating system: linux-gnu.
13883
pub fn host_triple(&self) -> InternedString {
139-
self.rustc.host
140-
}
141-
142-
/// Gets the target configuration for a particular host or target.
143-
pub fn target_config(&self, kind: CompileKind) -> &TargetConfig {
144-
match kind {
145-
CompileKind::Host => &self.host_config,
146-
CompileKind::Target(s) => &self.target_config[&s],
147-
}
84+
self.target_data.rustc.host
14885
}
14986

15087
/// Gets the number of jobs specified for this build.
@@ -153,24 +90,17 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> {
15390
}
15491

15592
pub fn rustflags_args(&self, unit: &Unit<'_>) -> &[String] {
156-
&self.info(unit.kind).rustflags
93+
&self.target_data.info(unit.kind).rustflags
15794
}
15895

15996
pub fn rustdocflags_args(&self, unit: &Unit<'_>) -> &[String] {
160-
&self.info(unit.kind).rustdocflags
97+
&self.target_data.info(unit.kind).rustdocflags
16198
}
16299

163100
pub fn show_warnings(&self, pkg: PackageId) -> bool {
164101
pkg.source_id().is_path() || self.config.extra_verbose()
165102
}
166103

167-
pub fn info(&self, kind: CompileKind) -> &TargetInfo {
168-
match kind {
169-
CompileKind::Host => &self.host_info,
170-
CompileKind::Target(s) => &self.target_info[&s],
171-
}
172-
}
173-
174104
pub fn extra_args_for(&self, unit: &Unit<'a>) -> Option<&Vec<String>> {
175105
self.extra_compiler_args.get(unit)
176106
}
@@ -180,6 +110,9 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> {
180110
/// `lib_name` is the `links` library name and `kind` is whether it is for
181111
/// Host or Target.
182112
pub fn script_override(&self, lib_name: &str, kind: CompileKind) -> Option<&BuildOutput> {
183-
self.target_config(kind).links_overrides.get(lib_name)
113+
self.target_data
114+
.target_config(kind)
115+
.links_overrides
116+
.get(lib_name)
184117
}
185118
}

src/cargo/core/compiler/build_context/target_info.rs

Lines changed: 95 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1+
use crate::core::compiler::CompileKind;
2+
use crate::core::compiler::CompileTarget;
3+
use crate::core::{Dependency, TargetKind, Workspace};
4+
use crate::util::config::{Config, StringList, TargetConfig};
5+
use crate::util::{CargoResult, CargoResultExt, ProcessBuilder, Rustc};
6+
use cargo_platform::{Cfg, CfgExpr};
17
use std::cell::RefCell;
28
use std::collections::hash_map::{Entry, HashMap};
39
use std::env;
410
use std::path::PathBuf;
511
use std::str::{self, FromStr};
612

7-
use crate::core::compiler::CompileKind;
8-
use crate::core::TargetKind;
9-
use crate::util::config::StringList;
10-
use crate::util::{CargoResult, CargoResultExt, Config, ProcessBuilder, Rustc};
11-
use cargo_platform::{Cfg, CfgExpr};
12-
1313
/// Information about the platform target gleaned from querying rustc.
1414
///
15-
/// The `BuildContext` keeps two of these, one for the host and one for the
15+
/// `RustcTargetData` keeps two of these, one for the host and one for the
1616
/// target. If no target is specified, it uses a clone from the host.
1717
#[derive(Clone)]
1818
pub struct TargetInfo {
@@ -468,3 +468,91 @@ fn env_args(
468468

469469
Ok(Vec::new())
470470
}
471+
472+
/// Collection of information about `rustc` and the host and target.
473+
pub struct RustcTargetData {
474+
/// Information about `rustc` itself.
475+
pub rustc: Rustc,
476+
/// Build information for the "host", which is information about when
477+
/// `rustc` is invoked without a `--target` flag. This is used for
478+
/// procedural macros, build scripts, etc.
479+
host_config: TargetConfig,
480+
host_info: TargetInfo,
481+
482+
/// Build information for targets that we're building for. This will be
483+
/// empty if the `--target` flag is not passed, and currently also only ever
484+
/// has at most one entry, but eventually we'd like to support multi-target
485+
/// builds with Cargo.
486+
target_config: HashMap<CompileTarget, TargetConfig>,
487+
target_info: HashMap<CompileTarget, TargetInfo>,
488+
}
489+
490+
impl RustcTargetData {
491+
pub fn new(ws: &Workspace<'_>, requested_kind: CompileKind) -> CargoResult<RustcTargetData> {
492+
let config = ws.config();
493+
let rustc = config.load_global_rustc(Some(ws))?;
494+
let host_config = config.target_cfg_triple(&rustc.host)?;
495+
let host_info = TargetInfo::new(config, requested_kind, &rustc, CompileKind::Host)?;
496+
let mut target_config = HashMap::new();
497+
let mut target_info = HashMap::new();
498+
if let CompileKind::Target(target) = requested_kind {
499+
let tcfg = config.target_cfg_triple(target.short_name())?;
500+
target_config.insert(target, tcfg);
501+
target_info.insert(
502+
target,
503+
TargetInfo::new(config, requested_kind, &rustc, CompileKind::Target(target))?,
504+
);
505+
}
506+
507+
Ok(RustcTargetData {
508+
rustc,
509+
target_config,
510+
target_info,
511+
host_config,
512+
host_info,
513+
})
514+
}
515+
516+
/// Returns a "short" name for the given kind, suitable for keying off
517+
/// configuration in Cargo or presenting to users.
518+
pub fn short_name<'a>(&'a self, kind: &'a CompileKind) -> &'a str {
519+
match kind {
520+
CompileKind::Host => &self.rustc.host,
521+
CompileKind::Target(target) => target.short_name(),
522+
}
523+
}
524+
525+
/// Whether a dependency should be compiled for the host or target platform,
526+
/// specified by `CompileKind`.
527+
pub fn dep_platform_activated(&self, dep: &Dependency, kind: CompileKind) -> bool {
528+
// If this dependency is only available for certain platforms,
529+
// make sure we're only enabling it for that platform.
530+
let platform = match dep.platform() {
531+
Some(p) => p,
532+
None => return true,
533+
};
534+
let name = self.short_name(&kind);
535+
platform.matches(name, self.cfg(kind))
536+
}
537+
538+
/// Gets the list of `cfg`s printed out from the compiler for the specified kind.
539+
pub fn cfg(&self, kind: CompileKind) -> &[Cfg] {
540+
self.info(kind).cfg()
541+
}
542+
543+
/// Information about the given target platform, learned by querying rustc.
544+
pub fn info(&self, kind: CompileKind) -> &TargetInfo {
545+
match kind {
546+
CompileKind::Host => &self.host_info,
547+
CompileKind::Target(s) => &self.target_info[&s],
548+
}
549+
}
550+
551+
/// Gets the target configuration for a particular host or target.
552+
pub fn target_config(&self, kind: CompileKind) -> &TargetConfig {
553+
match kind {
554+
CompileKind::Host => &self.host_config,
555+
CompileKind::Target(s) => &self.target_config[&s],
556+
}
557+
}
558+
}

src/cargo/core/compiler/compilation.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl<'cfg> Compilation<'cfg> {
8484
bcx: &BuildContext<'a, 'cfg>,
8585
default_kind: CompileKind,
8686
) -> CargoResult<Compilation<'cfg>> {
87-
let mut rustc = bcx.rustc.process();
87+
let mut rustc = bcx.rustc().process();
8888

8989
let mut primary_unit_rustc_process = bcx.build_config.primary_unit_rustc.clone();
9090

@@ -102,8 +102,16 @@ impl<'cfg> Compilation<'cfg> {
102102
root_output: PathBuf::from("/"),
103103
deps_output: PathBuf::from("/"),
104104
host_deps_output: PathBuf::from("/"),
105-
host_dylib_path: bcx.info(CompileKind::Host).sysroot_host_libdir.clone(),
106-
target_dylib_path: bcx.info(default_kind).sysroot_target_libdir.clone(),
105+
host_dylib_path: bcx
106+
.target_data
107+
.info(CompileKind::Host)
108+
.sysroot_host_libdir
109+
.clone(),
110+
target_dylib_path: bcx
111+
.target_data
112+
.info(default_kind)
113+
.sysroot_target_libdir
114+
.clone(),
107115
tests: Vec::new(),
108116
binaries: Vec::new(),
109117
extra_env: HashMap::new(),
@@ -114,7 +122,7 @@ impl<'cfg> Compilation<'cfg> {
114122
rustc_process: rustc,
115123
primary_unit_rustc_process,
116124
host: bcx.host_triple().to_string(),
117-
target: default_kind.short_name(bcx).to_string(),
125+
target: bcx.target_data.short_name(&default_kind).to_string(),
118126
target_runner: target_runner(bcx, default_kind)?,
119127
})
120128
}
@@ -286,7 +294,7 @@ fn target_runner(
286294
bcx: &BuildContext<'_, '_>,
287295
kind: CompileKind,
288296
) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
289-
let target = kind.short_name(bcx);
297+
let target = bcx.target_data.short_name(&kind);
290298

291299
// try target.{}.runner
292300
let key = format!("target.{}.runner", target);
@@ -296,7 +304,7 @@ fn target_runner(
296304
}
297305

298306
// try target.'cfg(...)'.runner
299-
let target_cfg = bcx.info(kind).cfg();
307+
let target_cfg = bcx.target_data.info(kind).cfg();
300308
let mut cfgs = bcx
301309
.config
302310
.target_cfgs()?

src/cargo/core/compiler/compile_kind.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::core::compiler::BuildContext;
21
use crate::core::{InternedString, Target};
32
use crate::util::errors::{CargoResult, CargoResultExt};
43
use serde::Serialize;
@@ -40,15 +39,6 @@ impl CompileKind {
4039
CompileKind::Target(n) => CompileKind::Target(n),
4140
}
4241
}
43-
44-
/// Returns a "short" name for this kind, suitable for keying off
45-
/// configuration in Cargo or presenting to users.
46-
pub fn short_name(&self, bcx: &BuildContext<'_, '_>) -> &str {
47-
match self {
48-
CompileKind::Host => bcx.host_triple().as_str(),
49-
CompileKind::Target(target) => target.short_name(),
50-
}
51-
}
5242
}
5343

5444
/// Abstraction for the representation of a compilation target that Cargo has.

0 commit comments

Comments
 (0)