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