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