]>
Commit | Line | Data |
---|---|---|
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 | 25 | use std::collections::HashSet; |
8cf53bca | 26 | use std::path::PathBuf; |
1ab19a5a | 27 | use std::sync::Arc; |
12af54e1 | 28 | |
c32e395c | 29 | use core::compiler::{BuildConfig, BuildContext, Compilation, Context, DefaultExecutor, Executor}; |
08025169 | 30 | use core::compiler::{CompileMode, Kind, Unit}; |
73660740 | 31 | use core::profiles::{ProfileFor, Profiles}; |
1e682848 | 32 | use core::resolver::{Method, Resolve}; |
575d6e81 EH |
33 | use core::{Package, Source, Target}; |
34 | use core::{PackageId, PackageIdSpec, TargetKind, Workspace}; | |
a340ba0b | 35 | use ops; |
56db20df | 36 | use util::config::Config; |
8cf53bca | 37 | use util::{lev_distance, profile, CargoResult}; |
50f110a4 | 38 | |
bacb6be3 | 39 | /// Contains information about how a package should be compiled. |
876af7c1 | 40 | #[derive(Debug)] |
2fe0bf83 AC |
41 | pub 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 | 69 | impl<'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)] |
107 | pub enum Packages { | |
ba7911dd | 108 | Default, |
62c04979 | 109 | All, |
bb643cca AK |
110 | OptOut(Vec<String>), |
111 | Packages(Vec<String>), | |
62c04979 AR |
112 | } |
113 | ||
bb643cca | 114 | impl 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)] |
163 | pub enum FilterRule { | |
2a82378a | 164 | All, |
bb643cca | 165 | Just(Vec<String>), |
2a82378a BW |
166 | } |
167 | ||
876af7c1 | 168 | #[derive(Debug)] |
bb643cca | 169 | pub 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 |
184 | pub 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 |
193 | pub 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 |
202 | pub 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 |
306 | impl 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 | 337 | impl 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`. | |
436 | fn 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 |
646 | fn 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. | |
665 | fn 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. | |
694 | fn 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. |
714 | fn 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 | } |