]> git.proxmox.com Git - cargo.git/blame - src/cargo/ops/cargo_compile.rs
Auto merge of #8540 - vandenheuvel:update_termcolor_version, r=ehuss
[cargo.git] / src / cargo / ops / cargo_compile.rs
CommitLineData
2599c34d 1//! The Cargo "compile" operation.
64ff29ff 2//!
2599c34d
EH
3//! This module contains the entry point for starting the compilation process
4//! for commands like `build`, `test`, `doc`, `rustc`, etc.
64ff29ff 5//!
2599c34d
EH
6//! The `compile` function will do all the work to compile a workspace. A
7//! rough outline is:
64ff29ff 8//!
2599c34d 9//! - Resolve the dependency graph (see `ops::resolve`).
e0bd9e23 10//! - Download any packages needed (see `PackageSet`).
2599c34d
EH
11//! - Generate a list of top-level "units" of work for the targets the user
12//! requested on the command-line. Each `Unit` corresponds to a compiler
13//! invocation. This is done in this module (`generate_targets`).
e0bd9e23
EH
14//! - Build the graph of `Unit` dependencies (see
15//! `core::compiler::context::unit_dependencies`).
2599c34d 16//! - Create a `Context` which will perform the following steps:
2599c34d
EH
17//! - Prepare the `target` directory (see `Layout`).
18//! - Create a job queue (see `JobQueue`). The queue checks the
19//! fingerprint of each `Unit` to determine if it should run or be
20//! skipped.
21//! - Execute the queue. Each leaf in the queue's dependency graph is
22//! executed, and then removed from the graph when finished. This
23//! repeats until the queue is empty.
62bff631 24
45ce445c
DW
25use std::collections::{BTreeSet, HashMap, HashSet};
26use std::iter::FromIterator;
1ab19a5a 27use std::sync::Arc;
12af54e1 28
1f14fa31 29use crate::core::compiler::unit_dependencies::build_unit_dependencies;
e0bd9e23 30use crate::core::compiler::{standard_lib, unit_graph};
e4544466 31use crate::core::compiler::{BuildConfig, BuildContext, Compilation, Context};
949eccac 32use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit};
e4544466 33use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
04ddd4d0 34use crate::core::profiles::{Profiles, UnitFor};
137642c4 35use crate::core::resolver::features::{self, FeaturesFor};
e0d64f94 36use crate::core::resolver::{HasDevUnits, Resolve, ResolveOpts};
98b6f816 37use crate::core::{Package, PackageSet, Target};
04ddd4d0
DW
38use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
39use crate::ops;
12392f6d 40use crate::ops::resolve::WorkspaceResolve;
04ddd4d0 41use crate::util::config::Config;
7d7fe679 42use crate::util::{closest_msg, profile, CargoResult};
50f110a4 43
bacb6be3 44/// Contains information about how a package should be compiled.
e0bd9e23
EH
45///
46/// Note on distinction between `CompileOptions` and `BuildConfig`:
47/// `BuildConfig` contains values that need to be retained after
48/// `BuildContext` is created. The other fields are no longer necessary. Think
49/// of it as `CompileOptions` are high-level settings requested on the
50/// command-line, and `BuildConfig` are low-level settings for actually
51/// driving `rustc`.
876af7c1 52#[derive(Debug)]
e4918c45 53pub struct CompileOptions {
08025169
DO
54 /// Configuration information for a rustc build
55 pub build_config: BuildConfig,
9e779198 56 /// Extra features to build for the root package
bb643cca 57 pub features: Vec<String>,
1f8b3988
DA
58 /// Flag whether all available features should be built for the root package
59 pub all_features: bool,
9e779198 60 /// Flag if the default feature should be built for the root package
2b46d039 61 pub no_default_features: bool,
6b32c4d7 62 /// A set of packages to build.
bb643cca 63 pub spec: Packages,
9e779198
AC
64 /// Filter to apply to the root package to select which targets will be
65 /// built.
bb643cca 66 pub filter: CompileFilter,
1ef954ea 67 /// Extra arguments to be passed to rustdoc (single target only)
bb643cca 68 pub target_rustdoc_args: Option<Vec<String>>,
77bb01ec
SL
69 /// The specified target will be compiled with all the available arguments,
70 /// note that this only accounts for the *final* invocation of rustc
bb643cca 71 pub target_rustc_args: Option<Vec<String>>,
1ef954ea
EH
72 /// Extra arguments passed to all selected targets for rustdoc.
73 pub local_rustdoc_args: Option<Vec<String>>,
e7ee237c
LK
74 /// Whether the `--document-private-items` flags was specified and should
75 /// be forwarded to `rustdoc`.
76 pub rustdoc_document_private_items: bool,
9e779198
AC
77}
78
e4918c45
EH
79impl<'a> CompileOptions {
80 pub fn new(config: &Config, mode: CompileMode) -> CargoResult<CompileOptions> {
08025169 81 Ok(CompileOptions {
3fd28143 82 build_config: BuildConfig::new(config, None, &[], mode)?,
bb643cca 83 features: Vec::new(),
69caa63b
NC
84 all_features: false,
85 no_default_features: false,
bb643cca 86 spec: ops::Packages::Packages(Vec::new()),
1e682848
AC
87 filter: CompileFilter::Default {
88 required_features_filterable: false,
89 },
69caa63b
NC
90 target_rustdoc_args: None,
91 target_rustc_args: None,
1ef954ea 92 local_rustdoc_args: None,
e7ee237c 93 rustdoc_document_private_items: false,
08025169 94 })
69caa63b
NC
95 }
96}
97
bb643cca
AK
98#[derive(Clone, PartialEq, Eq, Debug)]
99pub enum Packages {
ba7911dd 100 Default,
62c04979 101 All,
bb643cca
AK
102 OptOut(Vec<String>),
103 Packages(Vec<String>),
62c04979
AR
104}
105
bb643cca 106impl Packages {
1e682848 107 pub fn from_flags(all: bool, exclude: Vec<String>, package: Vec<String>) -> CargoResult<Self> {
ba7911dd
SS
108 Ok(match (all, exclude.len(), package.len()) {
109 (false, 0, 0) => Packages::Default,
110 (false, 0, _) => Packages::Packages(package),
3a18c89a 111 (false, _, _) => anyhow::bail!("--exclude can only be used together with --workspace"),
ba7911dd
SS
112 (true, 0, _) => Packages::All,
113 (true, _, _) => Packages::OptOut(exclude),
114 })
7ffd1cfc 115 }
116
b8b7faee 117 pub fn to_package_id_specs(&self, ws: &Workspace<'_>) -> CargoResult<Vec<PackageIdSpec>> {
d347908d 118 let specs = match self {
dae87a26
E
119 Packages::All => ws
120 .members()
1e682848
AC
121 .map(Package::package_id)
122 .map(PackageIdSpec::from_package_id)
123 .collect(),
45ce445c
DW
124 Packages::OptOut(opt_out) => {
125 let mut opt_out = BTreeSet::from_iter(opt_out.iter().cloned());
126 let packages = ws
127 .members()
128 .filter(|pkg| !opt_out.remove(pkg.name().as_str()))
129 .map(Package::package_id)
130 .map(PackageIdSpec::from_package_id)
131 .collect();
132 if !opt_out.is_empty() {
133 ws.config().shell().warn(format!(
134 "excluded package(s) {} not found in workspace `{}`",
f16efff1
AC
135 opt_out
136 .iter()
137 .map(|x| x.as_ref())
138 .collect::<Vec<_>>()
139 .join(", "),
45ce445c
DW
140 ws.root().display(),
141 ))?;
142 }
143 packages
f16efff1 144 }
d347908d 145 Packages::Packages(packages) if packages.is_empty() => {
037a879d
EH
146 vec![PackageIdSpec::from_package_id(ws.current()?.package_id())]
147 }
d347908d 148 Packages::Packages(packages) => packages
1e682848
AC
149 .iter()
150 .map(|p| PackageIdSpec::parse(p))
151 .collect::<CargoResult<Vec<_>>>()?,
dae87a26
E
152 Packages::Default => ws
153 .default_members()
1e682848
AC
154 .map(Package::package_id)
155 .map(PackageIdSpec::from_package_id)
156 .collect(),
6b32c4d7 157 };
68a681ff 158 if specs.is_empty() {
691f95de 159 if ws.is_virtual() {
3a18c89a 160 anyhow::bail!(
1e682848
AC
161 "manifest path `{}` contains no package: The manifest is virtual, \
162 and the workspace has no members.",
163 ws.root().display()
164 )
05b896ac 165 }
3a18c89a 166 anyhow::bail!("no packages to compile")
68a681ff 167 }
6b32c4d7
AK
168 Ok(specs)
169 }
fa54a794 170
b8b7faee 171 pub fn get_packages<'ws>(&self, ws: &'ws Workspace<'_>) -> CargoResult<Vec<&'ws Package>> {
fa54a794
EH
172 let packages: Vec<_> = match self {
173 Packages::Default => ws.default_members().collect(),
174 Packages::All => ws.members().collect(),
d347908d 175 Packages::OptOut(opt_out) => ws
fa54a794
EH
176 .members()
177 .filter(|pkg| !opt_out.iter().any(|name| pkg.name().as_str() == name))
178 .collect(),
d347908d 179 Packages::Packages(packages) => packages
fa54a794
EH
180 .iter()
181 .map(|name| {
182 ws.members()
183 .find(|pkg| pkg.name().as_str() == name)
184 .ok_or_else(|| {
3a18c89a 185 anyhow::format_err!(
54c42142
DW
186 "package `{}` is not a member of the workspace",
187 name
188 )
fa54a794 189 })
dae87a26
E
190 })
191 .collect::<CargoResult<Vec<_>>>()?,
fa54a794
EH
192 };
193 Ok(packages)
194 }
44c535ab
EH
195
196 /// Returns whether or not the user needs to pass a `-p` flag to target a
197 /// specific package in the workspace.
198 pub fn needs_spec_flag(&self, ws: &Workspace<'_>) -> bool {
199 match self {
200 Packages::Default => ws.default_members().count() > 1,
201 Packages::All => ws.members().count() > 1,
202 Packages::Packages(_) => true,
203 Packages::OptOut(_) => true,
204 }
205 }
6b32c4d7
AK
206}
207
aef99e0e
DW
208#[derive(Debug, PartialEq, Eq)]
209pub enum LibRule {
210 /// Include the library, fail if not present
211 True,
212 /// Include the library if present
213 Default,
214 /// Exclude the library
215 False,
216}
217
bb643cca
AK
218#[derive(Debug)]
219pub enum FilterRule {
2a82378a 220 All,
bb643cca 221 Just(Vec<String>),
2a82378a
BW
222}
223
876af7c1 224#[derive(Debug)]
bb643cca 225pub enum CompileFilter {
6344db05 226 Default {
25e50b58
JB
227 /// Flag whether targets can be safely skipped when required-features are not satisfied.
228 required_features_filterable: bool,
229 },
9e779198 230 Only {
b9497ab2 231 all_targets: bool,
aef99e0e 232 lib: LibRule,
bb643cca
AK
233 bins: FilterRule,
234 examples: FilterRule,
235 tests: FilterRule,
236 benches: FilterRule,
1e682848 237 },
a1980dc7
YKCL
238}
239
e4918c45 240pub fn compile<'a>(ws: &Workspace<'a>, options: &CompileOptions) -> CargoResult<Compilation<'a>> {
b8b7faee 241 let exec: Arc<dyn Executor> = Arc::new(DefaultExecutor);
689f412c 242 compile_with_exec(ws, options, &exec)
8c3b3605
NC
243}
244
4a64d05e 245/// Like `compile` but allows specifying a custom `Executor` that will be able to intercept build
d70f91ee 246/// calls and add custom logic. `compile` uses `DefaultExecutor` which just passes calls through.
1e682848
AC
247pub fn compile_with_exec<'a>(
248 ws: &Workspace<'a>,
e4918c45 249 options: &CompileOptions,
b8b7faee 250 exec: &Arc<dyn Executor>,
a340ba0b 251) -> CargoResult<Compilation<'a>> {
688a4fa6 252 ws.emit_warnings()?;
8a301589 253 compile_ws(ws, options, exec)
9fba127e
AC
254}
255
1e682848
AC
256pub fn compile_ws<'a>(
257 ws: &Workspace<'a>,
e4918c45 258 options: &CompileOptions,
b8b7faee 259 exec: &Arc<dyn Executor>,
a340ba0b 260) -> CargoResult<Compilation<'a>> {
e0bd9e23
EH
261 let interner = UnitInterner::new();
262 let bcx = create_bcx(ws, options, &interner)?;
263 if options.build_config.unit_graph {
264 unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph)?;
265 return Ok(Compilation::new(&bcx)?);
266 }
267
268 let _p = profile::start("compiling");
269 let cx = Context::new(&bcx)?;
270 cx.compile(exec)
271}
272
273pub fn create_bcx<'a, 'cfg>(
274 ws: &'a Workspace<'cfg>,
275 options: &'a CompileOptions,
276 interner: &'a UnitInterner,
277) -> CargoResult<BuildContext<'a, 'cfg>> {
1e682848 278 let CompileOptions {
08025169 279 ref build_config,
1e682848
AC
280 ref spec,
281 ref features,
282 all_features,
283 no_default_features,
1e682848
AC
284 ref filter,
285 ref target_rustdoc_args,
286 ref target_rustc_args,
1ef954ea 287 ref local_rustdoc_args,
e7ee237c 288 rustdoc_document_private_items,
1e682848 289 } = *options;
e4918c45 290 let config = ws.config();
3656bec8 291
d17ccec2
HY
292 match build_config.mode {
293 CompileMode::Test
294 | CompileMode::Build
295 | CompileMode::Check { .. }
296 | CompileMode::Bench
297 | CompileMode::RunCustomBuild => {
298 if std::env::var("RUST_FLAGS").is_ok() {
299 config.shell().warn(
300 "Cargo does not read `RUST_FLAGS` environment variable. Did you mean `RUSTFLAGS`?",
301 )?;
302 }
303 }
304 CompileMode::Doc { .. } | CompileMode::Doctest => {
305 if std::env::var("RUSTDOC_FLAGS").is_ok() {
306 config.shell().warn(
307 "Cargo does not read `RUSTDOC_FLAGS` environment variable. Did you mean `RUSTDOCFLAGS`?"
308 )?;
309 }
310 }
311 }
312
3fd28143 313 let target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?;
47007d97 314
8947ed1f 315 let specs = spec.to_package_id_specs(ws)?;
e26ef017
EH
316 let dev_deps = ws.require_optional_deps() || filter.need_dev_deps(build_config.mode);
317 let opts = ResolveOpts::new(dev_deps, features, all_features, !no_default_features);
2b1dc3e5
AT
318 let has_dev_units = if filter.need_dev_deps(build_config.mode) {
319 HasDevUnits::Yes
320 } else {
321 HasDevUnits::No
e0d64f94 322 };
d728f386
EH
323 let resolve = ops::resolve_ws_with_opts(
324 ws,
325 &target_data,
3fd28143 326 &build_config.requested_kinds,
d728f386
EH
327 &opts,
328 &specs,
329 has_dev_units,
d267fac2 330 crate::core::resolver::features::ForceAllTargets::No,
d728f386 331 )?;
12392f6d
EH
332 let WorkspaceResolve {
333 mut pkg_set,
334 workspace_resolve,
335 targeted_resolve: resolve,
7caa1612 336 resolved_features,
12392f6d 337 } = resolve;
1f14fa31 338
7caa1612 339 let std_resolve_features = if let Some(crates) = &config.cli_unstable().build_std {
3afb5d7d
EH
340 if build_config.build_plan {
341 config
342 .shell()
343 .warn("-Zbuild-std does not currently fully support --build-plan")?;
344 }
3fd28143 345 if build_config.requested_kinds[0].is_host() {
1f14fa31
EH
346 // TODO: This should eventually be fixed. Unfortunately it is not
347 // easy to get the host triple in BuildConfig. Consider changing
348 // requested_target to an enum, or some other approach.
3a18c89a 349 anyhow::bail!("-Zbuild-std requires --target");
1f14fa31 350 }
98b6f816 351 let (std_package_set, std_resolve, std_features) =
3fd28143 352 standard_lib::resolve_std(ws, &target_data, &build_config.requested_kinds, crates)?;
12392f6d 353 pkg_set.add_set(std_package_set);
7caa1612 354 Some((std_resolve, std_features))
1f14fa31
EH
355 } else {
356 None
357 };
9c296792 358
1f14fa31
EH
359 // Find the packages in the resolver that the user wants to build (those
360 // passed in with `-p` or the defaults from the workspace), and convert
96a39371
EH
361 // Vec<PackageIdSpec> to a Vec<PackageId>.
362 let to_build_ids = resolve.specs_to_ids(&specs)?;
1f14fa31
EH
363 // Now get the `Package` for each `PackageId`. This may trigger a download
364 // if the user specified `-p` for a dependency that is not downloaded.
365 // Dependencies will be downloaded during build_unit_dependencies.
12392f6d 366 let mut to_builds = pkg_set.get_many(to_build_ids)?;
0ea0c8ba 367
7b1a0dc5
AC
368 // The ordering here affects some error messages coming out of cargo, so
369 // let's be test and CLI friendly by always printing in the same order if
370 // there's an error.
371 to_builds.sort_by_key(|p| p.package_id());
372
373 for pkg in to_builds.iter() {
1f14fa31 374 pkg.manifest().print_teapot(config);
786848a0
EH
375
376 if build_config.mode.is_any_test()
377 && !ws.is_member(pkg)
378 && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
379 {
3a18c89a 380 anyhow::bail!(
786848a0
EH
381 "package `{}` cannot be tested because it requires dev-dependencies \
382 and is not a member of the workspace",
383 pkg.name()
384 );
385 }
7b1a0dc5 386 }
f0647ea2 387
575d6e81
EH
388 let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
389 (&Some(ref args), _) => (Some(args.clone()), "rustc"),
390 (_, &Some(ref args)) => (Some(args.clone()), "rustdoc"),
391 _ => (None, ""),
f0647ea2 392 };
b3ade7c7 393
575d6e81
EH
394 if extra_args.is_some() && to_builds.len() != 1 {
395 panic!(
396 "`{}` should not accept multiple `-p` flags",
397 extra_args_name
398 );
399 }
400
e0bd9e23
EH
401 let profiles = Profiles::new(
402 ws.profiles(),
403 config,
404 build_config.requested_profile,
405 ws.features(),
406 )?;
12392f6d 407 profiles.validate_packages(
77ee608d 408 ws.profiles(),
12392f6d
EH
409 &mut config.shell(),
410 workspace_resolve.as_ref().unwrap_or(&resolve),
411 )?;
a0a880c3 412
575d6e81 413 let units = generate_targets(
73660740 414 ws,
575d6e81
EH
415 &to_builds,
416 filter,
3fd28143 417 &build_config.requested_kinds,
e0bd9e23 418 build_config.mode,
12392f6d 419 &resolve,
7caa1612 420 &resolved_features,
e0bd9e23
EH
421 &pkg_set,
422 &profiles,
686ccfa4 423 interner,
575d6e81
EH
424 )?;
425
1f14fa31 426 let std_roots = if let Some(crates) = &config.cli_unstable().build_std {
3ba9de85
EH
427 // Only build libtest if it looks like it is needed.
428 let mut crates = crates.clone();
429 if !crates.iter().any(|c| c == "test")
430 && units
431 .iter()
432 .any(|unit| unit.mode.is_rustc_test() && unit.target.harness())
433 {
18a89732
PO
434 // Only build libtest when libstd is built (libtest depends on libstd)
435 if crates.iter().any(|c| c == "std") {
436 crates.push("test".to_string());
437 }
3ba9de85 438 }
7caa1612 439 let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
593a02f2 440 standard_lib::generate_std_roots(
593a02f2 441 &crates,
7caa1612
EH
442 std_resolve,
443 std_features,
3fd28143 444 &build_config.requested_kinds,
e0bd9e23 445 &pkg_set,
686ccfa4 446 interner,
e0bd9e23 447 &profiles,
593a02f2 448 )?
1f14fa31 449 } else {
3fd28143 450 Default::default()
1f14fa31
EH
451 };
452
e0bd9e23 453 let mut extra_compiler_args = HashMap::new();
575d6e81
EH
454 if let Some(args) = extra_args {
455 if units.len() != 1 {
3a18c89a 456 anyhow::bail!(
575d6e81 457 "extra arguments to `{}` can only be passed to one \
f7c91ba6
AR
458 target, consider filtering\nthe package by passing, \
459 e.g., `--lib` or `--bin NAME` to specify a single target",
575d6e81
EH
460 extra_args_name
461 );
8a8ea1e8 462 }
45d49579 463 extra_compiler_args.insert(units[0].clone(), args);
1ef954ea 464 }
e7ee237c
LK
465 for unit in &units {
466 if unit.mode.is_doc() || unit.mode.is_doc_test() {
467 let mut extra_args = local_rustdoc_args.clone();
468
469 // Add `--document-private-items` rustdoc flag if requested or if
470 // the target is a binary. Binary crates get their private items
471 // documented by default.
472 if rustdoc_document_private_items || unit.target.is_bin() {
a6a395c6 473 let mut args = extra_args.take().unwrap_or_else(|| vec![]);
e7ee237c
LK
474 args.push("--document-private-items".into());
475 extra_args = Some(args);
476 }
477
1d1b3447
JN
478 if let Some(args) = extra_args {
479 extra_compiler_args
480 .entry(unit.clone())
481 .or_default()
482 .extend(args);
1ef954ea
EH
483 }
484 }
b3ade7c7 485 }
575d6e81 486
e0bd9e23
EH
487 let unit_graph = build_unit_dependencies(
488 ws,
489 &pkg_set,
7caa1612
EH
490 &resolve,
491 &resolved_features,
492 std_resolve_features.as_ref(),
493 &units,
494 &std_roots,
e0bd9e23
EH
495 build_config.mode,
496 &target_data,
497 &profiles,
686ccfa4 498 interner,
7caa1612 499 )?;
9a8d695b 500
e0bd9e23
EH
501 let bcx = BuildContext::new(
502 ws,
503 pkg_set,
504 build_config,
505 profiles,
506 extra_compiler_args,
507 target_data,
508 units,
509 unit_graph,
510 )?;
8b840393 511
e0bd9e23 512 Ok(bcx)
62bff631 513}
51c9cf0f 514
bb643cca
AK
515impl FilterRule {
516 pub fn new(targets: Vec<String>, all: bool) -> FilterRule {
2a82378a
BW
517 if all {
518 FilterRule::All
519 } else {
520 FilterRule::Just(targets)
521 }
522 }
523
12307fc2
DW
524 pub fn none() -> FilterRule {
525 FilterRule::Just(Vec::new())
526 }
527
2a82378a
BW
528 fn matches(&self, target: &Target) -> bool {
529 match *self {
530 FilterRule::All => true,
1e682848 531 FilterRule::Just(ref targets) => targets.iter().any(|x| *x == target.name()),
2a82378a
BW
532 }
533 }
534
535 fn is_specific(&self) -> bool {
536 match *self {
537 FilterRule::All => true,
bb643cca 538 FilterRule::Just(ref targets) => !targets.is_empty(),
2a82378a
BW
539 }
540 }
541
542 pub fn try_collect(&self) -> Option<Vec<String>> {
543 match *self {
544 FilterRule::All => None,
bb643cca 545 FilterRule::Just(ref targets) => Some(targets.clone()),
2a82378a
BW
546 }
547 }
548}
549
bb643cca 550impl CompileFilter {
12307fc2
DW
551 /// Construct a CompileFilter from raw command line arguments.
552 pub fn from_raw_arguments(
1e682848
AC
553 lib_only: bool,
554 bins: Vec<String>,
555 all_bins: bool,
556 tsts: Vec<String>,
557 all_tsts: bool,
558 exms: Vec<String>,
559 all_exms: bool,
560 bens: Vec<String>,
561 all_bens: bool,
562 all_targets: bool,
563 ) -> CompileFilter {
5a59b809
EH
564 if all_targets {
565 return CompileFilter::new_all_targets();
566 }
f16efff1
AC
567 let rule_lib = if lib_only {
568 LibRule::True
569 } else {
570 LibRule::False
571 };
2a82378a
BW
572 let rule_bins = FilterRule::new(bins, all_bins);
573 let rule_tsts = FilterRule::new(tsts, all_tsts);
574 let rule_exms = FilterRule::new(exms, all_exms);
575 let rule_bens = FilterRule::new(bens, all_bens);
576
5a59b809 577 CompileFilter::new(rule_lib, rule_bins, rule_tsts, rule_exms, rule_bens)
12307fc2
DW
578 }
579
580 /// Construct a CompileFilter from underlying primitives.
581 pub fn new(
aef99e0e 582 rule_lib: LibRule,
12307fc2
DW
583 rule_bins: FilterRule,
584 rule_tsts: FilterRule,
585 rule_exms: FilterRule,
586 rule_bens: FilterRule,
587 ) -> CompileFilter {
aef99e0e 588 if rule_lib == LibRule::True
dae87a26
E
589 || rule_bins.is_specific()
590 || rule_tsts.is_specific()
591 || rule_exms.is_specific()
592 || rule_bens.is_specific()
1e682848 593 {
7a1d8d94 594 CompileFilter::Only {
b9497ab2 595 all_targets: false,
aef99e0e 596 lib: rule_lib,
1e682848
AC
597 bins: rule_bins,
598 examples: rule_exms,
599 benches: rule_bens,
2a82378a 600 tests: rule_tsts,
7a1d8d94
AC
601 }
602 } else {
6344db05 603 CompileFilter::Default {
25e50b58
JB
604 required_features_filterable: true,
605 }
7a1d8d94
AC
606 }
607 }
608
5a59b809
EH
609 pub fn new_all_targets() -> CompileFilter {
610 CompileFilter::Only {
611 all_targets: true,
612 lib: LibRule::Default,
613 bins: FilterRule::All,
614 examples: FilterRule::All,
615 benches: FilterRule::All,
616 tests: FilterRule::All,
617 }
618 }
619
89d5161d
XL
620 pub fn need_dev_deps(&self, mode: CompileMode) -> bool {
621 match mode {
622 CompileMode::Test | CompileMode::Doctest | CompileMode::Bench => true,
b112255f
EH
623 CompileMode::Check { test: true } => true,
624 CompileMode::Build | CompileMode::Doc { .. } | CompileMode::Check { test: false } => {
625 match *self {
626 CompileFilter::Default { .. } => false,
627 CompileFilter::Only {
628 ref examples,
629 ref tests,
630 ref benches,
631 ..
632 } => examples.is_specific() || tests.is_specific() || benches.is_specific(),
633 }
634 }
575d6e81 635 CompileMode::RunCustomBuild => panic!("Invalid mode"),
7de30dd2
XL
636 }
637 }
638
ce26ddfd 639 // this selects targets for "cargo run". for logic to select targets for
771fec3c 640 // other subcommands, see generate_targets and filter_default_targets
ce26ddfd 641 pub fn target_run(&self, target: &Target) -> bool {
9e779198 642 match *self {
70170d1b 643 CompileFilter::Default { .. } => true,
1e682848 644 CompileFilter::Only {
aef99e0e 645 ref lib,
1e682848
AC
646 ref bins,
647 ref examples,
648 ref tests,
649 ref benches,
650 ..
651 } => {
2a82378a 652 let rule = match *target.kind() {
9e779198
AC
653 TargetKind::Bin => bins,
654 TargetKind::Test => tests,
655 TargetKind::Bench => benches,
1e682848 656 TargetKind::ExampleBin | TargetKind::ExampleLib(..) => examples,
f16efff1
AC
657 TargetKind::Lib(..) => {
658 return match *lib {
659 LibRule::True => true,
660 LibRule::Default => true,
661 LibRule::False => false,
662 };
663 }
9e779198
AC
664 TargetKind::CustomBuild => return false,
665 };
2a82378a 666 rule.matches(target)
9e779198
AC
667 }
668 }
669 }
c98b8c4c
BW
670
671 pub fn is_specific(&self) -> bool {
672 match *self {
6344db05 673 CompileFilter::Default { .. } => false,
c98b8c4c
BW
674 CompileFilter::Only { .. } => true,
675 }
676 }
9e779198
AC
677}
678
3a1cad6f
EH
679/// A proposed target.
680///
f7c91ba6 681/// Proposed targets are later filtered into actual `Unit`s based on whether or
3a1cad6f
EH
682/// not the target requires its features to be present.
683#[derive(Debug)]
684struct Proposal<'a> {
fbd34a69 685 pkg: &'a Package,
45d49579 686 target: &'a Target,
3a1cad6f
EH
687 /// Indicates whether or not all required features *must* be present. If
688 /// false, and the features are not available, then it will be silently
689 /// skipped. Generally, targets specified by name (`--bin foo`) are
690 /// required, all others can be silently skipped if features are missing.
691 requires_features: bool,
692 mode: CompileMode,
693}
694
575d6e81 695/// Generates all the base targets for the packages the user has requested to
f7c91ba6 696/// compile. Dependencies for these targets are computed later in `unit_dependencies`.
c85ed044 697fn generate_targets(
b8b7faee 698 ws: &Workspace<'_>,
fbd34a69 699 packages: &[&Package],
575d6e81 700 filter: &CompileFilter,
3fd28143 701 requested_kinds: &[CompileKind],
e0bd9e23
EH
702 mode: CompileMode,
703 resolve: &Resolve,
7caa1612 704 resolved_features: &features::ResolvedFeatures,
e0bd9e23
EH
705 package_set: &PackageSet<'_>,
706 profiles: &Profiles,
c85ed044
AC
707 interner: &UnitInterner,
708) -> CargoResult<Vec<Unit>> {
e0bd9e23 709 let config = ws.config();
3fd28143
AC
710 // Helper for creating a list of `Unit` structures
711 let new_unit =
712 |units: &mut HashSet<Unit>, pkg: &Package, target: &Target, target_mode: CompileMode| {
713 let unit_for = if target_mode.is_any_test() {
714 // NOTE: the `UnitFor` here is subtle. If you have a profile
715 // with `panic` set, the `panic` flag is cleared for
716 // tests/benchmarks and their dependencies. If this
717 // was `normal`, then the lib would get compiled three
718 // times (once with panic, once without, and once with
719 // `--test`).
720 //
721 // This would cause a problem for doc tests, which would fail
722 // because `rustdoc` would attempt to link with both libraries
723 // at the same time. Also, it's probably not important (or
724 // even desirable?) for rustdoc to link with a lib with
725 // `panic` set.
726 //
727 // As a consequence, Examples and Binaries get compiled
728 // without `panic` set. This probably isn't a bad deal.
729 //
730 // Forcing the lib to be compiled three times during `cargo
731 // test` is probably also not desirable.
732 UnitFor::new_test(config)
733 } else if target.for_host() {
734 // Proc macro / plugin should not have `panic` set.
735 UnitFor::new_compiler()
736 } else {
737 UnitFor::new_normal()
738 };
739 // Custom build units are added in `build_unit_dependencies`.
740 assert!(!target.is_custom_build());
741 let target_mode = match target_mode {
742 CompileMode::Test => {
743 if target.is_example() && !filter.is_specific() && !target.tested() {
744 // Examples are included as regular binaries to verify
745 // that they compile.
746 CompileMode::Build
747 } else {
748 CompileMode::Test
749 }
2a82378a 750 }
3fd28143
AC
751 CompileMode::Build => match *target.kind() {
752 TargetKind::Test => CompileMode::Test,
753 TargetKind::Bench => CompileMode::Bench,
754 _ => CompileMode::Build,
755 },
756 // `CompileMode::Bench` is only used to inform `filter_default_targets`
757 // which command is being used (`cargo bench`). Afterwards, tests
758 // and benches are treated identically. Switching the mode allows
759 // de-duplication of units that are essentially identical. For
760 // example, `cargo build --all-targets --release` creates the units
761 // (lib profile:bench, mode:test) and (lib profile:bench, mode:bench)
762 // and since these are the same, we want them to be de-duplicated in
763 // `unit_dependencies`.
764 CompileMode::Bench => CompileMode::Test,
765 _ => target_mode,
766 };
767
768 let is_local = pkg.package_id().source_id().is_path();
769 let profile = profiles.get_profile(
770 pkg.package_id(),
771 ws.is_member(pkg),
772 is_local,
773 unit_for,
774 target_mode,
775 );
776
777 // No need to worry about build-dependencies, roots are never build dependencies.
778 let features_for = FeaturesFor::from_for_host(target.proc_macro());
779 let features = resolved_features.activated_features(pkg.package_id(), features_for);
780
781 for kind in requested_kinds {
782 let unit = interner.intern(
783 pkg,
784 target,
785 profile,
786 kind.for_target(target),
787 target_mode,
788 features.clone(),
789 /*is_std*/ false,
790 );
791 units.insert(unit);
2a82378a 792 }
575d6e81
EH
793 };
794
3a1cad6f 795 // Create a list of proposed targets.
b8b7faee 796 let mut proposals: Vec<Proposal<'_>> = Vec::new();
771fec3c
EH
797
798 match *filter {
799 CompileFilter::Default {
800 required_features_filterable,
801 } => {
802 for pkg in packages {
e0bd9e23 803 let default = filter_default_targets(pkg.targets(), mode);
3a1cad6f
EH
804 proposals.extend(default.into_iter().map(|target| Proposal {
805 pkg,
806 target,
807 requires_features: !required_features_filterable,
e0bd9e23 808 mode,
771fec3c 809 }));
e0bd9e23 810 if mode == CompileMode::Test {
dd6c6102
EH
811 if let Some(t) = pkg
812 .targets()
813 .iter()
814 .find(|t| t.is_lib() && t.doctested() && t.doctestable())
815 {
3a1cad6f
EH
816 proposals.push(Proposal {
817 pkg,
818 target: t,
819 requires_features: false,
820 mode: CompileMode::Doctest,
821 });
575d6e81 822 }
2a82378a
BW
823 }
824 }
771fec3c
EH
825 }
826 CompileFilter::Only {
827 all_targets,
aef99e0e 828 ref lib,
771fec3c
EH
829 ref bins,
830 ref examples,
831 ref tests,
832 ref benches,
833 } => {
aef99e0e 834 if *lib != LibRule::False {
771fec3c 835 let mut libs = Vec::new();
e0bd9e23 836 for proposal in filter_targets(packages, Target::is_lib, false, mode) {
df9ca871 837 let Proposal { target, pkg, .. } = proposal;
e0bd9e23 838 if mode.is_doc_test() && !target.doctestable() {
7a06be14
EH
839 let types = target.rustc_crate_types();
840 let types_str: Vec<&str> = types.iter().map(|t| t.as_str()).collect();
df9ca871
DW
841 ws.config().shell().warn(format!(
842 "doc tests are not supported for crate type(s) `{}` in package `{}`",
7a06be14 843 types_str.join(", "),
df9ca871
DW
844 pkg.name()
845 ))?;
846 } else {
847 libs.push(proposal)
575d6e81
EH
848 }
849 }
aef99e0e 850 if !all_targets && libs.is_empty() && *lib == LibRule::True {
771fec3c
EH
851 let names = packages.iter().map(|pkg| pkg.name()).collect::<Vec<_>>();
852 if names.len() == 1 {
3a18c89a 853 anyhow::bail!("no library targets found in package `{}`", names[0]);
771fec3c 854 } else {
3a18c89a 855 anyhow::bail!("no library targets found in packages: {}", names.join(", "));
771fec3c
EH
856 }
857 }
858 proposals.extend(libs);
575d6e81 859 }
3a1cad6f 860
f7c91ba6 861 // If `--tests` was specified, add all targets that would be
771fec3c 862 // generated by `cargo test`.
df9ca871 863 let test_filter = match tests {
771fec3c
EH
864 FilterRule::All => Target::tested,
865 FilterRule::Just(_) => Target::is_test,
866 };
e0bd9e23 867 let test_mode = match mode {
771fec3c
EH
868 CompileMode::Build => CompileMode::Test,
869 CompileMode::Check { .. } => CompileMode::Check { test: true },
e0bd9e23 870 _ => mode,
771fec3c 871 };
f7c91ba6 872 // If `--benches` was specified, add all targets that would be
771fec3c 873 // generated by `cargo bench`.
df9ca871 874 let bench_filter = match benches {
771fec3c
EH
875 FilterRule::All => Target::benched,
876 FilterRule::Just(_) => Target::is_bench,
877 };
e0bd9e23 878 let bench_mode = match mode {
771fec3c
EH
879 CompileMode::Build => CompileMode::Bench,
880 CompileMode::Check { .. } => CompileMode::Check { test: true },
e0bd9e23 881 _ => mode,
771fec3c
EH
882 };
883
884 proposals.extend(list_rule_targets(
885 packages,
886 bins,
887 "bin",
888 Target::is_bin,
e0bd9e23 889 mode,
771fec3c
EH
890 )?);
891 proposals.extend(list_rule_targets(
892 packages,
893 examples,
894 "example",
895 Target::is_example,
e0bd9e23 896 mode,
771fec3c
EH
897 )?);
898 proposals.extend(list_rule_targets(
899 packages,
900 tests,
901 "test",
902 test_filter,
903 test_mode,
904 )?);
905 proposals.extend(list_rule_targets(
906 packages,
907 benches,
908 "bench",
909 bench_filter,
910 bench_mode,
911 )?);
2a82378a 912 }
771fec3c 913 }
2a82378a 914
771fec3c
EH
915 // Only include targets that are libraries or have all required
916 // features available.
7caa1612
EH
917 //
918 // `features_map` is a map of &Package -> enabled_features
919 // It is computed by the set of enabled features for the package plus
920 // every enabled feature of every enabled dependency.
771fec3c 921 let mut features_map = HashMap::new();
3a1cad6f 922 let mut units = HashSet::new();
dae87a26
E
923 for Proposal {
924 pkg,
925 target,
926 requires_features,
927 mode,
928 } in proposals
929 {
771fec3c
EH
930 let unavailable_features = match target.required_features() {
931 Some(rf) => {
7caa1612 932 let features = features_map.entry(pkg).or_insert_with(|| {
e0bd9e23 933 resolve_all_features(resolve, resolved_features, package_set, pkg.package_id())
7caa1612 934 });
771fec3c 935 rf.iter().filter(|f| !features.contains(*f)).collect()
2a82378a 936 }
771fec3c
EH
937 None => Vec::new(),
938 };
939 if target.is_lib() || unavailable_features.is_empty() {
3fd28143 940 new_unit(&mut units, pkg, target, mode);
3a1cad6f 941 } else if requires_features {
771fec3c
EH
942 let required_features = target.required_features().unwrap();
943 let quoted_required_features: Vec<String> = required_features
944 .iter()
945 .map(|s| format!("`{}`", s))
946 .collect();
3a18c89a 947 anyhow::bail!(
771fec3c 948 "target `{}` in package `{}` requires the features: {}\n\
f7c91ba6 949 Consider enabling them by passing, e.g., `--features=\"{}\"`",
771fec3c
EH
950 target.name(),
951 pkg.name(),
952 quoted_required_features.join(", "),
953 required_features.join(" ")
954 );
2a82378a 955 }
771fec3c 956 // else, silently skip target.
2a82378a 957 }
3a1cad6f 958 Ok(units.into_iter().collect())
2a82378a
BW
959}
960
3f068230
EH
961/// Gets all of the features enabled for a package, plus its dependencies'
962/// features.
963///
964/// Dependencies are added as `dep_name/feat_name` because `required-features`
965/// wants to support that syntax.
8b17895b 966pub fn resolve_all_features(
575d6e81 967 resolve_with_overrides: &Resolve,
7caa1612 968 resolved_features: &features::ResolvedFeatures,
944f5049 969 package_set: &PackageSet<'_>,
dae87a26 970 package_id: PackageId,
575d6e81 971) -> HashSet<String> {
7caa1612 972 let mut features: HashSet<String> = resolved_features
137642c4 973 .activated_features(package_id, FeaturesFor::NormalOrDev)
7caa1612
EH
974 .iter()
975 .map(|s| s.to_string())
976 .collect();
575d6e81
EH
977
978 // Include features enabled for use by dependencies so targets can also use them with the
979 // required-features field when deciding whether to be built or skipped.
cafec114 980 for (dep_id, deps) in resolve_with_overrides.deps(package_id) {
944f5049
EH
981 let is_proc_macro = package_set
982 .get_one(dep_id)
983 .expect("packages downloaded")
984 .proc_macro();
8973b959 985 for dep in deps {
96a39371 986 let features_for = FeaturesFor::from_for_host(is_proc_macro || dep.is_build());
e94facea
R
987 for feature in resolved_features
988 .activated_features_unverified(dep_id, features_for)
989 .unwrap_or_default()
990 {
887ee6cc 991 features.insert(format!("{}/{}", dep.name_in_toml(), feature));
cafec114 992 }
575d6e81
EH
993 }
994 }
995
996 features
997}
998
999/// Given a list of all targets for a package, filters out only the targets
1000/// that are automatically included when the user doesn't specify any targets.
45d49579 1001fn filter_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Target> {
575d6e81
EH
1002 match mode {
1003 CompileMode::Bench => targets.iter().filter(|t| t.benched()).collect(),
a4947c2b
EH
1004 CompileMode::Test => targets
1005 .iter()
1006 .filter(|t| t.tested() || t.is_example())
1007 .collect(),
575d6e81
EH
1008 CompileMode::Build | CompileMode::Check { .. } => targets
1009 .iter()
1010 .filter(|t| t.is_bin() || t.is_lib())
1011 .collect(),
1012 CompileMode::Doc { .. } => {
1013 // `doc` does lib and bins (bin with same name as lib is skipped).
1014 targets
1e682848 1015 .iter()
575d6e81
EH
1016 .filter(|t| {
1017 t.documented()
1018 && (!t.is_bin()
1019 || !targets.iter().any(|l| l.is_lib() && l.name() == t.name()))
1020 })
1021 .collect()
2a82378a 1022 }
00d325db 1023 CompileMode::Doctest | CompileMode::RunCustomBuild => panic!("Invalid mode {:?}", mode),
2a82378a 1024 }
2a82378a
BW
1025}
1026
df9ca871 1027/// Returns a list of proposed targets based on command-line target selection flags.
575d6e81 1028fn list_rule_targets<'a>(
fbd34a69 1029 packages: &[&'a Package],
575d6e81
EH
1030 rule: &FilterRule,
1031 target_desc: &'static str,
1032 is_expected_kind: fn(&Target) -> bool,
771fec3c 1033 mode: CompileMode,
3a1cad6f 1034) -> CargoResult<Vec<Proposal<'a>>> {
df9ca871
DW
1035 let mut proposals = Vec::new();
1036 match rule {
771fec3c 1037 FilterRule::All => {
df9ca871 1038 proposals.extend(filter_targets(packages, is_expected_kind, false, mode))
771fec3c 1039 }
df9ca871 1040 FilterRule::Just(names) => {
771fec3c 1041 for name in names {
df9ca871 1042 proposals.extend(find_named_targets(
771fec3c
EH
1043 packages,
1044 name,
1045 target_desc,
1046 is_expected_kind,
1047 mode,
1048 )?);
1049 }
1050 }
575d6e81 1051 }
df9ca871 1052 Ok(proposals)
575d6e81 1053}
235712f5 1054
f7c91ba6 1055/// Finds the targets for a specifically named target.
771fec3c 1056fn find_named_targets<'a>(
fbd34a69 1057 packages: &[&'a Package],
575d6e81
EH
1058 target_name: &str,
1059 target_desc: &'static str,
1060 is_expected_kind: fn(&Target) -> bool,
771fec3c 1061 mode: CompileMode,
3a1cad6f 1062) -> CargoResult<Vec<Proposal<'a>>> {
df9ca871
DW
1063 let filter = |t: &Target| t.name() == target_name && is_expected_kind(t);
1064 let proposals = filter_targets(packages, filter, true, mode);
1065 if proposals.is_empty() {
7d7fe679
EH
1066 let targets = packages.iter().flat_map(|pkg| {
1067 pkg.targets()
1068 .iter()
1069 .filter(|target| is_expected_kind(target))
1070 });
1071 let suggestion = closest_msg(target_name, targets, |t| t.name());
3a18c89a 1072 anyhow::bail!(
7d7fe679
EH
1073 "no {} target named `{}`{}",
1074 target_desc,
1075 target_name,
1076 suggestion
1077 );
771fec3c 1078 }
df9ca871
DW
1079 Ok(proposals)
1080}
1081
1082fn filter_targets<'a>(
fbd34a69 1083 packages: &[&'a Package],
df9ca871
DW
1084 predicate: impl Fn(&Target) -> bool,
1085 requires_features: bool,
1086 mode: CompileMode,
1087) -> Vec<Proposal<'a>> {
1088 let mut proposals = Vec::new();
1089 for pkg in packages {
1090 for target in pkg.targets().iter().filter(|t| predicate(t)) {
1091 proposals.push(Proposal {
1092 pkg,
1093 target,
1094 requires_features,
1095 mode,
1096 });
1097 }
1098 }
1099 proposals
9e779198 1100}