]> git.proxmox.com Git - cargo.git/blame - src/cargo/ops/cargo_compile.rs
Auto merge of #5794 - Nemo157:rustdoc-rename-extern, r=alexcrichton
[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
7051b630 25use std::collections::HashSet;
8cf53bca 26use std::path::PathBuf;
1ab19a5a 27use std::sync::Arc;
12af54e1 28
c32e395c 29use core::compiler::{BuildConfig, BuildContext, Compilation, Context, DefaultExecutor, Executor};
08025169 30use core::compiler::{CompileMode, Kind, Unit};
73660740 31use core::profiles::{ProfileFor, Profiles};
1e682848 32use core::resolver::{Method, Resolve};
575d6e81
EH
33use core::{Package, Source, Target};
34use core::{PackageId, PackageIdSpec, TargetKind, Workspace};
a340ba0b 35use ops;
56db20df 36use util::config::Config;
8cf53bca 37use 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,
4816a2a5 56 /// Extra arguments to be passed to rustdoc (for main crate and dependencies)
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>>,
5baac6b3
AK
61 /// The directory to copy final artifacts to. Note that even if `out_dir` is
62 /// set, a copy of artifacts still could be found a `target/(debug\release)`
63 /// as usual.
64 // Note that, although the cmd-line flag name is `out-dir`, in code we use
65 // `export_dir`, to avoid confusion with out dir at `target/debug/deps`.
66 pub export_dir: Option<PathBuf>,
9e779198
AC
67}
68
69caa63b 69impl<'a> CompileOptions<'a> {
072254c3 70 pub fn new(config: &'a Config, mode: CompileMode) -> CargoResult<CompileOptions<'a>> {
08025169 71 Ok(CompileOptions {
0247dc42 72 config,
08025169 73 build_config: BuildConfig::new(config, None, &None, mode)?,
bb643cca 74 features: Vec::new(),
69caa63b
NC
75 all_features: false,
76 no_default_features: false,
bb643cca 77 spec: ops::Packages::Packages(Vec::new()),
1e682848
AC
78 filter: CompileFilter::Default {
79 required_features_filterable: false,
80 },
69caa63b
NC
81 target_rustdoc_args: None,
82 target_rustc_args: None,
5baac6b3 83 export_dir: None,
08025169 84 })
69caa63b 85 }
5936f6ae
RJ
86
87 // Returns the unique specified package, or None
88 pub fn get_package<'b>(&self, ws: &'b Workspace) -> CargoResult<Option<&'b Package>> {
89 Ok(match self.spec {
90 Packages::All | Packages::Default | Packages::OptOut(_) => {
91 None
92 }
93 Packages::Packages(ref xs) => match xs.len() {
94 0 => Some(ws.current()?),
95 1 => Some(ws.members()
96 .find(|pkg| *pkg.name() == xs[0])
97 .ok_or_else(|| {
98 format_err!("package `{}` is not a member of the workspace", xs[0])
99 })?),
100 _ => None,
101 },
102 })
103 }
69caa63b
NC
104}
105
bb643cca
AK
106#[derive(Clone, PartialEq, Eq, Debug)]
107pub enum Packages {
ba7911dd 108 Default,
62c04979 109 All,
bb643cca
AK
110 OptOut(Vec<String>),
111 Packages(Vec<String>),
62c04979
AR
112}
113
bb643cca 114impl Packages {
1e682848 115 pub fn from_flags(all: bool, exclude: Vec<String>, package: Vec<String>) -> CargoResult<Self> {
ba7911dd
SS
116 Ok(match (all, exclude.len(), package.len()) {
117 (false, 0, 0) => Packages::Default,
118 (false, 0, _) => Packages::Packages(package),
119 (false, _, _) => bail!("--exclude can only be used together with --all"),
120 (true, 0, _) => Packages::All,
121 (true, _, _) => Packages::OptOut(exclude),
122 })
7ffd1cfc 123 }
124
bb643cca
AK
125 pub fn into_package_id_specs(&self, ws: &Workspace) -> CargoResult<Vec<PackageIdSpec>> {
126 let specs = match *self {
1e682848
AC
127 Packages::All => ws.members()
128 .map(Package::package_id)
129 .map(PackageIdSpec::from_package_id)
130 .collect(),
131 Packages::OptOut(ref opt_out) => ws.members()
132 .map(Package::package_id)
133 .map(PackageIdSpec::from_package_id)
134 .filter(|p| opt_out.iter().position(|x| *x == p.name()).is_none())
135 .collect(),
037a879d
EH
136 Packages::Packages(ref packages) if packages.is_empty() => {
137 vec![PackageIdSpec::from_package_id(ws.current()?.package_id())]
138 }
1e682848
AC
139 Packages::Packages(ref packages) => packages
140 .iter()
141 .map(|p| PackageIdSpec::parse(p))
142 .collect::<CargoResult<Vec<_>>>()?,
143 Packages::Default => ws.default_members()
144 .map(Package::package_id)
145 .map(PackageIdSpec::from_package_id)
146 .collect(),
6b32c4d7 147 };
68a681ff 148 if specs.is_empty() {
691f95de 149 if ws.is_virtual() {
1e682848
AC
150 bail!(
151 "manifest path `{}` contains no package: The manifest is virtual, \
152 and the workspace has no members.",
153 ws.root().display()
154 )
05b896ac 155 }
691f95de 156 bail!("no packages to compile")
68a681ff 157 }
6b32c4d7
AK
158 Ok(specs)
159 }
160}
161
bb643cca
AK
162#[derive(Debug)]
163pub enum FilterRule {
2a82378a 164 All,
bb643cca 165 Just(Vec<String>),
2a82378a
BW
166}
167
876af7c1 168#[derive(Debug)]
bb643cca 169pub enum CompileFilter {
6344db05 170 Default {
25e50b58
JB
171 /// Flag whether targets can be safely skipped when required-features are not satisfied.
172 required_features_filterable: bool,
173 },
9e779198 174 Only {
b9497ab2 175 all_targets: bool,
9e779198 176 lib: bool,
bb643cca
AK
177 bins: FilterRule,
178 examples: FilterRule,
179 tests: FilterRule,
180 benches: FilterRule,
1e682848 181 },
a1980dc7
YKCL
182}
183
1e682848
AC
184pub fn compile<'a>(
185 ws: &Workspace<'a>,
186 options: &CompileOptions<'a>,
a340ba0b 187) -> CargoResult<Compilation<'a>> {
1ab19a5a 188 compile_with_exec(ws, options, Arc::new(DefaultExecutor))
8c3b3605
NC
189}
190
4a64d05e 191/// Like `compile` but allows specifying a custom `Executor` that will be able to intercept build
d70f91ee 192/// calls and add custom logic. `compile` uses `DefaultExecutor` which just passes calls through.
1e682848
AC
193pub fn compile_with_exec<'a>(
194 ws: &Workspace<'a>,
195 options: &CompileOptions<'a>,
196 exec: Arc<Executor>,
a340ba0b 197) -> CargoResult<Compilation<'a>> {
688a4fa6 198 ws.emit_warnings()?;
8c3b3605 199 compile_ws(ws, None, options, exec)
9fba127e
AC
200}
201
1e682848
AC
202pub fn compile_ws<'a>(
203 ws: &Workspace<'a>,
204 source: Option<Box<Source + 'a>>,
205 options: &CompileOptions<'a>,
206 exec: Arc<Executor>,
a340ba0b 207) -> CargoResult<Compilation<'a>> {
1e682848
AC
208 let CompileOptions {
209 config,
08025169 210 ref build_config,
1e682848
AC
211 ref spec,
212 ref features,
213 all_features,
214 no_default_features,
1e682848
AC
215 ref filter,
216 ref target_rustdoc_args,
217 ref target_rustc_args,
5baac6b3 218 ref export_dir,
1e682848 219 } = *options;
3656bec8 220
575d6e81
EH
221 let default_arch_kind = if build_config.requested_target.is_some() {
222 Kind::Target
223 } else {
224 Kind::Host
225 };
a9562433 226
6b32c4d7 227 let specs = spec.into_package_id_specs(ws)?;
7de30dd2
XL
228 let features = Method::split_features(features);
229 let method = Method::Required {
08025169 230 dev_deps: ws.require_optional_deps() || filter.need_dev_deps(build_config.mode),
7de30dd2
XL
231 features: &features,
232 all_features,
233 uses_default_features: !no_default_features,
234 };
1e682848 235 let resolve = ops::resolve_ws_with_method(ws, source, method, &specs)?;
6b32c4d7 236 let (packages, resolve_with_overrides) = resolve;
9c296792 237
1e682848
AC
238 let to_builds = specs
239 .iter()
240 .map(|p| {
241 let pkgid = p.query(resolve_with_overrides.iter())?;
242 let p = packages.get(pkgid)?;
243 p.manifest().print_teapot(ws.config());
244 Ok(p)
245 })
246 .collect::<CargoResult<Vec<_>>>()?;
f0647ea2 247
575d6e81
EH
248 let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
249 (&Some(ref args), _) => (Some(args.clone()), "rustc"),
250 (_, &Some(ref args)) => (Some(args.clone()), "rustdoc"),
251 _ => (None, ""),
f0647ea2 252 };
b3ade7c7 253
575d6e81
EH
254 if extra_args.is_some() && to_builds.len() != 1 {
255 panic!(
256 "`{}` should not accept multiple `-p` flags",
257 extra_args_name
258 );
259 }
260
261 let profiles = ws.profiles();
5415a341 262 profiles.validate_packages(&mut config.shell(), &packages)?;
a0a880c3 263
7051b630 264 let mut extra_compiler_args = None;
575d6e81
EH
265
266 let units = generate_targets(
73660740
EH
267 ws,
268 profiles,
575d6e81
EH
269 &to_builds,
270 filter,
271 default_arch_kind,
575d6e81 272 &resolve_with_overrides,
08025169 273 build_config,
575d6e81
EH
274 )?;
275
276 if let Some(args) = extra_args {
277 if units.len() != 1 {
278 bail!(
279 "extra arguments to `{}` can only be passed to one \
280 target, consider filtering\nthe package by passing \
281 e.g. `--lib` or `--bin NAME` to specify a single target",
282 extra_args_name
283 );
8a8ea1e8 284 }
7051b630 285 extra_compiler_args = Some((units[0], args));
b3ade7c7 286 }
575d6e81 287
00d325db 288 let ret = {
4f12a2b5 289 let _p = profile::start("compiling");
c32e395c 290 let bcx = BuildContext::new(
1e682848 291 ws,
1e682848 292 &resolve_with_overrides,
3f3f27be 293 &packages,
1e682848 294 config,
11a7cbdb 295 &build_config,
1e682848 296 profiles,
575d6e81 297 extra_compiler_args,
3f3f27be 298 )?;
c32e395c 299 let mut cx = Context::new(config, &bcx)?;
19b854d8 300 cx.compile(&units, export_dir.clone(), &exec)?
0484cd88 301 };
8b840393 302
385b54b3 303 Ok(ret)
62bff631 304}
51c9cf0f 305
bb643cca
AK
306impl FilterRule {
307 pub fn new(targets: Vec<String>, all: bool) -> FilterRule {
2a82378a
BW
308 if all {
309 FilterRule::All
310 } else {
311 FilterRule::Just(targets)
312 }
313 }
314
315 fn matches(&self, target: &Target) -> bool {
316 match *self {
317 FilterRule::All => true,
1e682848 318 FilterRule::Just(ref targets) => targets.iter().any(|x| *x == target.name()),
2a82378a
BW
319 }
320 }
321
322 fn is_specific(&self) -> bool {
323 match *self {
324 FilterRule::All => true,
bb643cca 325 FilterRule::Just(ref targets) => !targets.is_empty(),
2a82378a
BW
326 }
327 }
328
329 pub fn try_collect(&self) -> Option<Vec<String>> {
330 match *self {
331 FilterRule::All => None,
bb643cca 332 FilterRule::Just(ref targets) => Some(targets.clone()),
2a82378a
BW
333 }
334 }
335}
336
bb643cca 337impl CompileFilter {
1e682848
AC
338 pub fn new(
339 lib_only: bool,
340 bins: Vec<String>,
341 all_bins: bool,
342 tsts: Vec<String>,
343 all_tsts: bool,
344 exms: Vec<String>,
345 all_exms: bool,
346 bens: Vec<String>,
347 all_bens: bool,
348 all_targets: bool,
349 ) -> CompileFilter {
2a82378a
BW
350 let rule_bins = FilterRule::new(bins, all_bins);
351 let rule_tsts = FilterRule::new(tsts, all_tsts);
352 let rule_exms = FilterRule::new(exms, all_exms);
353 let rule_bens = FilterRule::new(bens, all_bens);
354
6344db05
SK
355 if all_targets {
356 CompileFilter::Only {
b9497ab2 357 all_targets: true,
1e682848
AC
358 lib: true,
359 bins: FilterRule::All,
360 examples: FilterRule::All,
361 benches: FilterRule::All,
6344db05
SK
362 tests: FilterRule::All,
363 }
364 } else if lib_only || rule_bins.is_specific() || rule_tsts.is_specific()
1e682848
AC
365 || rule_exms.is_specific() || rule_bens.is_specific()
366 {
7a1d8d94 367 CompileFilter::Only {
b9497ab2 368 all_targets: false,
1e682848
AC
369 lib: lib_only,
370 bins: rule_bins,
371 examples: rule_exms,
372 benches: rule_bens,
2a82378a 373 tests: rule_tsts,
7a1d8d94
AC
374 }
375 } else {
6344db05 376 CompileFilter::Default {
25e50b58
JB
377 required_features_filterable: true,
378 }
7a1d8d94
AC
379 }
380 }
381
89d5161d
XL
382 pub fn need_dev_deps(&self, mode: CompileMode) -> bool {
383 match mode {
384 CompileMode::Test | CompileMode::Doctest | CompileMode::Bench => true,
385 CompileMode::Build | CompileMode::Doc { .. } | CompileMode::Check { .. } => match *self
386 {
387 CompileFilter::Default { .. } => false,
388 CompileFilter::Only {
389 ref examples,
390 ref tests,
391 ref benches,
392 ..
393 } => examples.is_specific() || tests.is_specific() || benches.is_specific(),
394 },
575d6e81 395 CompileMode::RunCustomBuild => panic!("Invalid mode"),
7de30dd2
XL
396 }
397 }
398
ce26ddfd
XL
399 // this selects targets for "cargo run". for logic to select targets for
400 // other subcommands, see generate_targets and generate_default_targets
401 pub fn target_run(&self, target: &Target) -> bool {
9e779198 402 match *self {
70170d1b 403 CompileFilter::Default { .. } => true,
1e682848
AC
404 CompileFilter::Only {
405 lib,
406 ref bins,
407 ref examples,
408 ref tests,
409 ref benches,
410 ..
411 } => {
2a82378a 412 let rule = match *target.kind() {
9e779198
AC
413 TargetKind::Bin => bins,
414 TargetKind::Test => tests,
415 TargetKind::Bench => benches,
1e682848 416 TargetKind::ExampleBin | TargetKind::ExampleLib(..) => examples,
9e779198
AC
417 TargetKind::Lib(..) => return lib,
418 TargetKind::CustomBuild => return false,
419 };
2a82378a 420 rule.matches(target)
9e779198
AC
421 }
422 }
423 }
c98b8c4c
BW
424
425 pub fn is_specific(&self) -> bool {
426 match *self {
6344db05 427 CompileFilter::Default { .. } => false,
c98b8c4c
BW
428 CompileFilter::Only { .. } => true,
429 }
430 }
9e779198
AC
431}
432
575d6e81
EH
433/// Generates all the base targets for the packages the user has requested to
434/// compile. Dependencies for these targets are computed later in
435/// `unit_dependencies`.
436fn generate_targets<'a>(
73660740
EH
437 ws: &Workspace,
438 profiles: &Profiles,
575d6e81
EH
439 packages: &[&'a Package],
440 filter: &CompileFilter,
441 default_arch_kind: Kind,
575d6e81 442 resolve: &Resolve,
55a27fdc 443 build_config: &BuildConfig,
575d6e81
EH
444) -> CargoResult<Vec<Unit<'a>>> {
445 let mut units = Vec::new();
446
447 // Helper for creating a Unit struct.
a4947c2b 448 let new_unit = |pkg: &'a Package, target: &'a Target, target_mode: CompileMode| {
55a27fdc 449 let profile_for = if build_config.mode.is_any_test() {
a4947c2b
EH
450 // NOTE: The ProfileFor here is subtle. If you have a profile
451 // with `panic` set, the `panic` flag is cleared for
452 // tests/benchmarks and their dependencies. If we left this
453 // as an "Any" profile, then the lib would get compiled three
454 // times (once with panic, once without, and once with
455 // --test).
456 //
457 // This would cause a problem for Doc tests, which would fail
458 // because `rustdoc` would attempt to link with both libraries
459 // at the same time. Also, it's probably not important (or
460 // even desirable?) for rustdoc to link with a lib with
461 // `panic` set.
462 //
463 // As a consequence, Examples and Binaries get compiled
464 // without `panic` set. This probably isn't a bad deal.
465 //
466 // Forcing the lib to be compiled three times during `cargo
467 // test` is probably also not desirable.
468 ProfileFor::TestDependency
469 } else {
470 ProfileFor::Any
471 };
472 let target_mode = match target_mode {
473 CompileMode::Test => {
dffc5bae 474 if target.is_example() && !filter.is_specific() && !target.tested() {
a4947c2b
EH
475 // Examples are included as regular binaries to verify
476 // that they compile.
477 CompileMode::Build
478 } else {
479 CompileMode::Test
2a82378a
BW
480 }
481 }
a4947c2b
EH
482 CompileMode::Build => match *target.kind() {
483 TargetKind::Test => CompileMode::Test,
484 TargetKind::Bench => CompileMode::Bench,
485 _ => CompileMode::Build,
486 },
487 _ => target_mode,
575d6e81 488 };
a4947c2b
EH
489 // Plugins or proc-macro should be built for the host.
490 let kind = if target.for_host() {
491 Kind::Host
492 } else {
493 default_arch_kind
494 };
495 let profile = profiles.get_profile(
5415a341 496 pkg.package_id(),
a4947c2b
EH
497 ws.is_member(pkg),
498 profile_for,
499 target_mode,
55a27fdc 500 build_config.release,
a4947c2b 501 );
195520b8
EH
502 // Once the profile has been selected for benchmarks, we don't need to
503 // distinguish between benches and tests. Switching the mode allows
504 // de-duplication of units that are essentially identical. For
505 // example, `cargo build --all-targets --release` creates the units
506 // (lib profile:bench, mode:test) and (lib profile:bench, mode:bench)
507 // and since these are the same, we want them to be de-duped in
508 // `unit_dependencies`.
509 let target_mode = match target_mode {
510 CompileMode::Bench => CompileMode::Test,
511 _ => target_mode,
512 };
a4947c2b
EH
513 Unit {
514 pkg,
515 target,
516 profile,
517 kind,
518 mode: target_mode,
519 }
520 };
575d6e81
EH
521
522 for pkg in packages {
523 let features = resolve_all_features(resolve, pkg.package_id());
524 // Create a list of proposed targets. The `bool` value indicates
525 // whether or not all required features *must* be present. If false,
526 // and the features are not available, then it will be silently
527 // skipped. Generally, targets specified by name (`--bin foo`) are
528 // required, all others can be silently skipped if features are
529 // missing.
530 let mut proposals: Vec<(Unit<'a>, bool)> = Vec::new();
531
532 match *filter {
533 CompileFilter::Default {
534 required_features_filterable,
535 } => {
55a27fdc 536 let default_units = generate_default_targets(pkg.targets(), build_config.mode)
575d6e81 537 .iter()
55a27fdc
DO
538 .map(|t| {
539 (
540 new_unit(pkg, t, build_config.mode),
541 !required_features_filterable,
542 )
543 })
575d6e81
EH
544 .collect::<Vec<_>>();
545 proposals.extend(default_units);
55a27fdc 546 if build_config.mode == CompileMode::Test {
00d325db 547 // Include doctest for lib.
bce5ab5b 548 if let Some(t) = pkg.targets().iter().find(|t| t.is_lib() && t.doctested()) {
00d325db 549 proposals.push((new_unit(pkg, t, CompileMode::Doctest), false));
575d6e81 550 }
2a82378a
BW
551 }
552 }
575d6e81
EH
553 CompileFilter::Only {
554 all_targets,
555 lib,
556 ref bins,
557 ref examples,
558 ref tests,
559 ref benches,
560 } => {
561 if lib {
bce5ab5b 562 if let Some(target) = pkg.targets().iter().find(|t| t.is_lib()) {
55a27fdc 563 proposals.push((new_unit(pkg, target, build_config.mode), false));
575d6e81
EH
564 } else if !all_targets {
565 bail!("no library targets found")
566 }
567 }
568 // If --tests was specified, add all targets that would be
569 // generated by `cargo test`.
570 let test_filter = match *tests {
571 FilterRule::All => Target::tested,
572 FilterRule::Just(_) => Target::is_test,
573 };
55a27fdc 574 let test_mode = match build_config.mode {
575d6e81
EH
575 CompileMode::Build => CompileMode::Test,
576 CompileMode::Check { .. } => CompileMode::Check { test: true },
55a27fdc 577 _ => build_config.mode,
575d6e81
EH
578 };
579 // If --benches was specified, add all targets that would be
580 // generated by `cargo bench`.
581 let bench_filter = match *benches {
582 FilterRule::All => Target::benched,
583 FilterRule::Just(_) => Target::is_bench,
584 };
55a27fdc 585 let bench_mode = match build_config.mode {
575d6e81
EH
586 CompileMode::Build => CompileMode::Bench,
587 CompileMode::Check { .. } => CompileMode::Check { test: true },
55a27fdc 588 _ => build_config.mode,
575d6e81 589 };
2a82378a 590
575d6e81
EH
591 proposals.extend(
592 list_rule_targets(pkg, bins, "bin", Target::is_bin)?
593 .into_iter()
55a27fdc 594 .map(|(t, required)| (new_unit(pkg, t, build_config.mode), required))
575d6e81
EH
595 .chain(
596 list_rule_targets(pkg, examples, "example", Target::is_example)?
597 .into_iter()
55a27fdc
DO
598 .map(|(t, required)| {
599 (new_unit(pkg, t, build_config.mode), required)
600 }),
575d6e81
EH
601 )
602 .chain(
603 list_rule_targets(pkg, tests, "test", test_filter)?
604 .into_iter()
a4947c2b 605 .map(|(t, required)| (new_unit(pkg, t, test_mode), required)),
575d6e81
EH
606 )
607 .chain(
608 list_rule_targets(pkg, benches, "bench", bench_filter)?
609 .into_iter()
a4947c2b 610 .map(|(t, required)| (new_unit(pkg, t, bench_mode), required)),
575d6e81
EH
611 )
612 .collect::<Vec<_>>(),
613 );
614 }
2a82378a 615 }
2a82378a 616
575d6e81
EH
617 // Only include targets that are libraries or have all required
618 // features available.
619 for (unit, required) in proposals {
620 let unavailable_features = match unit.target.required_features() {
621 Some(rf) => rf.iter().filter(|f| !features.contains(*f)).collect(),
622 None => Vec::new(),
623 };
624 if unit.target.is_lib() || unavailable_features.is_empty() {
625 units.push(unit);
626 } else if required {
627 let required_features = unit.target.required_features().unwrap();
628 let quoted_required_features: Vec<String> = required_features
1e682848 629 .iter()
575d6e81
EH
630 .map(|s| format!("`{}`", s))
631 .collect();
632 bail!(
633 "target `{}` requires the features: {}\n\
634 Consider enabling them by passing e.g. `--features=\"{}\"`",
635 unit.target.name(),
636 quoted_required_features.join(", "),
637 required_features.join(" ")
638 );
2a82378a 639 }
575d6e81 640 // else, silently skip target.
2a82378a
BW
641 }
642 }
575d6e81 643 Ok(units)
2a82378a
BW
644}
645
575d6e81
EH
646fn resolve_all_features(
647 resolve_with_overrides: &Resolve,
648 package_id: &PackageId,
649) -> HashSet<String> {
650 let mut features = resolve_with_overrides.features(package_id).clone();
651
652 // Include features enabled for use by dependencies so targets can also use them with the
653 // required-features field when deciding whether to be built or skipped.
654 for (dep, _) in resolve_with_overrides.deps(package_id) {
655 for feature in resolve_with_overrides.features(dep) {
656 features.insert(dep.name().to_string() + "/" + feature);
657 }
658 }
659
660 features
661}
662
663/// Given a list of all targets for a package, filters out only the targets
664/// that are automatically included when the user doesn't specify any targets.
665fn generate_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Target> {
666 match mode {
667 CompileMode::Bench => targets.iter().filter(|t| t.benched()).collect(),
a4947c2b
EH
668 CompileMode::Test => targets
669 .iter()
670 .filter(|t| t.tested() || t.is_example())
671 .collect(),
575d6e81
EH
672 CompileMode::Build | CompileMode::Check { .. } => targets
673 .iter()
674 .filter(|t| t.is_bin() || t.is_lib())
675 .collect(),
676 CompileMode::Doc { .. } => {
677 // `doc` does lib and bins (bin with same name as lib is skipped).
678 targets
1e682848 679 .iter()
575d6e81
EH
680 .filter(|t| {
681 t.documented()
682 && (!t.is_bin()
683 || !targets.iter().any(|l| l.is_lib() && l.name() == t.name()))
684 })
685 .collect()
2a82378a 686 }
00d325db 687 CompileMode::Doctest | CompileMode::RunCustomBuild => panic!("Invalid mode {:?}", mode),
2a82378a 688 }
2a82378a
BW
689}
690
575d6e81
EH
691/// Returns a list of targets based on command-line target selection flags.
692/// The return value is a list of `(Target, bool)` pairs. The `bool` value
693/// indicates whether or not all required features *must* be present.
694fn list_rule_targets<'a>(
1e682848 695 pkg: &'a Package,
575d6e81
EH
696 rule: &FilterRule,
697 target_desc: &'static str,
698 is_expected_kind: fn(&Target) -> bool,
699) -> CargoResult<Vec<(&'a Target, bool)>> {
700 match *rule {
701 FilterRule::All => Ok(pkg.targets()
702 .iter()
703 .filter(|t| is_expected_kind(t))
704 .map(|t| (t, false))
705 .collect()),
706 FilterRule::Just(ref names) => names
707 .iter()
708 .map(|name| find_target(pkg, name, target_desc, is_expected_kind))
709 .collect(),
710 }
711}
235712f5 712
575d6e81
EH
713/// Find the target for a specifically named target.
714fn find_target<'a>(
715 pkg: &'a Package,
716 target_name: &str,
717 target_desc: &'static str,
718 is_expected_kind: fn(&Target) -> bool,
719) -> CargoResult<(&'a Target, bool)> {
720 match pkg.targets()
721 .iter()
722 .find(|t| t.name() == target_name && is_expected_kind(t))
723 {
724 // When a target is specified by name, required features *must* be
725 // available.
726 Some(t) => Ok((t, true)),
727 None => {
728 let suggestion = pkg.targets()
729 .iter()
730 .filter(|t| is_expected_kind(t))
731 .map(|t| (lev_distance(target_name, t.name()), t))
732 .filter(|&(d, _)| d < 4)
733 .min_by_key(|t| t.0)
734 .map(|t| t.1);
735 match suggestion {
736 Some(s) => bail!(
737 "no {} target named `{}`\n\nDid you mean `{}`?",
738 target_desc,
739 target_name,
740 s.name()
741 ),
742 None => bail!("no {} target named `{}`", target_desc, target_name),
9e779198 743 }
f3468348 744 }
575d6e81 745 }
9e779198 746}