]>
Commit | Line | Data |
---|---|---|
2599c34d | 1 | //! The Cargo "compile" operation. |
64ff29ff | 2 | //! |
2599c34d EH |
3 | //! This module contains the entry point for starting the compilation process |
4 | //! for commands like `build`, `test`, `doc`, `rustc`, etc. | |
64ff29ff | 5 | //! |
2599c34d EH |
6 | //! The `compile` function will do all the work to compile a workspace. A |
7 | //! rough outline is: | |
64ff29ff | 8 | //! |
2599c34d | 9 | //! - Resolve the dependency graph (see `ops::resolve`). |
e0bd9e23 | 10 | //! - Download any packages needed (see `PackageSet`). |
2599c34d EH |
11 | //! - Generate a list of top-level "units" of work for the targets the user |
12 | //! requested on the command-line. Each `Unit` corresponds to a compiler | |
13 | //! invocation. This is done in this module (`generate_targets`). | |
e0bd9e23 EH |
14 | //! - Build the graph of `Unit` dependencies (see |
15 | //! `core::compiler::context::unit_dependencies`). | |
2599c34d | 16 | //! - Create a `Context` which will perform the following steps: |
2599c34d EH |
17 | //! - Prepare the `target` directory (see `Layout`). |
18 | //! - Create a job queue (see `JobQueue`). The queue checks the | |
19 | //! fingerprint of each `Unit` to determine if it should run or be | |
20 | //! skipped. | |
21 | //! - Execute the queue. Each leaf in the queue's dependency graph is | |
22 | //! executed, and then removed from the graph when finished. This | |
23 | //! repeats until the queue is empty. | |
62bff631 | 24 | |
45ce445c DW |
25 | use std::collections::{BTreeSet, HashMap, HashSet}; |
26 | use std::iter::FromIterator; | |
1ab19a5a | 27 | use std::sync::Arc; |
12af54e1 | 28 | |
1f14fa31 | 29 | use crate::core::compiler::unit_dependencies::build_unit_dependencies; |
e0bd9e23 | 30 | use crate::core::compiler::{standard_lib, unit_graph}; |
e4544466 | 31 | use crate::core::compiler::{BuildConfig, BuildContext, Compilation, Context}; |
949eccac | 32 | use crate::core::compiler::{CompileKind, CompileMode, RustcTargetData, Unit}; |
e4544466 | 33 | use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner}; |
04ddd4d0 | 34 | use crate::core::profiles::{Profiles, UnitFor}; |
137642c4 | 35 | use crate::core::resolver::features::{self, FeaturesFor}; |
e0d64f94 | 36 | use crate::core::resolver::{HasDevUnits, Resolve, ResolveOpts}; |
98b6f816 | 37 | use crate::core::{Package, PackageSet, Target}; |
04ddd4d0 DW |
38 | use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace}; |
39 | use crate::ops; | |
12392f6d | 40 | use crate::ops::resolve::WorkspaceResolve; |
04ddd4d0 | 41 | use crate::util::config::Config; |
7d7fe679 | 42 | use crate::util::{closest_msg, profile, CargoResult}; |
50f110a4 | 43 | |
bacb6be3 | 44 | /// Contains information about how a package should be compiled. |
e0bd9e23 EH |
45 | /// |
46 | /// Note on distinction between `CompileOptions` and `BuildConfig`: | |
47 | /// `BuildConfig` contains values that need to be retained after | |
48 | /// `BuildContext` is created. The other fields are no longer necessary. Think | |
49 | /// of it as `CompileOptions` are high-level settings requested on the | |
50 | /// command-line, and `BuildConfig` are low-level settings for actually | |
51 | /// driving `rustc`. | |
876af7c1 | 52 | #[derive(Debug)] |
e4918c45 | 53 | pub struct CompileOptions { |
08025169 DO |
54 | /// Configuration information for a rustc build |
55 | pub build_config: BuildConfig, | |
9e779198 | 56 | /// Extra features to build for the root package |
bb643cca | 57 | pub features: Vec<String>, |
1f8b3988 DA |
58 | /// Flag whether all available features should be built for the root package |
59 | pub all_features: bool, | |
9e779198 | 60 | /// Flag if the default feature should be built for the root package |
2b46d039 | 61 | pub no_default_features: bool, |
6b32c4d7 | 62 | /// A set of packages to build. |
bb643cca | 63 | pub spec: Packages, |
9e779198 AC |
64 | /// Filter to apply to the root package to select which targets will be |
65 | /// built. | |
bb643cca | 66 | pub filter: CompileFilter, |
1ef954ea | 67 | /// Extra arguments to be passed to rustdoc (single target only) |
bb643cca | 68 | pub target_rustdoc_args: Option<Vec<String>>, |
77bb01ec SL |
69 | /// The specified target will be compiled with all the available arguments, |
70 | /// note that this only accounts for the *final* invocation of rustc | |
bb643cca | 71 | pub target_rustc_args: Option<Vec<String>>, |
1ef954ea EH |
72 | /// Extra arguments passed to all selected targets for rustdoc. |
73 | pub local_rustdoc_args: Option<Vec<String>>, | |
e7ee237c LK |
74 | /// Whether the `--document-private-items` flags was specified and should |
75 | /// be forwarded to `rustdoc`. | |
76 | pub rustdoc_document_private_items: bool, | |
9e779198 AC |
77 | } |
78 | ||
e4918c45 EH |
79 | impl<'a> CompileOptions { |
80 | pub fn new(config: &Config, mode: CompileMode) -> CargoResult<CompileOptions> { | |
08025169 | 81 | Ok(CompileOptions { |
3fd28143 | 82 | build_config: BuildConfig::new(config, None, &[], mode)?, |
bb643cca | 83 | features: Vec::new(), |
69caa63b NC |
84 | all_features: false, |
85 | no_default_features: false, | |
bb643cca | 86 | spec: ops::Packages::Packages(Vec::new()), |
1e682848 AC |
87 | filter: CompileFilter::Default { |
88 | required_features_filterable: false, | |
89 | }, | |
69caa63b NC |
90 | target_rustdoc_args: None, |
91 | target_rustc_args: None, | |
1ef954ea | 92 | local_rustdoc_args: None, |
e7ee237c | 93 | rustdoc_document_private_items: false, |
08025169 | 94 | }) |
69caa63b NC |
95 | } |
96 | } | |
97 | ||
bb643cca AK |
98 | #[derive(Clone, PartialEq, Eq, Debug)] |
99 | pub enum Packages { | |
ba7911dd | 100 | Default, |
62c04979 | 101 | All, |
bb643cca AK |
102 | OptOut(Vec<String>), |
103 | Packages(Vec<String>), | |
62c04979 AR |
104 | } |
105 | ||
bb643cca | 106 | impl Packages { |
1e682848 | 107 | pub fn from_flags(all: bool, exclude: Vec<String>, package: Vec<String>) -> CargoResult<Self> { |
ba7911dd SS |
108 | Ok(match (all, exclude.len(), package.len()) { |
109 | (false, 0, 0) => Packages::Default, | |
110 | (false, 0, _) => Packages::Packages(package), | |
3a18c89a | 111 | (false, _, _) => anyhow::bail!("--exclude can only be used together with --workspace"), |
ba7911dd SS |
112 | (true, 0, _) => Packages::All, |
113 | (true, _, _) => Packages::OptOut(exclude), | |
114 | }) | |
7ffd1cfc | 115 | } |
116 | ||
b8b7faee | 117 | pub fn to_package_id_specs(&self, ws: &Workspace<'_>) -> CargoResult<Vec<PackageIdSpec>> { |
d347908d | 118 | let specs = match self { |
dae87a26 E |
119 | Packages::All => ws |
120 | .members() | |
1e682848 AC |
121 | .map(Package::package_id) |
122 | .map(PackageIdSpec::from_package_id) | |
123 | .collect(), | |
45ce445c DW |
124 | Packages::OptOut(opt_out) => { |
125 | let mut opt_out = BTreeSet::from_iter(opt_out.iter().cloned()); | |
126 | let packages = ws | |
127 | .members() | |
128 | .filter(|pkg| !opt_out.remove(pkg.name().as_str())) | |
129 | .map(Package::package_id) | |
130 | .map(PackageIdSpec::from_package_id) | |
131 | .collect(); | |
132 | if !opt_out.is_empty() { | |
133 | ws.config().shell().warn(format!( | |
134 | "excluded package(s) {} not found in workspace `{}`", | |
f16efff1 AC |
135 | opt_out |
136 | .iter() | |
137 | .map(|x| x.as_ref()) | |
138 | .collect::<Vec<_>>() | |
139 | .join(", "), | |
45ce445c DW |
140 | ws.root().display(), |
141 | ))?; | |
142 | } | |
143 | packages | |
f16efff1 | 144 | } |
d347908d | 145 | Packages::Packages(packages) if packages.is_empty() => { |
037a879d EH |
146 | vec![PackageIdSpec::from_package_id(ws.current()?.package_id())] |
147 | } | |
d347908d | 148 | Packages::Packages(packages) => packages |
1e682848 AC |
149 | .iter() |
150 | .map(|p| PackageIdSpec::parse(p)) | |
151 | .collect::<CargoResult<Vec<_>>>()?, | |
dae87a26 E |
152 | Packages::Default => ws |
153 | .default_members() | |
1e682848 AC |
154 | .map(Package::package_id) |
155 | .map(PackageIdSpec::from_package_id) | |
156 | .collect(), | |
6b32c4d7 | 157 | }; |
68a681ff | 158 | if specs.is_empty() { |
691f95de | 159 | if ws.is_virtual() { |
3a18c89a | 160 | anyhow::bail!( |
1e682848 AC |
161 | "manifest path `{}` contains no package: The manifest is virtual, \ |
162 | and the workspace has no members.", | |
163 | ws.root().display() | |
164 | ) | |
05b896ac | 165 | } |
3a18c89a | 166 | anyhow::bail!("no packages to compile") |
68a681ff | 167 | } |
6b32c4d7 AK |
168 | Ok(specs) |
169 | } | |
fa54a794 | 170 | |
b8b7faee | 171 | pub fn get_packages<'ws>(&self, ws: &'ws Workspace<'_>) -> CargoResult<Vec<&'ws Package>> { |
fa54a794 EH |
172 | let packages: Vec<_> = match self { |
173 | Packages::Default => ws.default_members().collect(), | |
174 | Packages::All => ws.members().collect(), | |
d347908d | 175 | Packages::OptOut(opt_out) => ws |
fa54a794 EH |
176 | .members() |
177 | .filter(|pkg| !opt_out.iter().any(|name| pkg.name().as_str() == name)) | |
178 | .collect(), | |
d347908d | 179 | Packages::Packages(packages) => packages |
fa54a794 EH |
180 | .iter() |
181 | .map(|name| { | |
182 | ws.members() | |
183 | .find(|pkg| pkg.name().as_str() == name) | |
184 | .ok_or_else(|| { | |
3a18c89a | 185 | anyhow::format_err!( |
54c42142 DW |
186 | "package `{}` is not a member of the workspace", |
187 | name | |
188 | ) | |
fa54a794 | 189 | }) |
dae87a26 E |
190 | }) |
191 | .collect::<CargoResult<Vec<_>>>()?, | |
fa54a794 EH |
192 | }; |
193 | Ok(packages) | |
194 | } | |
44c535ab EH |
195 | |
196 | /// Returns whether or not the user needs to pass a `-p` flag to target a | |
197 | /// specific package in the workspace. | |
198 | pub fn needs_spec_flag(&self, ws: &Workspace<'_>) -> bool { | |
199 | match self { | |
200 | Packages::Default => ws.default_members().count() > 1, | |
201 | Packages::All => ws.members().count() > 1, | |
202 | Packages::Packages(_) => true, | |
203 | Packages::OptOut(_) => true, | |
204 | } | |
205 | } | |
6b32c4d7 AK |
206 | } |
207 | ||
aef99e0e DW |
208 | #[derive(Debug, PartialEq, Eq)] |
209 | pub enum LibRule { | |
210 | /// Include the library, fail if not present | |
211 | True, | |
212 | /// Include the library if present | |
213 | Default, | |
214 | /// Exclude the library | |
215 | False, | |
216 | } | |
217 | ||
bb643cca AK |
218 | #[derive(Debug)] |
219 | pub enum FilterRule { | |
2a82378a | 220 | All, |
bb643cca | 221 | Just(Vec<String>), |
2a82378a BW |
222 | } |
223 | ||
876af7c1 | 224 | #[derive(Debug)] |
bb643cca | 225 | pub enum CompileFilter { |
6344db05 | 226 | Default { |
25e50b58 JB |
227 | /// Flag whether targets can be safely skipped when required-features are not satisfied. |
228 | required_features_filterable: bool, | |
229 | }, | |
9e779198 | 230 | Only { |
b9497ab2 | 231 | all_targets: bool, |
aef99e0e | 232 | lib: LibRule, |
bb643cca AK |
233 | bins: FilterRule, |
234 | examples: FilterRule, | |
235 | tests: FilterRule, | |
236 | benches: FilterRule, | |
1e682848 | 237 | }, |
a1980dc7 YKCL |
238 | } |
239 | ||
e4918c45 | 240 | pub fn compile<'a>(ws: &Workspace<'a>, options: &CompileOptions) -> CargoResult<Compilation<'a>> { |
b8b7faee | 241 | let exec: Arc<dyn Executor> = Arc::new(DefaultExecutor); |
689f412c | 242 | compile_with_exec(ws, options, &exec) |
8c3b3605 NC |
243 | } |
244 | ||
4a64d05e | 245 | /// Like `compile` but allows specifying a custom `Executor` that will be able to intercept build |
d70f91ee | 246 | /// calls and add custom logic. `compile` uses `DefaultExecutor` which just passes calls through. |
1e682848 AC |
247 | pub fn compile_with_exec<'a>( |
248 | ws: &Workspace<'a>, | |
e4918c45 | 249 | options: &CompileOptions, |
b8b7faee | 250 | exec: &Arc<dyn Executor>, |
a340ba0b | 251 | ) -> CargoResult<Compilation<'a>> { |
688a4fa6 | 252 | ws.emit_warnings()?; |
8a301589 | 253 | compile_ws(ws, options, exec) |
9fba127e AC |
254 | } |
255 | ||
1e682848 AC |
256 | pub fn compile_ws<'a>( |
257 | ws: &Workspace<'a>, | |
e4918c45 | 258 | options: &CompileOptions, |
b8b7faee | 259 | exec: &Arc<dyn Executor>, |
a340ba0b | 260 | ) -> CargoResult<Compilation<'a>> { |
e0bd9e23 EH |
261 | let interner = UnitInterner::new(); |
262 | let bcx = create_bcx(ws, options, &interner)?; | |
263 | if options.build_config.unit_graph { | |
264 | unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph)?; | |
265 | return Ok(Compilation::new(&bcx)?); | |
266 | } | |
267 | ||
268 | let _p = profile::start("compiling"); | |
269 | let cx = Context::new(&bcx)?; | |
270 | cx.compile(exec) | |
271 | } | |
272 | ||
273 | pub fn create_bcx<'a, 'cfg>( | |
274 | ws: &'a Workspace<'cfg>, | |
275 | options: &'a CompileOptions, | |
276 | interner: &'a UnitInterner, | |
277 | ) -> CargoResult<BuildContext<'a, 'cfg>> { | |
1e682848 | 278 | let CompileOptions { |
08025169 | 279 | ref build_config, |
1e682848 AC |
280 | ref spec, |
281 | ref features, | |
282 | all_features, | |
283 | no_default_features, | |
1e682848 AC |
284 | ref filter, |
285 | ref target_rustdoc_args, | |
286 | ref target_rustc_args, | |
1ef954ea | 287 | ref local_rustdoc_args, |
e7ee237c | 288 | rustdoc_document_private_items, |
1e682848 | 289 | } = *options; |
e4918c45 | 290 | let config = ws.config(); |
3656bec8 | 291 | |
d17ccec2 HY |
292 | match build_config.mode { |
293 | CompileMode::Test | |
294 | | CompileMode::Build | |
295 | | CompileMode::Check { .. } | |
296 | | CompileMode::Bench | |
297 | | CompileMode::RunCustomBuild => { | |
298 | if std::env::var("RUST_FLAGS").is_ok() { | |
299 | config.shell().warn( | |
300 | "Cargo does not read `RUST_FLAGS` environment variable. Did you mean `RUSTFLAGS`?", | |
301 | )?; | |
302 | } | |
303 | } | |
304 | CompileMode::Doc { .. } | CompileMode::Doctest => { | |
305 | if std::env::var("RUSTDOC_FLAGS").is_ok() { | |
306 | config.shell().warn( | |
307 | "Cargo does not read `RUSTDOC_FLAGS` environment variable. Did you mean `RUSTDOCFLAGS`?" | |
308 | )?; | |
309 | } | |
310 | } | |
311 | } | |
312 | ||
3fd28143 | 313 | let target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?; |
47007d97 | 314 | |
8947ed1f | 315 | let specs = spec.to_package_id_specs(ws)?; |
e26ef017 EH |
316 | let dev_deps = ws.require_optional_deps() || filter.need_dev_deps(build_config.mode); |
317 | let opts = ResolveOpts::new(dev_deps, features, all_features, !no_default_features); | |
2b1dc3e5 AT |
318 | let has_dev_units = if filter.need_dev_deps(build_config.mode) { |
319 | HasDevUnits::Yes | |
320 | } else { | |
321 | HasDevUnits::No | |
e0d64f94 | 322 | }; |
d728f386 EH |
323 | let resolve = ops::resolve_ws_with_opts( |
324 | ws, | |
325 | &target_data, | |
3fd28143 | 326 | &build_config.requested_kinds, |
d728f386 EH |
327 | &opts, |
328 | &specs, | |
329 | has_dev_units, | |
d267fac2 | 330 | crate::core::resolver::features::ForceAllTargets::No, |
d728f386 | 331 | )?; |
12392f6d EH |
332 | let WorkspaceResolve { |
333 | mut pkg_set, | |
334 | workspace_resolve, | |
335 | targeted_resolve: resolve, | |
7caa1612 | 336 | resolved_features, |
12392f6d | 337 | } = resolve; |
1f14fa31 | 338 | |
7caa1612 | 339 | let std_resolve_features = if let Some(crates) = &config.cli_unstable().build_std { |
3afb5d7d EH |
340 | if build_config.build_plan { |
341 | config | |
342 | .shell() | |
343 | .warn("-Zbuild-std does not currently fully support --build-plan")?; | |
344 | } | |
3fd28143 | 345 | if build_config.requested_kinds[0].is_host() { |
1f14fa31 EH |
346 | // TODO: This should eventually be fixed. Unfortunately it is not |
347 | // easy to get the host triple in BuildConfig. Consider changing | |
348 | // requested_target to an enum, or some other approach. | |
3a18c89a | 349 | anyhow::bail!("-Zbuild-std requires --target"); |
1f14fa31 | 350 | } |
98b6f816 | 351 | let (std_package_set, std_resolve, std_features) = |
3fd28143 | 352 | standard_lib::resolve_std(ws, &target_data, &build_config.requested_kinds, crates)?; |
12392f6d | 353 | pkg_set.add_set(std_package_set); |
7caa1612 | 354 | Some((std_resolve, std_features)) |
1f14fa31 EH |
355 | } else { |
356 | None | |
357 | }; | |
9c296792 | 358 | |
1f14fa31 EH |
359 | // Find the packages in the resolver that the user wants to build (those |
360 | // passed in with `-p` or the defaults from the workspace), and convert | |
96a39371 EH |
361 | // Vec<PackageIdSpec> to a Vec<PackageId>. |
362 | let to_build_ids = resolve.specs_to_ids(&specs)?; | |
1f14fa31 EH |
363 | // Now get the `Package` for each `PackageId`. This may trigger a download |
364 | // if the user specified `-p` for a dependency that is not downloaded. | |
365 | // Dependencies will be downloaded during build_unit_dependencies. | |
12392f6d | 366 | let mut to_builds = pkg_set.get_many(to_build_ids)?; |
0ea0c8ba | 367 | |
7b1a0dc5 AC |
368 | // The ordering here affects some error messages coming out of cargo, so |
369 | // let's be test and CLI friendly by always printing in the same order if | |
370 | // there's an error. | |
371 | to_builds.sort_by_key(|p| p.package_id()); | |
372 | ||
373 | for pkg in to_builds.iter() { | |
1f14fa31 | 374 | pkg.manifest().print_teapot(config); |
786848a0 EH |
375 | |
376 | if build_config.mode.is_any_test() | |
377 | && !ws.is_member(pkg) | |
378 | && pkg.dependencies().iter().any(|dep| !dep.is_transitive()) | |
379 | { | |
3a18c89a | 380 | anyhow::bail!( |
786848a0 EH |
381 | "package `{}` cannot be tested because it requires dev-dependencies \ |
382 | and is not a member of the workspace", | |
383 | pkg.name() | |
384 | ); | |
385 | } | |
7b1a0dc5 | 386 | } |
f0647ea2 | 387 | |
575d6e81 EH |
388 | let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) { |
389 | (&Some(ref args), _) => (Some(args.clone()), "rustc"), | |
390 | (_, &Some(ref args)) => (Some(args.clone()), "rustdoc"), | |
391 | _ => (None, ""), | |
f0647ea2 | 392 | }; |
b3ade7c7 | 393 | |
575d6e81 EH |
394 | if extra_args.is_some() && to_builds.len() != 1 { |
395 | panic!( | |
396 | "`{}` should not accept multiple `-p` flags", | |
397 | extra_args_name | |
398 | ); | |
399 | } | |
400 | ||
e0bd9e23 EH |
401 | let profiles = Profiles::new( |
402 | ws.profiles(), | |
403 | config, | |
404 | build_config.requested_profile, | |
405 | ws.features(), | |
406 | )?; | |
12392f6d | 407 | profiles.validate_packages( |
77ee608d | 408 | ws.profiles(), |
12392f6d EH |
409 | &mut config.shell(), |
410 | workspace_resolve.as_ref().unwrap_or(&resolve), | |
411 | )?; | |
a0a880c3 | 412 | |
575d6e81 | 413 | let units = generate_targets( |
73660740 | 414 | ws, |
575d6e81 EH |
415 | &to_builds, |
416 | filter, | |
3fd28143 | 417 | &build_config.requested_kinds, |
e0bd9e23 | 418 | build_config.mode, |
12392f6d | 419 | &resolve, |
7caa1612 | 420 | &resolved_features, |
e0bd9e23 EH |
421 | &pkg_set, |
422 | &profiles, | |
686ccfa4 | 423 | interner, |
575d6e81 EH |
424 | )?; |
425 | ||
1f14fa31 | 426 | let std_roots = if let Some(crates) = &config.cli_unstable().build_std { |
3ba9de85 EH |
427 | // Only build libtest if it looks like it is needed. |
428 | let mut crates = crates.clone(); | |
429 | if !crates.iter().any(|c| c == "test") | |
430 | && units | |
431 | .iter() | |
432 | .any(|unit| unit.mode.is_rustc_test() && unit.target.harness()) | |
433 | { | |
18a89732 PO |
434 | // Only build libtest when libstd is built (libtest depends on libstd) |
435 | if crates.iter().any(|c| c == "std") { | |
436 | crates.push("test".to_string()); | |
437 | } | |
3ba9de85 | 438 | } |
7caa1612 | 439 | let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap(); |
593a02f2 | 440 | standard_lib::generate_std_roots( |
593a02f2 | 441 | &crates, |
7caa1612 EH |
442 | std_resolve, |
443 | std_features, | |
3fd28143 | 444 | &build_config.requested_kinds, |
e0bd9e23 | 445 | &pkg_set, |
686ccfa4 | 446 | interner, |
e0bd9e23 | 447 | &profiles, |
593a02f2 | 448 | )? |
1f14fa31 | 449 | } else { |
3fd28143 | 450 | Default::default() |
1f14fa31 EH |
451 | }; |
452 | ||
e0bd9e23 | 453 | let mut extra_compiler_args = HashMap::new(); |
575d6e81 EH |
454 | if let Some(args) = extra_args { |
455 | if units.len() != 1 { | |
3a18c89a | 456 | anyhow::bail!( |
575d6e81 | 457 | "extra arguments to `{}` can only be passed to one \ |
f7c91ba6 AR |
458 | target, consider filtering\nthe package by passing, \ |
459 | e.g., `--lib` or `--bin NAME` to specify a single target", | |
575d6e81 EH |
460 | extra_args_name |
461 | ); | |
8a8ea1e8 | 462 | } |
45d49579 | 463 | extra_compiler_args.insert(units[0].clone(), args); |
1ef954ea | 464 | } |
e7ee237c LK |
465 | for unit in &units { |
466 | if unit.mode.is_doc() || unit.mode.is_doc_test() { | |
467 | let mut extra_args = local_rustdoc_args.clone(); | |
468 | ||
469 | // Add `--document-private-items` rustdoc flag if requested or if | |
470 | // the target is a binary. Binary crates get their private items | |
471 | // documented by default. | |
472 | if rustdoc_document_private_items || unit.target.is_bin() { | |
a6a395c6 | 473 | let mut args = extra_args.take().unwrap_or_else(|| vec![]); |
e7ee237c LK |
474 | args.push("--document-private-items".into()); |
475 | extra_args = Some(args); | |
476 | } | |
477 | ||
1d1b3447 JN |
478 | if let Some(args) = extra_args { |
479 | extra_compiler_args | |
480 | .entry(unit.clone()) | |
481 | .or_default() | |
482 | .extend(args); | |
1ef954ea EH |
483 | } |
484 | } | |
b3ade7c7 | 485 | } |
575d6e81 | 486 | |
e0bd9e23 EH |
487 | let unit_graph = build_unit_dependencies( |
488 | ws, | |
489 | &pkg_set, | |
7caa1612 EH |
490 | &resolve, |
491 | &resolved_features, | |
492 | std_resolve_features.as_ref(), | |
493 | &units, | |
494 | &std_roots, | |
e0bd9e23 EH |
495 | build_config.mode, |
496 | &target_data, | |
497 | &profiles, | |
686ccfa4 | 498 | interner, |
7caa1612 | 499 | )?; |
9a8d695b | 500 | |
e0bd9e23 EH |
501 | let bcx = BuildContext::new( |
502 | ws, | |
503 | pkg_set, | |
504 | build_config, | |
505 | profiles, | |
506 | extra_compiler_args, | |
507 | target_data, | |
508 | units, | |
509 | unit_graph, | |
510 | )?; | |
8b840393 | 511 | |
e0bd9e23 | 512 | Ok(bcx) |
62bff631 | 513 | } |
51c9cf0f | 514 | |
bb643cca AK |
515 | impl FilterRule { |
516 | pub fn new(targets: Vec<String>, all: bool) -> FilterRule { | |
2a82378a BW |
517 | if all { |
518 | FilterRule::All | |
519 | } else { | |
520 | FilterRule::Just(targets) | |
521 | } | |
522 | } | |
523 | ||
12307fc2 DW |
524 | pub fn none() -> FilterRule { |
525 | FilterRule::Just(Vec::new()) | |
526 | } | |
527 | ||
2a82378a BW |
528 | fn matches(&self, target: &Target) -> bool { |
529 | match *self { | |
530 | FilterRule::All => true, | |
1e682848 | 531 | FilterRule::Just(ref targets) => targets.iter().any(|x| *x == target.name()), |
2a82378a BW |
532 | } |
533 | } | |
534 | ||
535 | fn is_specific(&self) -> bool { | |
536 | match *self { | |
537 | FilterRule::All => true, | |
bb643cca | 538 | FilterRule::Just(ref targets) => !targets.is_empty(), |
2a82378a BW |
539 | } |
540 | } | |
541 | ||
542 | pub fn try_collect(&self) -> Option<Vec<String>> { | |
543 | match *self { | |
544 | FilterRule::All => None, | |
bb643cca | 545 | FilterRule::Just(ref targets) => Some(targets.clone()), |
2a82378a BW |
546 | } |
547 | } | |
548 | } | |
549 | ||
bb643cca | 550 | impl CompileFilter { |
12307fc2 DW |
551 | /// Construct a CompileFilter from raw command line arguments. |
552 | pub fn from_raw_arguments( | |
1e682848 AC |
553 | lib_only: bool, |
554 | bins: Vec<String>, | |
555 | all_bins: bool, | |
556 | tsts: Vec<String>, | |
557 | all_tsts: bool, | |
558 | exms: Vec<String>, | |
559 | all_exms: bool, | |
560 | bens: Vec<String>, | |
561 | all_bens: bool, | |
562 | all_targets: bool, | |
563 | ) -> CompileFilter { | |
5a59b809 EH |
564 | if all_targets { |
565 | return CompileFilter::new_all_targets(); | |
566 | } | |
f16efff1 AC |
567 | let rule_lib = if lib_only { |
568 | LibRule::True | |
569 | } else { | |
570 | LibRule::False | |
571 | }; | |
2a82378a BW |
572 | let rule_bins = FilterRule::new(bins, all_bins); |
573 | let rule_tsts = FilterRule::new(tsts, all_tsts); | |
574 | let rule_exms = FilterRule::new(exms, all_exms); | |
575 | let rule_bens = FilterRule::new(bens, all_bens); | |
576 | ||
5a59b809 | 577 | CompileFilter::new(rule_lib, rule_bins, rule_tsts, rule_exms, rule_bens) |
12307fc2 DW |
578 | } |
579 | ||
580 | /// Construct a CompileFilter from underlying primitives. | |
581 | pub fn new( | |
aef99e0e | 582 | rule_lib: LibRule, |
12307fc2 DW |
583 | rule_bins: FilterRule, |
584 | rule_tsts: FilterRule, | |
585 | rule_exms: FilterRule, | |
586 | rule_bens: FilterRule, | |
587 | ) -> CompileFilter { | |
aef99e0e | 588 | if rule_lib == LibRule::True |
dae87a26 E |
589 | || rule_bins.is_specific() |
590 | || rule_tsts.is_specific() | |
591 | || rule_exms.is_specific() | |
592 | || rule_bens.is_specific() | |
1e682848 | 593 | { |
7a1d8d94 | 594 | CompileFilter::Only { |
b9497ab2 | 595 | all_targets: false, |
aef99e0e | 596 | lib: rule_lib, |
1e682848 AC |
597 | bins: rule_bins, |
598 | examples: rule_exms, | |
599 | benches: rule_bens, | |
2a82378a | 600 | tests: rule_tsts, |
7a1d8d94 AC |
601 | } |
602 | } else { | |
6344db05 | 603 | CompileFilter::Default { |
25e50b58 JB |
604 | required_features_filterable: true, |
605 | } | |
7a1d8d94 AC |
606 | } |
607 | } | |
608 | ||
5a59b809 EH |
609 | pub fn new_all_targets() -> CompileFilter { |
610 | CompileFilter::Only { | |
611 | all_targets: true, | |
612 | lib: LibRule::Default, | |
613 | bins: FilterRule::All, | |
614 | examples: FilterRule::All, | |
615 | benches: FilterRule::All, | |
616 | tests: FilterRule::All, | |
617 | } | |
618 | } | |
619 | ||
89d5161d XL |
620 | pub fn need_dev_deps(&self, mode: CompileMode) -> bool { |
621 | match mode { | |
622 | CompileMode::Test | CompileMode::Doctest | CompileMode::Bench => true, | |
b112255f EH |
623 | CompileMode::Check { test: true } => true, |
624 | CompileMode::Build | CompileMode::Doc { .. } | CompileMode::Check { test: false } => { | |
625 | match *self { | |
626 | CompileFilter::Default { .. } => false, | |
627 | CompileFilter::Only { | |
628 | ref examples, | |
629 | ref tests, | |
630 | ref benches, | |
631 | .. | |
632 | } => examples.is_specific() || tests.is_specific() || benches.is_specific(), | |
633 | } | |
634 | } | |
575d6e81 | 635 | CompileMode::RunCustomBuild => panic!("Invalid mode"), |
7de30dd2 XL |
636 | } |
637 | } | |
638 | ||
ce26ddfd | 639 | // this selects targets for "cargo run". for logic to select targets for |
771fec3c | 640 | // other subcommands, see generate_targets and filter_default_targets |
ce26ddfd | 641 | pub fn target_run(&self, target: &Target) -> bool { |
9e779198 | 642 | match *self { |
70170d1b | 643 | CompileFilter::Default { .. } => true, |
1e682848 | 644 | CompileFilter::Only { |
aef99e0e | 645 | ref lib, |
1e682848 AC |
646 | ref bins, |
647 | ref examples, | |
648 | ref tests, | |
649 | ref benches, | |
650 | .. | |
651 | } => { | |
2a82378a | 652 | let rule = match *target.kind() { |
9e779198 AC |
653 | TargetKind::Bin => bins, |
654 | TargetKind::Test => tests, | |
655 | TargetKind::Bench => benches, | |
1e682848 | 656 | TargetKind::ExampleBin | TargetKind::ExampleLib(..) => examples, |
f16efff1 AC |
657 | TargetKind::Lib(..) => { |
658 | return match *lib { | |
659 | LibRule::True => true, | |
660 | LibRule::Default => true, | |
661 | LibRule::False => false, | |
662 | }; | |
663 | } | |
9e779198 AC |
664 | TargetKind::CustomBuild => return false, |
665 | }; | |
2a82378a | 666 | rule.matches(target) |
9e779198 AC |
667 | } |
668 | } | |
669 | } | |
c98b8c4c BW |
670 | |
671 | pub fn is_specific(&self) -> bool { | |
672 | match *self { | |
6344db05 | 673 | CompileFilter::Default { .. } => false, |
c98b8c4c BW |
674 | CompileFilter::Only { .. } => true, |
675 | } | |
676 | } | |
9e779198 AC |
677 | } |
678 | ||
3a1cad6f EH |
679 | /// A proposed target. |
680 | /// | |
f7c91ba6 | 681 | /// Proposed targets are later filtered into actual `Unit`s based on whether or |
3a1cad6f EH |
682 | /// not the target requires its features to be present. |
683 | #[derive(Debug)] | |
684 | struct Proposal<'a> { | |
fbd34a69 | 685 | pkg: &'a Package, |
45d49579 | 686 | target: &'a Target, |
3a1cad6f EH |
687 | /// Indicates whether or not all required features *must* be present. If |
688 | /// false, and the features are not available, then it will be silently | |
689 | /// skipped. Generally, targets specified by name (`--bin foo`) are | |
690 | /// required, all others can be silently skipped if features are missing. | |
691 | requires_features: bool, | |
692 | mode: CompileMode, | |
693 | } | |
694 | ||
575d6e81 | 695 | /// Generates all the base targets for the packages the user has requested to |
f7c91ba6 | 696 | /// compile. Dependencies for these targets are computed later in `unit_dependencies`. |
c85ed044 | 697 | fn generate_targets( |
b8b7faee | 698 | ws: &Workspace<'_>, |
fbd34a69 | 699 | packages: &[&Package], |
575d6e81 | 700 | filter: &CompileFilter, |
3fd28143 | 701 | requested_kinds: &[CompileKind], |
e0bd9e23 EH |
702 | mode: CompileMode, |
703 | resolve: &Resolve, | |
7caa1612 | 704 | resolved_features: &features::ResolvedFeatures, |
e0bd9e23 EH |
705 | package_set: &PackageSet<'_>, |
706 | profiles: &Profiles, | |
c85ed044 AC |
707 | interner: &UnitInterner, |
708 | ) -> CargoResult<Vec<Unit>> { | |
e0bd9e23 | 709 | let config = ws.config(); |
3fd28143 AC |
710 | // Helper for creating a list of `Unit` structures |
711 | let new_unit = | |
712 | |units: &mut HashSet<Unit>, pkg: &Package, target: &Target, target_mode: CompileMode| { | |
713 | let unit_for = if target_mode.is_any_test() { | |
714 | // NOTE: the `UnitFor` here is subtle. If you have a profile | |
715 | // with `panic` set, the `panic` flag is cleared for | |
716 | // tests/benchmarks and their dependencies. If this | |
717 | // was `normal`, then the lib would get compiled three | |
718 | // times (once with panic, once without, and once with | |
719 | // `--test`). | |
720 | // | |
721 | // This would cause a problem for doc tests, which would fail | |
722 | // because `rustdoc` would attempt to link with both libraries | |
723 | // at the same time. Also, it's probably not important (or | |
724 | // even desirable?) for rustdoc to link with a lib with | |
725 | // `panic` set. | |
726 | // | |
727 | // As a consequence, Examples and Binaries get compiled | |
728 | // without `panic` set. This probably isn't a bad deal. | |
729 | // | |
730 | // Forcing the lib to be compiled three times during `cargo | |
731 | // test` is probably also not desirable. | |
732 | UnitFor::new_test(config) | |
733 | } else if target.for_host() { | |
734 | // Proc macro / plugin should not have `panic` set. | |
735 | UnitFor::new_compiler() | |
736 | } else { | |
737 | UnitFor::new_normal() | |
738 | }; | |
739 | // Custom build units are added in `build_unit_dependencies`. | |
740 | assert!(!target.is_custom_build()); | |
741 | let target_mode = match target_mode { | |
742 | CompileMode::Test => { | |
743 | if target.is_example() && !filter.is_specific() && !target.tested() { | |
744 | // Examples are included as regular binaries to verify | |
745 | // that they compile. | |
746 | CompileMode::Build | |
747 | } else { | |
748 | CompileMode::Test | |
749 | } | |
2a82378a | 750 | } |
3fd28143 AC |
751 | CompileMode::Build => match *target.kind() { |
752 | TargetKind::Test => CompileMode::Test, | |
753 | TargetKind::Bench => CompileMode::Bench, | |
754 | _ => CompileMode::Build, | |
755 | }, | |
756 | // `CompileMode::Bench` is only used to inform `filter_default_targets` | |
757 | // which command is being used (`cargo bench`). Afterwards, tests | |
758 | // and benches are treated identically. Switching the mode allows | |
759 | // de-duplication of units that are essentially identical. For | |
760 | // example, `cargo build --all-targets --release` creates the units | |
761 | // (lib profile:bench, mode:test) and (lib profile:bench, mode:bench) | |
762 | // and since these are the same, we want them to be de-duplicated in | |
763 | // `unit_dependencies`. | |
764 | CompileMode::Bench => CompileMode::Test, | |
765 | _ => target_mode, | |
766 | }; | |
767 | ||
768 | let is_local = pkg.package_id().source_id().is_path(); | |
769 | let profile = profiles.get_profile( | |
770 | pkg.package_id(), | |
771 | ws.is_member(pkg), | |
772 | is_local, | |
773 | unit_for, | |
774 | target_mode, | |
775 | ); | |
776 | ||
777 | // No need to worry about build-dependencies, roots are never build dependencies. | |
778 | let features_for = FeaturesFor::from_for_host(target.proc_macro()); | |
779 | let features = resolved_features.activated_features(pkg.package_id(), features_for); | |
780 | ||
781 | for kind in requested_kinds { | |
782 | let unit = interner.intern( | |
783 | pkg, | |
784 | target, | |
785 | profile, | |
786 | kind.for_target(target), | |
787 | target_mode, | |
788 | features.clone(), | |
789 | /*is_std*/ false, | |
790 | ); | |
791 | units.insert(unit); | |
2a82378a | 792 | } |
575d6e81 EH |
793 | }; |
794 | ||
3a1cad6f | 795 | // Create a list of proposed targets. |
b8b7faee | 796 | let mut proposals: Vec<Proposal<'_>> = Vec::new(); |
771fec3c EH |
797 | |
798 | match *filter { | |
799 | CompileFilter::Default { | |
800 | required_features_filterable, | |
801 | } => { | |
802 | for pkg in packages { | |
e0bd9e23 | 803 | let default = filter_default_targets(pkg.targets(), mode); |
3a1cad6f EH |
804 | proposals.extend(default.into_iter().map(|target| Proposal { |
805 | pkg, | |
806 | target, | |
807 | requires_features: !required_features_filterable, | |
e0bd9e23 | 808 | mode, |
771fec3c | 809 | })); |
e0bd9e23 | 810 | if mode == CompileMode::Test { |
dd6c6102 EH |
811 | if let Some(t) = pkg |
812 | .targets() | |
813 | .iter() | |
814 | .find(|t| t.is_lib() && t.doctested() && t.doctestable()) | |
815 | { | |
3a1cad6f EH |
816 | proposals.push(Proposal { |
817 | pkg, | |
818 | target: t, | |
819 | requires_features: false, | |
820 | mode: CompileMode::Doctest, | |
821 | }); | |
575d6e81 | 822 | } |
2a82378a BW |
823 | } |
824 | } | |
771fec3c EH |
825 | } |
826 | CompileFilter::Only { | |
827 | all_targets, | |
aef99e0e | 828 | ref lib, |
771fec3c EH |
829 | ref bins, |
830 | ref examples, | |
831 | ref tests, | |
832 | ref benches, | |
833 | } => { | |
aef99e0e | 834 | if *lib != LibRule::False { |
771fec3c | 835 | let mut libs = Vec::new(); |
e0bd9e23 | 836 | for proposal in filter_targets(packages, Target::is_lib, false, mode) { |
df9ca871 | 837 | let Proposal { target, pkg, .. } = proposal; |
e0bd9e23 | 838 | if mode.is_doc_test() && !target.doctestable() { |
7a06be14 EH |
839 | let types = target.rustc_crate_types(); |
840 | let types_str: Vec<&str> = types.iter().map(|t| t.as_str()).collect(); | |
df9ca871 DW |
841 | ws.config().shell().warn(format!( |
842 | "doc tests are not supported for crate type(s) `{}` in package `{}`", | |
7a06be14 | 843 | types_str.join(", "), |
df9ca871 DW |
844 | pkg.name() |
845 | ))?; | |
846 | } else { | |
847 | libs.push(proposal) | |
575d6e81 EH |
848 | } |
849 | } | |
aef99e0e | 850 | if !all_targets && libs.is_empty() && *lib == LibRule::True { |
771fec3c EH |
851 | let names = packages.iter().map(|pkg| pkg.name()).collect::<Vec<_>>(); |
852 | if names.len() == 1 { | |
3a18c89a | 853 | anyhow::bail!("no library targets found in package `{}`", names[0]); |
771fec3c | 854 | } else { |
3a18c89a | 855 | anyhow::bail!("no library targets found in packages: {}", names.join(", ")); |
771fec3c EH |
856 | } |
857 | } | |
858 | proposals.extend(libs); | |
575d6e81 | 859 | } |
3a1cad6f | 860 | |
f7c91ba6 | 861 | // If `--tests` was specified, add all targets that would be |
771fec3c | 862 | // generated by `cargo test`. |
df9ca871 | 863 | let test_filter = match tests { |
771fec3c EH |
864 | FilterRule::All => Target::tested, |
865 | FilterRule::Just(_) => Target::is_test, | |
866 | }; | |
e0bd9e23 | 867 | let test_mode = match mode { |
771fec3c EH |
868 | CompileMode::Build => CompileMode::Test, |
869 | CompileMode::Check { .. } => CompileMode::Check { test: true }, | |
e0bd9e23 | 870 | _ => mode, |
771fec3c | 871 | }; |
f7c91ba6 | 872 | // If `--benches` was specified, add all targets that would be |
771fec3c | 873 | // generated by `cargo bench`. |
df9ca871 | 874 | let bench_filter = match benches { |
771fec3c EH |
875 | FilterRule::All => Target::benched, |
876 | FilterRule::Just(_) => Target::is_bench, | |
877 | }; | |
e0bd9e23 | 878 | let bench_mode = match mode { |
771fec3c EH |
879 | CompileMode::Build => CompileMode::Bench, |
880 | CompileMode::Check { .. } => CompileMode::Check { test: true }, | |
e0bd9e23 | 881 | _ => mode, |
771fec3c EH |
882 | }; |
883 | ||
884 | proposals.extend(list_rule_targets( | |
885 | packages, | |
886 | bins, | |
887 | "bin", | |
888 | Target::is_bin, | |
e0bd9e23 | 889 | mode, |
771fec3c EH |
890 | )?); |
891 | proposals.extend(list_rule_targets( | |
892 | packages, | |
893 | examples, | |
894 | "example", | |
895 | Target::is_example, | |
e0bd9e23 | 896 | mode, |
771fec3c EH |
897 | )?); |
898 | proposals.extend(list_rule_targets( | |
899 | packages, | |
900 | tests, | |
901 | "test", | |
902 | test_filter, | |
903 | test_mode, | |
904 | )?); | |
905 | proposals.extend(list_rule_targets( | |
906 | packages, | |
907 | benches, | |
908 | "bench", | |
909 | bench_filter, | |
910 | bench_mode, | |
911 | )?); | |
2a82378a | 912 | } |
771fec3c | 913 | } |
2a82378a | 914 | |
771fec3c EH |
915 | // Only include targets that are libraries or have all required |
916 | // features available. | |
7caa1612 EH |
917 | // |
918 | // `features_map` is a map of &Package -> enabled_features | |
919 | // It is computed by the set of enabled features for the package plus | |
920 | // every enabled feature of every enabled dependency. | |
771fec3c | 921 | let mut features_map = HashMap::new(); |
3a1cad6f | 922 | let mut units = HashSet::new(); |
dae87a26 E |
923 | for Proposal { |
924 | pkg, | |
925 | target, | |
926 | requires_features, | |
927 | mode, | |
928 | } in proposals | |
929 | { | |
771fec3c EH |
930 | let unavailable_features = match target.required_features() { |
931 | Some(rf) => { | |
7caa1612 | 932 | let features = features_map.entry(pkg).or_insert_with(|| { |
e0bd9e23 | 933 | resolve_all_features(resolve, resolved_features, package_set, pkg.package_id()) |
7caa1612 | 934 | }); |
771fec3c | 935 | rf.iter().filter(|f| !features.contains(*f)).collect() |
2a82378a | 936 | } |
771fec3c EH |
937 | None => Vec::new(), |
938 | }; | |
939 | if target.is_lib() || unavailable_features.is_empty() { | |
3fd28143 | 940 | new_unit(&mut units, pkg, target, mode); |
3a1cad6f | 941 | } else if requires_features { |
771fec3c EH |
942 | let required_features = target.required_features().unwrap(); |
943 | let quoted_required_features: Vec<String> = required_features | |
944 | .iter() | |
945 | .map(|s| format!("`{}`", s)) | |
946 | .collect(); | |
3a18c89a | 947 | anyhow::bail!( |
771fec3c | 948 | "target `{}` in package `{}` requires the features: {}\n\ |
f7c91ba6 | 949 | Consider enabling them by passing, e.g., `--features=\"{}\"`", |
771fec3c EH |
950 | target.name(), |
951 | pkg.name(), | |
952 | quoted_required_features.join(", "), | |
953 | required_features.join(" ") | |
954 | ); | |
2a82378a | 955 | } |
771fec3c | 956 | // else, silently skip target. |
2a82378a | 957 | } |
3a1cad6f | 958 | Ok(units.into_iter().collect()) |
2a82378a BW |
959 | } |
960 | ||
3f068230 EH |
961 | /// Gets all of the features enabled for a package, plus its dependencies' |
962 | /// features. | |
963 | /// | |
964 | /// Dependencies are added as `dep_name/feat_name` because `required-features` | |
965 | /// wants to support that syntax. | |
8b17895b | 966 | pub fn resolve_all_features( |
575d6e81 | 967 | resolve_with_overrides: &Resolve, |
7caa1612 | 968 | resolved_features: &features::ResolvedFeatures, |
944f5049 | 969 | package_set: &PackageSet<'_>, |
dae87a26 | 970 | package_id: PackageId, |
575d6e81 | 971 | ) -> HashSet<String> { |
7caa1612 | 972 | let mut features: HashSet<String> = resolved_features |
137642c4 | 973 | .activated_features(package_id, FeaturesFor::NormalOrDev) |
7caa1612 EH |
974 | .iter() |
975 | .map(|s| s.to_string()) | |
976 | .collect(); | |
575d6e81 EH |
977 | |
978 | // Include features enabled for use by dependencies so targets can also use them with the | |
979 | // required-features field when deciding whether to be built or skipped. | |
cafec114 | 980 | for (dep_id, deps) in resolve_with_overrides.deps(package_id) { |
944f5049 EH |
981 | let is_proc_macro = package_set |
982 | .get_one(dep_id) | |
983 | .expect("packages downloaded") | |
984 | .proc_macro(); | |
8973b959 | 985 | for dep in deps { |
96a39371 | 986 | let features_for = FeaturesFor::from_for_host(is_proc_macro || dep.is_build()); |
e94facea R |
987 | for feature in resolved_features |
988 | .activated_features_unverified(dep_id, features_for) | |
989 | .unwrap_or_default() | |
990 | { | |
887ee6cc | 991 | features.insert(format!("{}/{}", dep.name_in_toml(), feature)); |
cafec114 | 992 | } |
575d6e81 EH |
993 | } |
994 | } | |
995 | ||
996 | features | |
997 | } | |
998 | ||
999 | /// Given a list of all targets for a package, filters out only the targets | |
1000 | /// that are automatically included when the user doesn't specify any targets. | |
45d49579 | 1001 | fn filter_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Target> { |
575d6e81 EH |
1002 | match mode { |
1003 | CompileMode::Bench => targets.iter().filter(|t| t.benched()).collect(), | |
a4947c2b EH |
1004 | CompileMode::Test => targets |
1005 | .iter() | |
1006 | .filter(|t| t.tested() || t.is_example()) | |
1007 | .collect(), | |
575d6e81 EH |
1008 | CompileMode::Build | CompileMode::Check { .. } => targets |
1009 | .iter() | |
1010 | .filter(|t| t.is_bin() || t.is_lib()) | |
1011 | .collect(), | |
1012 | CompileMode::Doc { .. } => { | |
1013 | // `doc` does lib and bins (bin with same name as lib is skipped). | |
1014 | targets | |
1e682848 | 1015 | .iter() |
575d6e81 EH |
1016 | .filter(|t| { |
1017 | t.documented() | |
1018 | && (!t.is_bin() | |
1019 | || !targets.iter().any(|l| l.is_lib() && l.name() == t.name())) | |
1020 | }) | |
1021 | .collect() | |
2a82378a | 1022 | } |
00d325db | 1023 | CompileMode::Doctest | CompileMode::RunCustomBuild => panic!("Invalid mode {:?}", mode), |
2a82378a | 1024 | } |
2a82378a BW |
1025 | } |
1026 | ||
df9ca871 | 1027 | /// Returns a list of proposed targets based on command-line target selection flags. |
575d6e81 | 1028 | fn list_rule_targets<'a>( |
fbd34a69 | 1029 | packages: &[&'a Package], |
575d6e81 EH |
1030 | rule: &FilterRule, |
1031 | target_desc: &'static str, | |
1032 | is_expected_kind: fn(&Target) -> bool, | |
771fec3c | 1033 | mode: CompileMode, |
3a1cad6f | 1034 | ) -> CargoResult<Vec<Proposal<'a>>> { |
df9ca871 DW |
1035 | let mut proposals = Vec::new(); |
1036 | match rule { | |
771fec3c | 1037 | FilterRule::All => { |
df9ca871 | 1038 | proposals.extend(filter_targets(packages, is_expected_kind, false, mode)) |
771fec3c | 1039 | } |
df9ca871 | 1040 | FilterRule::Just(names) => { |
771fec3c | 1041 | for name in names { |
df9ca871 | 1042 | proposals.extend(find_named_targets( |
771fec3c EH |
1043 | packages, |
1044 | name, | |
1045 | target_desc, | |
1046 | is_expected_kind, | |
1047 | mode, | |
1048 | )?); | |
1049 | } | |
1050 | } | |
575d6e81 | 1051 | } |
df9ca871 | 1052 | Ok(proposals) |
575d6e81 | 1053 | } |
235712f5 | 1054 | |
f7c91ba6 | 1055 | /// Finds the targets for a specifically named target. |
771fec3c | 1056 | fn find_named_targets<'a>( |
fbd34a69 | 1057 | packages: &[&'a Package], |
575d6e81 EH |
1058 | target_name: &str, |
1059 | target_desc: &'static str, | |
1060 | is_expected_kind: fn(&Target) -> bool, | |
771fec3c | 1061 | mode: CompileMode, |
3a1cad6f | 1062 | ) -> CargoResult<Vec<Proposal<'a>>> { |
df9ca871 DW |
1063 | let filter = |t: &Target| t.name() == target_name && is_expected_kind(t); |
1064 | let proposals = filter_targets(packages, filter, true, mode); | |
1065 | if proposals.is_empty() { | |
7d7fe679 EH |
1066 | let targets = packages.iter().flat_map(|pkg| { |
1067 | pkg.targets() | |
1068 | .iter() | |
1069 | .filter(|target| is_expected_kind(target)) | |
1070 | }); | |
1071 | let suggestion = closest_msg(target_name, targets, |t| t.name()); | |
3a18c89a | 1072 | anyhow::bail!( |
7d7fe679 EH |
1073 | "no {} target named `{}`{}", |
1074 | target_desc, | |
1075 | target_name, | |
1076 | suggestion | |
1077 | ); | |
771fec3c | 1078 | } |
df9ca871 DW |
1079 | Ok(proposals) |
1080 | } | |
1081 | ||
1082 | fn filter_targets<'a>( | |
fbd34a69 | 1083 | packages: &[&'a Package], |
df9ca871 DW |
1084 | predicate: impl Fn(&Target) -> bool, |
1085 | requires_features: bool, | |
1086 | mode: CompileMode, | |
1087 | ) -> Vec<Proposal<'a>> { | |
1088 | let mut proposals = Vec::new(); | |
1089 | for pkg in packages { | |
1090 | for target in pkg.targets().iter().filter(|t| predicate(t)) { | |
1091 | proposals.push(Proposal { | |
1092 | pkg, | |
1093 | target, | |
1094 | requires_features, | |
1095 | mode, | |
1096 | }); | |
1097 | } | |
1098 | } | |
1099 | proposals | |
9e779198 | 1100 | } |