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