]> git.proxmox.com Git - cargo.git/blame - src/cargo/ops/cargo_compile.rs
Upgrade to Rust 2018
[cargo.git] / src / cargo / ops / cargo_compile.rs
CommitLineData
64ff29ff
AC
1//!
2//! Cargo compile currently does the following steps:
3//!
4//! All configurations are already injected as environment variables via the
5//! main cargo command
6//!
7//! 1. Read the manifest
8//! 2. Shell out to `cargo-resolve` with a list of dependencies and sources as
9//! stdin
10//!
11//! a. Shell out to `--do update` and `--do list` for each source
12//! b. Resolve dependencies and return a list of name/version/source
13//!
14//! 3. Shell out to `--do download` for each source
15//! 4. Shell out to `--do get` for each source, and build up the list of paths
16//! to pass to rustc -L
17//! 5. Call `cargo-rustc` with the results of the resolver zipped together with
18//! the results of the `get`
19//!
20//! a. Topologically sort the dependencies
21//! b. Compile each dependency in order, passing in the -L's pointing at each
22//! previously compiled dependency
23//!
62bff631 24
771fec3c 25use std::collections::{HashMap, HashSet};
8cf53bca 26use std::path::PathBuf;
1ab19a5a 27use std::sync::Arc;
12af54e1 28
04ddd4d0
DW
29use crate::core::compiler::{BuildConfig, BuildContext, Compilation, Context, DefaultExecutor, Executor};
30use crate::core::compiler::{CompileMode, Kind, Unit};
31use crate::core::profiles::{Profiles, UnitFor};
32use crate::core::resolver::{Method, Resolve};
33use crate::core::{Package, Source, Target};
34use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
35use crate::ops;
36use crate::util::config::Config;
37use crate::util::{lev_distance, profile, CargoResult};
50f110a4 38
bacb6be3 39/// Contains information about how a package should be compiled.
876af7c1 40#[derive(Debug)]
2fe0bf83
AC
41pub struct CompileOptions<'a> {
42 pub config: &'a Config,
08025169
DO
43 /// Configuration information for a rustc build
44 pub build_config: BuildConfig,
9e779198 45 /// Extra features to build for the root package
bb643cca 46 pub features: Vec<String>,
1f8b3988
DA
47 /// Flag whether all available features should be built for the root package
48 pub all_features: bool,
9e779198 49 /// Flag if the default feature should be built for the root package
2b46d039 50 pub no_default_features: bool,
6b32c4d7 51 /// A set of packages to build.
bb643cca 52 pub spec: Packages,
9e779198
AC
53 /// Filter to apply to the root package to select which targets will be
54 /// built.
bb643cca 55 pub filter: CompileFilter,
1ef954ea 56 /// Extra arguments to be passed to rustdoc (single target only)
bb643cca 57 pub target_rustdoc_args: Option<Vec<String>>,
77bb01ec
SL
58 /// The specified target will be compiled with all the available arguments,
59 /// note that this only accounts for the *final* invocation of rustc
bb643cca 60 pub target_rustc_args: Option<Vec<String>>,
1ef954ea
EH
61 /// Extra arguments passed to all selected targets for rustdoc.
62 pub local_rustdoc_args: Option<Vec<String>>,
5baac6b3
AK
63 /// The directory to copy final artifacts to. Note that even if `out_dir` is
64 /// set, a copy of artifacts still could be found a `target/(debug\release)`
65 /// as usual.
66 // Note that, although the cmd-line flag name is `out-dir`, in code we use
67 // `export_dir`, to avoid confusion with out dir at `target/debug/deps`.
68 pub export_dir: Option<PathBuf>,
9e779198
AC
69}
70
69caa63b 71impl<'a> CompileOptions<'a> {
072254c3 72 pub fn new(config: &'a Config, mode: CompileMode) -> CargoResult<CompileOptions<'a>> {
08025169 73 Ok(CompileOptions {
0247dc42 74 config,
08025169 75 build_config: BuildConfig::new(config, None, &None, mode)?,
bb643cca 76 features: Vec::new(),
69caa63b
NC
77 all_features: false,
78 no_default_features: false,
bb643cca 79 spec: ops::Packages::Packages(Vec::new()),
1e682848
AC
80 filter: CompileFilter::Default {
81 required_features_filterable: false,
82 },
69caa63b
NC
83 target_rustdoc_args: None,
84 target_rustc_args: None,
1ef954ea 85 local_rustdoc_args: None,
5baac6b3 86 export_dir: None,
08025169 87 })
69caa63b
NC
88 }
89}
90
bb643cca
AK
91#[derive(Clone, PartialEq, Eq, Debug)]
92pub enum Packages {
ba7911dd 93 Default,
62c04979 94 All,
bb643cca
AK
95 OptOut(Vec<String>),
96 Packages(Vec<String>),
62c04979
AR
97}
98
bb643cca 99impl Packages {
1e682848 100 pub fn from_flags(all: bool, exclude: Vec<String>, package: Vec<String>) -> CargoResult<Self> {
ba7911dd
SS
101 Ok(match (all, exclude.len(), package.len()) {
102 (false, 0, 0) => Packages::Default,
103 (false, 0, _) => Packages::Packages(package),
104 (false, _, _) => bail!("--exclude can only be used together with --all"),
105 (true, 0, _) => Packages::All,
106 (true, _, _) => Packages::OptOut(exclude),
107 })
7ffd1cfc 108 }
109
8947ed1f 110 pub fn to_package_id_specs(&self, ws: &Workspace) -> CargoResult<Vec<PackageIdSpec>> {
bb643cca 111 let specs = match *self {
dae87a26
E
112 Packages::All => ws
113 .members()
1e682848
AC
114 .map(Package::package_id)
115 .map(PackageIdSpec::from_package_id)
116 .collect(),
dae87a26
E
117 Packages::OptOut(ref opt_out) => ws
118 .members()
1e682848
AC
119 .map(Package::package_id)
120 .map(PackageIdSpec::from_package_id)
121 .filter(|p| opt_out.iter().position(|x| *x == p.name()).is_none())
122 .collect(),
037a879d
EH
123 Packages::Packages(ref packages) if packages.is_empty() => {
124 vec![PackageIdSpec::from_package_id(ws.current()?.package_id())]
125 }
1e682848
AC
126 Packages::Packages(ref packages) => packages
127 .iter()
128 .map(|p| PackageIdSpec::parse(p))
129 .collect::<CargoResult<Vec<_>>>()?,
dae87a26
E
130 Packages::Default => ws
131 .default_members()
1e682848
AC
132 .map(Package::package_id)
133 .map(PackageIdSpec::from_package_id)
134 .collect(),
6b32c4d7 135 };
68a681ff 136 if specs.is_empty() {
691f95de 137 if ws.is_virtual() {
1e682848
AC
138 bail!(
139 "manifest path `{}` contains no package: The manifest is virtual, \
140 and the workspace has no members.",
141 ws.root().display()
142 )
05b896ac 143 }
691f95de 144 bail!("no packages to compile")
68a681ff 145 }
6b32c4d7
AK
146 Ok(specs)
147 }
fa54a794
EH
148
149 pub fn get_packages<'ws>(&self, ws: &'ws Workspace) -> CargoResult<Vec<&'ws Package>> {
150 let packages: Vec<_> = match self {
151 Packages::Default => ws.default_members().collect(),
152 Packages::All => ws.members().collect(),
153 Packages::OptOut(ref opt_out) => ws
154 .members()
155 .filter(|pkg| !opt_out.iter().any(|name| pkg.name().as_str() == name))
156 .collect(),
157 Packages::Packages(ref pkgs) => pkgs
158 .iter()
159 .map(|name| {
160 ws.members()
161 .find(|pkg| pkg.name().as_str() == name)
162 .ok_or_else(|| {
163 format_err!("package `{}` is not a member of the workspace", name)
164 })
dae87a26
E
165 })
166 .collect::<CargoResult<Vec<_>>>()?,
fa54a794
EH
167 };
168 Ok(packages)
169 }
6b32c4d7
AK
170}
171
bb643cca
AK
172#[derive(Debug)]
173pub enum FilterRule {
2a82378a 174 All,
bb643cca 175 Just(Vec<String>),
2a82378a
BW
176}
177
876af7c1 178#[derive(Debug)]
bb643cca 179pub enum CompileFilter {
6344db05 180 Default {
25e50b58
JB
181 /// Flag whether targets can be safely skipped when required-features are not satisfied.
182 required_features_filterable: bool,
183 },
9e779198 184 Only {
b9497ab2 185 all_targets: bool,
9e779198 186 lib: bool,
bb643cca
AK
187 bins: FilterRule,
188 examples: FilterRule,
189 tests: FilterRule,
190 benches: FilterRule,
1e682848 191 },
a1980dc7
YKCL
192}
193
1e682848
AC
194pub fn compile<'a>(
195 ws: &Workspace<'a>,
196 options: &CompileOptions<'a>,
a340ba0b 197) -> CargoResult<Compilation<'a>> {
689f412c
DW
198 let exec: Arc<Executor> = Arc::new(DefaultExecutor);
199 compile_with_exec(ws, options, &exec)
8c3b3605
NC
200}
201
4a64d05e 202/// Like `compile` but allows specifying a custom `Executor` that will be able to intercept build
d70f91ee 203/// calls and add custom logic. `compile` uses `DefaultExecutor` which just passes calls through.
1e682848
AC
204pub fn compile_with_exec<'a>(
205 ws: &Workspace<'a>,
206 options: &CompileOptions<'a>,
689f412c 207 exec: &Arc<Executor>,
a340ba0b 208) -> CargoResult<Compilation<'a>> {
688a4fa6 209 ws.emit_warnings()?;
8c3b3605 210 compile_ws(ws, None, options, exec)
9fba127e
AC
211}
212
1e682848
AC
213pub fn compile_ws<'a>(
214 ws: &Workspace<'a>,
215 source: Option<Box<Source + 'a>>,
216 options: &CompileOptions<'a>,
689f412c 217 exec: &Arc<Executor>,
a340ba0b 218) -> CargoResult<Compilation<'a>> {
1e682848
AC
219 let CompileOptions {
220 config,
08025169 221 ref build_config,
1e682848
AC
222 ref spec,
223 ref features,
224 all_features,
225 no_default_features,
1e682848
AC
226 ref filter,
227 ref target_rustdoc_args,
228 ref target_rustc_args,
1ef954ea 229 ref local_rustdoc_args,
5baac6b3 230 ref export_dir,
1e682848 231 } = *options;
3656bec8 232
575d6e81
EH
233 let default_arch_kind = if build_config.requested_target.is_some() {
234 Kind::Target
235 } else {
236 Kind::Host
237 };
a9562433 238
8947ed1f 239 let specs = spec.to_package_id_specs(ws)?;
7de30dd2
XL
240 let features = Method::split_features(features);
241 let method = Method::Required {
08025169 242 dev_deps: ws.require_optional_deps() || filter.need_dev_deps(build_config.mode),
7de30dd2
XL
243 features: &features,
244 all_features,
245 uses_default_features: !no_default_features,
246 };
1e682848 247 let resolve = ops::resolve_ws_with_method(ws, source, method, &specs)?;
6b32c4d7 248 let (packages, resolve_with_overrides) = resolve;
9c296792 249
dae87a26
E
250 let to_build_ids = specs
251 .iter()
e2637b65
AC
252 .map(|s| s.query(resolve_with_overrides.iter()))
253 .collect::<CargoResult<Vec<_>>>()?;
254 let mut to_builds = packages.get_many(to_build_ids)?;
0ea0c8ba 255
7b1a0dc5
AC
256 // The ordering here affects some error messages coming out of cargo, so
257 // let's be test and CLI friendly by always printing in the same order if
258 // there's an error.
259 to_builds.sort_by_key(|p| p.package_id());
260
261 for pkg in to_builds.iter() {
262 pkg.manifest().print_teapot(ws.config());
786848a0
EH
263
264 if build_config.mode.is_any_test()
265 && !ws.is_member(pkg)
266 && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
267 {
268 bail!(
269 "package `{}` cannot be tested because it requires dev-dependencies \
270 and is not a member of the workspace",
271 pkg.name()
272 );
273 }
7b1a0dc5 274 }
f0647ea2 275
575d6e81
EH
276 let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
277 (&Some(ref args), _) => (Some(args.clone()), "rustc"),
278 (_, &Some(ref args)) => (Some(args.clone()), "rustdoc"),
279 _ => (None, ""),
f0647ea2 280 };
b3ade7c7 281
575d6e81
EH
282 if extra_args.is_some() && to_builds.len() != 1 {
283 panic!(
284 "`{}` should not accept multiple `-p` flags",
285 extra_args_name
286 );
287 }
288
289 let profiles = ws.profiles();
5415a341 290 profiles.validate_packages(&mut config.shell(), &packages)?;
a0a880c3 291
575d6e81 292 let units = generate_targets(
73660740
EH
293 ws,
294 profiles,
575d6e81
EH
295 &to_builds,
296 filter,
297 default_arch_kind,
575d6e81 298 &resolve_with_overrides,
08025169 299 build_config,
575d6e81
EH
300 )?;
301
1ef954ea 302 let mut extra_compiler_args = HashMap::new();
575d6e81
EH
303 if let Some(args) = extra_args {
304 if units.len() != 1 {
305 bail!(
306 "extra arguments to `{}` can only be passed to one \
307 target, consider filtering\nthe package by passing \
308 e.g. `--lib` or `--bin NAME` to specify a single target",
309 extra_args_name
310 );
8a8ea1e8 311 }
1ef954ea
EH
312 extra_compiler_args.insert(units[0], args);
313 }
314 if let Some(args) = local_rustdoc_args {
315 for unit in &units {
316 if unit.mode.is_doc() {
317 extra_compiler_args.insert(*unit, args.clone());
318 }
319 }
b3ade7c7 320 }
575d6e81 321
00d325db 322 let ret = {
4f12a2b5 323 let _p = profile::start("compiling");
c32e395c 324 let bcx = BuildContext::new(
1e682848 325 ws,
1e682848 326 &resolve_with_overrides,
3f3f27be 327 &packages,
1e682848 328 config,
11a7cbdb 329 &build_config,
1e682848 330 profiles,
575d6e81 331 extra_compiler_args,
3f3f27be 332 )?;
90d0b120 333 let cx = Context::new(config, &bcx)?;
19b854d8 334 cx.compile(&units, export_dir.clone(), &exec)?
0484cd88 335 };
8b840393 336
385b54b3 337 Ok(ret)
62bff631 338}
51c9cf0f 339
bb643cca
AK
340impl FilterRule {
341 pub fn new(targets: Vec<String>, all: bool) -> FilterRule {
2a82378a
BW
342 if all {
343 FilterRule::All
344 } else {
345 FilterRule::Just(targets)
346 }
347 }
348
349 fn matches(&self, target: &Target) -> bool {
350 match *self {
351 FilterRule::All => true,
1e682848 352 FilterRule::Just(ref targets) => targets.iter().any(|x| *x == target.name()),
2a82378a
BW
353 }
354 }
355
356 fn is_specific(&self) -> bool {
357 match *self {
358 FilterRule::All => true,
bb643cca 359 FilterRule::Just(ref targets) => !targets.is_empty(),
2a82378a
BW
360 }
361 }
362
363 pub fn try_collect(&self) -> Option<Vec<String>> {
364 match *self {
365 FilterRule::All => None,
bb643cca 366 FilterRule::Just(ref targets) => Some(targets.clone()),
2a82378a
BW
367 }
368 }
369}
370
bb643cca 371impl CompileFilter {
1e682848
AC
372 pub fn new(
373 lib_only: bool,
374 bins: Vec<String>,
375 all_bins: bool,
376 tsts: Vec<String>,
377 all_tsts: bool,
378 exms: Vec<String>,
379 all_exms: bool,
380 bens: Vec<String>,
381 all_bens: bool,
382 all_targets: bool,
383 ) -> CompileFilter {
2a82378a
BW
384 let rule_bins = FilterRule::new(bins, all_bins);
385 let rule_tsts = FilterRule::new(tsts, all_tsts);
386 let rule_exms = FilterRule::new(exms, all_exms);
387 let rule_bens = FilterRule::new(bens, all_bens);
388
6344db05
SK
389 if all_targets {
390 CompileFilter::Only {
b9497ab2 391 all_targets: true,
1e682848
AC
392 lib: true,
393 bins: FilterRule::All,
394 examples: FilterRule::All,
395 benches: FilterRule::All,
6344db05
SK
396 tests: FilterRule::All,
397 }
dae87a26
E
398 } else if lib_only
399 || rule_bins.is_specific()
400 || rule_tsts.is_specific()
401 || rule_exms.is_specific()
402 || rule_bens.is_specific()
1e682848 403 {
7a1d8d94 404 CompileFilter::Only {
b9497ab2 405 all_targets: false,
1e682848
AC
406 lib: lib_only,
407 bins: rule_bins,
408 examples: rule_exms,
409 benches: rule_bens,
2a82378a 410 tests: rule_tsts,
7a1d8d94
AC
411 }
412 } else {
6344db05 413 CompileFilter::Default {
25e50b58
JB
414 required_features_filterable: true,
415 }
7a1d8d94
AC
416 }
417 }
418
89d5161d
XL
419 pub fn need_dev_deps(&self, mode: CompileMode) -> bool {
420 match mode {
421 CompileMode::Test | CompileMode::Doctest | CompileMode::Bench => true,
422 CompileMode::Build | CompileMode::Doc { .. } | CompileMode::Check { .. } => match *self
423 {
424 CompileFilter::Default { .. } => false,
425 CompileFilter::Only {
426 ref examples,
427 ref tests,
428 ref benches,
429 ..
430 } => examples.is_specific() || tests.is_specific() || benches.is_specific(),
431 },
575d6e81 432 CompileMode::RunCustomBuild => panic!("Invalid mode"),
7de30dd2
XL
433 }
434 }
435
ce26ddfd 436 // this selects targets for "cargo run". for logic to select targets for
771fec3c 437 // other subcommands, see generate_targets and filter_default_targets
ce26ddfd 438 pub fn target_run(&self, target: &Target) -> bool {
9e779198 439 match *self {
70170d1b 440 CompileFilter::Default { .. } => true,
1e682848
AC
441 CompileFilter::Only {
442 lib,
443 ref bins,
444 ref examples,
445 ref tests,
446 ref benches,
447 ..
448 } => {
2a82378a 449 let rule = match *target.kind() {
9e779198
AC
450 TargetKind::Bin => bins,
451 TargetKind::Test => tests,
452 TargetKind::Bench => benches,
1e682848 453 TargetKind::ExampleBin | TargetKind::ExampleLib(..) => examples,
9e779198
AC
454 TargetKind::Lib(..) => return lib,
455 TargetKind::CustomBuild => return false,
456 };
2a82378a 457 rule.matches(target)
9e779198
AC
458 }
459 }
460 }
c98b8c4c
BW
461
462 pub fn is_specific(&self) -> bool {
463 match *self {
6344db05 464 CompileFilter::Default { .. } => false,
c98b8c4c
BW
465 CompileFilter::Only { .. } => true,
466 }
467 }
9e779198
AC
468}
469
3a1cad6f
EH
470/// A proposed target.
471///
472/// Proposed targets are later filtered into actual Units based on whether or
473/// not the target requires its features to be present.
474#[derive(Debug)]
475struct Proposal<'a> {
476 pkg: &'a Package,
477 target: &'a Target,
478 /// Indicates whether or not all required features *must* be present. If
479 /// false, and the features are not available, then it will be silently
480 /// skipped. Generally, targets specified by name (`--bin foo`) are
481 /// required, all others can be silently skipped if features are missing.
482 requires_features: bool,
483 mode: CompileMode,
484}
485
575d6e81
EH
486/// Generates all the base targets for the packages the user has requested to
487/// compile. Dependencies for these targets are computed later in
488/// `unit_dependencies`.
489fn generate_targets<'a>(
73660740
EH
490 ws: &Workspace,
491 profiles: &Profiles,
575d6e81
EH
492 packages: &[&'a Package],
493 filter: &CompileFilter,
494 default_arch_kind: Kind,
575d6e81 495 resolve: &Resolve,
55a27fdc 496 build_config: &BuildConfig,
575d6e81 497) -> CargoResult<Vec<Unit<'a>>> {
575d6e81 498 // Helper for creating a Unit struct.
a4947c2b 499 let new_unit = |pkg: &'a Package, target: &'a Target, target_mode: CompileMode| {
86489946
EH
500 let unit_for = if build_config.mode.is_any_test() {
501 // NOTE: The UnitFor here is subtle. If you have a profile
a4947c2b 502 // with `panic` set, the `panic` flag is cleared for
86489946
EH
503 // tests/benchmarks and their dependencies. If this
504 // was `normal`, then the lib would get compiled three
a4947c2b
EH
505 // times (once with panic, once without, and once with
506 // --test).
507 //
508 // This would cause a problem for Doc tests, which would fail
509 // because `rustdoc` would attempt to link with both libraries
510 // at the same time. Also, it's probably not important (or
511 // even desirable?) for rustdoc to link with a lib with
512 // `panic` set.
513 //
514 // As a consequence, Examples and Binaries get compiled
515 // without `panic` set. This probably isn't a bad deal.
516 //
517 // Forcing the lib to be compiled three times during `cargo
518 // test` is probably also not desirable.
86489946
EH
519 UnitFor::new_test()
520 } else if target.for_host() {
521 // proc-macro/plugin should not have `panic` set.
522 UnitFor::new_compiler()
a4947c2b 523 } else {
86489946 524 UnitFor::new_normal()
a4947c2b 525 };
86489946
EH
526 // Custom build units are added in `build_unit_dependencies`.
527 assert!(!target.is_custom_build());
a4947c2b
EH
528 let target_mode = match target_mode {
529 CompileMode::Test => {
dffc5bae 530 if target.is_example() && !filter.is_specific() && !target.tested() {
a4947c2b
EH
531 // Examples are included as regular binaries to verify
532 // that they compile.
533 CompileMode::Build
534 } else {
535 CompileMode::Test
2a82378a
BW
536 }
537 }
a4947c2b
EH
538 CompileMode::Build => match *target.kind() {
539 TargetKind::Test => CompileMode::Test,
540 TargetKind::Bench => CompileMode::Bench,
541 _ => CompileMode::Build,
542 },
739c272f
EH
543 // CompileMode::Bench is only used to inform filter_default_targets
544 // which command is being used (`cargo bench`). Afterwards, tests
545 // and benches are treated identically. Switching the mode allows
546 // de-duplication of units that are essentially identical. For
547 // example, `cargo build --all-targets --release` creates the units
548 // (lib profile:bench, mode:test) and (lib profile:bench, mode:bench)
549 // and since these are the same, we want them to be de-duped in
550 // `unit_dependencies`.
551 CompileMode::Bench => CompileMode::Test,
a4947c2b 552 _ => target_mode,
575d6e81 553 };
a4947c2b
EH
554 // Plugins or proc-macro should be built for the host.
555 let kind = if target.for_host() {
556 Kind::Host
557 } else {
558 default_arch_kind
559 };
560 let profile = profiles.get_profile(
5415a341 561 pkg.package_id(),
a4947c2b 562 ws.is_member(pkg),
86489946 563 unit_for,
a4947c2b 564 target_mode,
55a27fdc 565 build_config.release,
a4947c2b
EH
566 );
567 Unit {
568 pkg,
569 target,
570 profile,
571 kind,
572 mode: target_mode,
573 }
574 };
575d6e81 575
3a1cad6f
EH
576 // Create a list of proposed targets.
577 let mut proposals: Vec<Proposal> = Vec::new();
771fec3c
EH
578
579 match *filter {
580 CompileFilter::Default {
581 required_features_filterable,
582 } => {
583 for pkg in packages {
584 let default = filter_default_targets(pkg.targets(), build_config.mode);
3a1cad6f
EH
585 proposals.extend(default.into_iter().map(|target| Proposal {
586 pkg,
587 target,
588 requires_features: !required_features_filterable,
589 mode: build_config.mode,
771fec3c 590 }));
55a27fdc 591 if build_config.mode == CompileMode::Test {
dd6c6102
EH
592 if let Some(t) = pkg
593 .targets()
594 .iter()
595 .find(|t| t.is_lib() && t.doctested() && t.doctestable())
596 {
3a1cad6f
EH
597 proposals.push(Proposal {
598 pkg,
599 target: t,
600 requires_features: false,
601 mode: CompileMode::Doctest,
602 });
575d6e81 603 }
2a82378a
BW
604 }
605 }
771fec3c
EH
606 }
607 CompileFilter::Only {
608 all_targets,
609 lib,
610 ref bins,
611 ref examples,
612 ref tests,
613 ref benches,
614 } => {
615 if lib {
616 let mut libs = Vec::new();
617 for pkg in packages {
618 for target in pkg.targets().iter().filter(|t| t.is_lib()) {
dd6c6102 619 if build_config.mode == CompileMode::Doctest && !target.doctestable() {
771fec3c
EH
620 ws.config()
621 .shell()
622 .warn(format!(
dd6c6102
EH
623 "doc tests are not supported for crate type(s) `{}` in package `{}`",
624 target.rustc_crate_types().join(", "),
625 pkg.name()
771fec3c
EH
626 ))?;
627 } else {
3a1cad6f
EH
628 libs.push(Proposal {
629 pkg,
630 target,
631 requires_features: false,
632 mode: build_config.mode,
633 });
dd6c6102 634 }
575d6e81
EH
635 }
636 }
771fec3c
EH
637 if !all_targets && libs.is_empty() {
638 let names = packages.iter().map(|pkg| pkg.name()).collect::<Vec<_>>();
639 if names.len() == 1 {
640 bail!("no library targets found in package `{}`", names[0]);
641 } else {
642 bail!("no library targets found in packages: {}", names.join(", "));
643 }
644 }
645 proposals.extend(libs);
575d6e81 646 }
3a1cad6f 647
771fec3c
EH
648 // If --tests was specified, add all targets that would be
649 // generated by `cargo test`.
650 let test_filter = match *tests {
651 FilterRule::All => Target::tested,
652 FilterRule::Just(_) => Target::is_test,
653 };
654 let test_mode = match build_config.mode {
655 CompileMode::Build => CompileMode::Test,
656 CompileMode::Check { .. } => CompileMode::Check { test: true },
657 _ => build_config.mode,
658 };
659 // If --benches was specified, add all targets that would be
660 // generated by `cargo bench`.
661 let bench_filter = match *benches {
662 FilterRule::All => Target::benched,
663 FilterRule::Just(_) => Target::is_bench,
664 };
665 let bench_mode = match build_config.mode {
666 CompileMode::Build => CompileMode::Bench,
667 CompileMode::Check { .. } => CompileMode::Check { test: true },
668 _ => build_config.mode,
669 };
670
671 proposals.extend(list_rule_targets(
672 packages,
673 bins,
674 "bin",
675 Target::is_bin,
676 build_config.mode,
677 )?);
678 proposals.extend(list_rule_targets(
679 packages,
680 examples,
681 "example",
682 Target::is_example,
683 build_config.mode,
684 )?);
685 proposals.extend(list_rule_targets(
686 packages,
687 tests,
688 "test",
689 test_filter,
690 test_mode,
691 )?);
692 proposals.extend(list_rule_targets(
693 packages,
694 benches,
695 "bench",
696 bench_filter,
697 bench_mode,
698 )?);
2a82378a 699 }
771fec3c 700 }
2a82378a 701
771fec3c
EH
702 // Only include targets that are libraries or have all required
703 // features available.
704 let mut features_map = HashMap::new();
3a1cad6f 705 let mut units = HashSet::new();
dae87a26
E
706 for Proposal {
707 pkg,
708 target,
709 requires_features,
710 mode,
711 } in proposals
712 {
771fec3c
EH
713 let unavailable_features = match target.required_features() {
714 Some(rf) => {
715 let features = features_map
716 .entry(pkg)
717 .or_insert_with(|| resolve_all_features(resolve, pkg.package_id()));
718 rf.iter().filter(|f| !features.contains(*f)).collect()
2a82378a 719 }
771fec3c
EH
720 None => Vec::new(),
721 };
722 if target.is_lib() || unavailable_features.is_empty() {
723 let unit = new_unit(pkg, target, mode);
3a1cad6f
EH
724 units.insert(unit);
725 } else if requires_features {
771fec3c
EH
726 let required_features = target.required_features().unwrap();
727 let quoted_required_features: Vec<String> = required_features
728 .iter()
729 .map(|s| format!("`{}`", s))
730 .collect();
731 bail!(
732 "target `{}` in package `{}` requires the features: {}\n\
733 Consider enabling them by passing e.g. `--features=\"{}\"`",
734 target.name(),
735 pkg.name(),
736 quoted_required_features.join(", "),
737 required_features.join(" ")
738 );
2a82378a 739 }
771fec3c 740 // else, silently skip target.
2a82378a 741 }
3a1cad6f 742 Ok(units.into_iter().collect())
2a82378a
BW
743}
744
575d6e81
EH
745fn resolve_all_features(
746 resolve_with_overrides: &Resolve,
dae87a26 747 package_id: PackageId,
575d6e81
EH
748) -> HashSet<String> {
749 let mut features = resolve_with_overrides.features(package_id).clone();
750
751 // Include features enabled for use by dependencies so targets can also use them with the
752 // required-features field when deciding whether to be built or skipped.
753 for (dep, _) in resolve_with_overrides.deps(package_id) {
754 for feature in resolve_with_overrides.features(dep) {
755 features.insert(dep.name().to_string() + "/" + feature);
756 }
757 }
758
759 features
760}
761
762/// Given a list of all targets for a package, filters out only the targets
763/// that are automatically included when the user doesn't specify any targets.
771fec3c 764fn filter_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Target> {
575d6e81
EH
765 match mode {
766 CompileMode::Bench => targets.iter().filter(|t| t.benched()).collect(),
a4947c2b
EH
767 CompileMode::Test => targets
768 .iter()
769 .filter(|t| t.tested() || t.is_example())
770 .collect(),
575d6e81
EH
771 CompileMode::Build | CompileMode::Check { .. } => targets
772 .iter()
773 .filter(|t| t.is_bin() || t.is_lib())
774 .collect(),
775 CompileMode::Doc { .. } => {
776 // `doc` does lib and bins (bin with same name as lib is skipped).
777 targets
1e682848 778 .iter()
575d6e81
EH
779 .filter(|t| {
780 t.documented()
781 && (!t.is_bin()
782 || !targets.iter().any(|l| l.is_lib() && l.name() == t.name()))
783 })
784 .collect()
2a82378a 785 }
00d325db 786 CompileMode::Doctest | CompileMode::RunCustomBuild => panic!("Invalid mode {:?}", mode),
2a82378a 787 }
2a82378a
BW
788}
789
575d6e81 790/// Returns a list of targets based on command-line target selection flags.
771fec3c
EH
791/// The return value is a list of `(Package, Target, bool, CompileMode)`
792/// tuples. The `bool` value indicates whether or not all required features
793/// *must* be present.
575d6e81 794fn list_rule_targets<'a>(
771fec3c 795 packages: &[&'a Package],
575d6e81
EH
796 rule: &FilterRule,
797 target_desc: &'static str,
798 is_expected_kind: fn(&Target) -> bool,
771fec3c 799 mode: CompileMode,
3a1cad6f 800) -> CargoResult<Vec<Proposal<'a>>> {
771fec3c 801 let mut result = Vec::new();
575d6e81 802 match *rule {
771fec3c
EH
803 FilterRule::All => {
804 for pkg in packages {
805 for target in pkg.targets() {
806 if is_expected_kind(target) {
3a1cad6f
EH
807 result.push(Proposal {
808 pkg,
809 target,
810 requires_features: false,
811 mode,
812 });
771fec3c
EH
813 }
814 }
815 }
816 }
817 FilterRule::Just(ref names) => {
818 for name in names {
819 result.extend(find_named_targets(
820 packages,
821 name,
822 target_desc,
823 is_expected_kind,
824 mode,
825 )?);
826 }
827 }
575d6e81 828 }
771fec3c 829 Ok(result)
575d6e81 830}
235712f5 831
771fec3c
EH
832/// Find the targets for a specifically named target.
833fn find_named_targets<'a>(
834 packages: &[&'a Package],
575d6e81
EH
835 target_name: &str,
836 target_desc: &'static str,
837 is_expected_kind: fn(&Target) -> bool,
771fec3c 838 mode: CompileMode,
3a1cad6f 839) -> CargoResult<Vec<Proposal<'a>>> {
771fec3c
EH
840 let mut result = Vec::new();
841 for pkg in packages {
842 for target in pkg.targets() {
843 if target.name() == target_name && is_expected_kind(target) {
3a1cad6f
EH
844 result.push(Proposal {
845 pkg,
846 target,
847 requires_features: true,
848 mode,
849 });
9e779198 850 }
f3468348 851 }
575d6e81 852 }
771fec3c
EH
853 if result.is_empty() {
854 let suggestion = packages
855 .iter()
856 .flat_map(|pkg| {
857 pkg.targets()
858 .iter()
859 .filter(|target| is_expected_kind(target))
dae87a26
E
860 })
861 .map(|target| (lev_distance(target_name, target.name()), target))
771fec3c
EH
862 .filter(|&(d, _)| d < 4)
863 .min_by_key(|t| t.0)
864 .map(|t| t.1);
865 match suggestion {
866 Some(s) => bail!(
867 "no {} target named `{}`\n\nDid you mean `{}`?",
868 target_desc,
869 target_name,
870 s.name()
871 ),
872 None => bail!("no {} target named `{}`", target_desc, target_name),
873 }
874 }
875 Ok(result)
9e779198 876}