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