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