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