]>
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 | |
fecb7246 AC |
29 | use crate::core::compiler::{ |
30 | BuildConfig, BuildContext, Compilation, Context, DefaultExecutor, Executor, | |
31 | }; | |
04ddd4d0 DW |
32 | use crate::core::compiler::{CompileMode, Kind, Unit}; |
33 | use crate::core::profiles::{Profiles, UnitFor}; | |
34 | use crate::core::resolver::{Method, Resolve}; | |
35 | use crate::core::{Package, Source, Target}; | |
36 | use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace}; | |
37 | use crate::ops; | |
38 | use crate::util::config::Config; | |
39 | use 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 |
43 | pub 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 | 73 | impl<'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)] |
94 | pub enum Packages { | |
ba7911dd | 95 | Default, |
62c04979 | 96 | All, |
bb643cca AK |
97 | OptOut(Vec<String>), |
98 | Packages(Vec<String>), | |
62c04979 AR |
99 | } |
100 | ||
bb643cca | 101 | impl 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)] |
175 | pub enum FilterRule { | |
2a82378a | 176 | All, |
bb643cca | 177 | Just(Vec<String>), |
2a82378a BW |
178 | } |
179 | ||
876af7c1 | 180 | #[derive(Debug)] |
bb643cca | 181 | pub 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 |
196 | pub 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 |
206 | pub 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 |
215 | pub 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 |
342 | impl 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 | 373 | impl 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)] | |
477 | struct 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`. | |
491 | fn 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 |
747 | fn 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 | 766 | fn 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 | 796 | fn 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. |
835 | fn 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 | } |