]> git.proxmox.com Git - cargo.git/blame - src/cargo/ops/cargo_compile.rs
fix: emit errors instead of warnings when glob patterns not found
[cargo.git] / src / cargo / ops / cargo_compile.rs
CommitLineData
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 25use std::collections::{BTreeSet, HashMap, HashSet};
9efa0d55 26use std::hash::{Hash, Hasher};
1ab19a5a 27use std::sync::Arc;
12af54e1 28
9efa0d55 29use crate::core::compiler::standard_lib;
1f14fa31 30use crate::core::compiler::unit_dependencies::build_unit_dependencies;
9efa0d55 31use crate::core::compiler::unit_graph::{self, UnitDep, UnitGraph};
e4544466 32use crate::core::compiler::{BuildConfig, BuildContext, Compilation, Context};
9efa0d55 33use crate::core::compiler::{CompileKind, CompileMode, CompileTarget, RustcTargetData, Unit};
e4544466 34use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
04ddd4d0 35use crate::core::profiles::{Profiles, UnitFor};
137642c4 36use crate::core::resolver::features::{self, FeaturesFor};
e0d64f94 37use crate::core::resolver::{HasDevUnits, Resolve, ResolveOpts};
19a95790 38use crate::core::{FeatureValue, Package, PackageSet, Shell, Summary, Target};
04ddd4d0
DW
39use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
40use crate::ops;
12392f6d 41use crate::ops::resolve::WorkspaceResolve;
04ddd4d0 42use crate::util::config::Config;
42696ae2 43use crate::util::restricted_names::is_glob_pattern;
9efa0d55 44use crate::util::{closest_msg, profile, CargoResult, StableHasher};
50f110a4 45
42696ae2
WL
46use anyhow::Context as _;
47
bacb6be3 48/// Contains information about how a package should be compiled.
e0bd9e23
EH
49///
50/// Note on distinction between `CompileOptions` and `BuildConfig`:
51/// `BuildConfig` contains values that need to be retained after
52/// `BuildContext` is created. The other fields are no longer necessary. Think
53/// of it as `CompileOptions` are high-level settings requested on the
54/// command-line, and `BuildConfig` are low-level settings for actually
55/// driving `rustc`.
876af7c1 56#[derive(Debug)]
e4918c45 57pub struct CompileOptions {
08025169
DO
58 /// Configuration information for a rustc build
59 pub build_config: BuildConfig,
9e779198 60 /// Extra features to build for the root package
bb643cca 61 pub features: Vec<String>,
1f8b3988
DA
62 /// Flag whether all available features should be built for the root package
63 pub all_features: bool,
9e779198 64 /// Flag if the default feature should be built for the root package
2b46d039 65 pub no_default_features: bool,
6b32c4d7 66 /// A set of packages to build.
bb643cca 67 pub spec: Packages,
9e779198
AC
68 /// Filter to apply to the root package to select which targets will be
69 /// built.
bb643cca 70 pub filter: CompileFilter,
1ef954ea 71 /// Extra arguments to be passed to rustdoc (single target only)
bb643cca 72 pub target_rustdoc_args: Option<Vec<String>>,
77bb01ec
SL
73 /// The specified target will be compiled with all the available arguments,
74 /// note that this only accounts for the *final* invocation of rustc
bb643cca 75 pub target_rustc_args: Option<Vec<String>>,
1ef954ea
EH
76 /// Extra arguments passed to all selected targets for rustdoc.
77 pub local_rustdoc_args: Option<Vec<String>>,
e7ee237c
LK
78 /// Whether the `--document-private-items` flags was specified and should
79 /// be forwarded to `rustdoc`.
80 pub rustdoc_document_private_items: bool,
9e779198
AC
81}
82
e4918c45
EH
83impl<'a> CompileOptions {
84 pub fn new(config: &Config, mode: CompileMode) -> CargoResult<CompileOptions> {
08025169 85 Ok(CompileOptions {
3fd28143 86 build_config: BuildConfig::new(config, None, &[], mode)?,
bb643cca 87 features: Vec::new(),
69caa63b
NC
88 all_features: false,
89 no_default_features: false,
bb643cca 90 spec: ops::Packages::Packages(Vec::new()),
1e682848
AC
91 filter: CompileFilter::Default {
92 required_features_filterable: false,
93 },
69caa63b
NC
94 target_rustdoc_args: None,
95 target_rustc_args: None,
1ef954ea 96 local_rustdoc_args: None,
e7ee237c 97 rustdoc_document_private_items: false,
08025169 98 })
69caa63b
NC
99 }
100}
101
bb643cca
AK
102#[derive(Clone, PartialEq, Eq, Debug)]
103pub enum Packages {
ba7911dd 104 Default,
62c04979 105 All,
bb643cca
AK
106 OptOut(Vec<String>),
107 Packages(Vec<String>),
62c04979
AR
108}
109
bb643cca 110impl Packages {
1e682848 111 pub fn from_flags(all: bool, exclude: Vec<String>, package: Vec<String>) -> CargoResult<Self> {
ba7911dd
SS
112 Ok(match (all, exclude.len(), package.len()) {
113 (false, 0, 0) => Packages::Default,
114 (false, 0, _) => Packages::Packages(package),
3a18c89a 115 (false, _, _) => anyhow::bail!("--exclude can only be used together with --workspace"),
ba7911dd
SS
116 (true, 0, _) => Packages::All,
117 (true, _, _) => Packages::OptOut(exclude),
118 })
7ffd1cfc 119 }
120
2361fb0f 121 /// Converts selected packages from a workspace to `PackageIdSpec`s.
b8b7faee 122 pub fn to_package_id_specs(&self, ws: &Workspace<'_>) -> CargoResult<Vec<PackageIdSpec>> {
d347908d 123 let specs = match self {
dae87a26
E
124 Packages::All => ws
125 .members()
1e682848
AC
126 .map(Package::package_id)
127 .map(PackageIdSpec::from_package_id)
128 .collect(),
45ce445c 129 Packages::OptOut(opt_out) => {
2361fb0f
WL
130 let (mut patterns, mut names) = opt_patterns_and_names(opt_out)?;
131 let specs = ws
45ce445c 132 .members()
2361fb0f
WL
133 .filter(|pkg| {
134 !names.remove(pkg.name().as_str()) && !match_patterns(pkg, &mut patterns)
135 })
45ce445c
DW
136 .map(Package::package_id)
137 .map(PackageIdSpec::from_package_id)
138 .collect();
2361fb0f
WL
139 let warn = |e| ws.config().shell().warn(e);
140 emit_package_not_found(ws, names, true).or_else(warn)?;
141 emit_pattern_not_found(ws, patterns, true).or_else(warn)?;
142 specs
f16efff1 143 }
d347908d 144 Packages::Packages(packages) if packages.is_empty() => {
037a879d
EH
145 vec![PackageIdSpec::from_package_id(ws.current()?.package_id())]
146 }
2361fb0f
WL
147 Packages::Packages(opt_in) => {
148 let (mut patterns, packages) = opt_patterns_and_names(opt_in)?;
149 let mut specs = packages
150 .iter()
151 .map(|p| PackageIdSpec::parse(p))
152 .collect::<CargoResult<Vec<_>>>()?;
153 if !patterns.is_empty() {
154 let matched_pkgs = ws
155 .members()
156 .filter(|pkg| match_patterns(pkg, &mut patterns))
157 .map(Package::package_id)
158 .map(PackageIdSpec::from_package_id);
159 specs.extend(matched_pkgs);
160 }
e4a1794b 161 emit_pattern_not_found(ws, patterns, false)?;
2361fb0f
WL
162 specs
163 }
dae87a26
E
164 Packages::Default => ws
165 .default_members()
1e682848
AC
166 .map(Package::package_id)
167 .map(PackageIdSpec::from_package_id)
168 .collect(),
6b32c4d7 169 };
68a681ff 170 if specs.is_empty() {
691f95de 171 if ws.is_virtual() {
3a18c89a 172 anyhow::bail!(
1e682848
AC
173 "manifest path `{}` contains no package: The manifest is virtual, \
174 and the workspace has no members.",
175 ws.root().display()
176 )
05b896ac 177 }
3a18c89a 178 anyhow::bail!("no packages to compile")
68a681ff 179 }
6b32c4d7
AK
180 Ok(specs)
181 }
fa54a794 182
2361fb0f 183 /// Gets a list of selected packages from a workspace.
b8b7faee 184 pub fn get_packages<'ws>(&self, ws: &'ws Workspace<'_>) -> CargoResult<Vec<&'ws Package>> {
fa54a794
EH
185 let packages: Vec<_> = match self {
186 Packages::Default => ws.default_members().collect(),
187 Packages::All => ws.members().collect(),
2361fb0f
WL
188 Packages::OptOut(opt_out) => {
189 let (mut patterns, mut names) = opt_patterns_and_names(opt_out)?;
190 let packages = ws
191 .members()
192 .filter(|pkg| {
193 !names.remove(pkg.name().as_str()) && !match_patterns(pkg, &mut patterns)
194 })
195 .collect();
196 emit_package_not_found(ws, names, true)?;
197 emit_pattern_not_found(ws, patterns, true)?;
198 packages
199 }
200 Packages::Packages(opt_in) => {
201 let (mut patterns, mut names) = opt_patterns_and_names(opt_in)?;
202 let packages = ws
203 .members()
204 .filter(|pkg| {
205 names.remove(pkg.name().as_str()) || match_patterns(pkg, &mut patterns)
206 })
207 .collect();
208 emit_package_not_found(ws, names, false)?;
209 emit_pattern_not_found(ws, patterns, false)?;
210 packages
211 }
fa54a794
EH
212 };
213 Ok(packages)
214 }
44c535ab
EH
215
216 /// Returns whether or not the user needs to pass a `-p` flag to target a
217 /// specific package in the workspace.
218 pub fn needs_spec_flag(&self, ws: &Workspace<'_>) -> bool {
219 match self {
220 Packages::Default => ws.default_members().count() > 1,
221 Packages::All => ws.members().count() > 1,
222 Packages::Packages(_) => true,
223 Packages::OptOut(_) => true,
224 }
225 }
6b32c4d7
AK
226}
227
aef99e0e
DW
228#[derive(Debug, PartialEq, Eq)]
229pub enum LibRule {
230 /// Include the library, fail if not present
231 True,
232 /// Include the library if present
233 Default,
234 /// Exclude the library
235 False,
236}
237
bb643cca
AK
238#[derive(Debug)]
239pub enum FilterRule {
2a82378a 240 All,
bb643cca 241 Just(Vec<String>),
2a82378a
BW
242}
243
876af7c1 244#[derive(Debug)]
bb643cca 245pub enum CompileFilter {
6344db05 246 Default {
25e50b58
JB
247 /// Flag whether targets can be safely skipped when required-features are not satisfied.
248 required_features_filterable: bool,
249 },
9e779198 250 Only {
b9497ab2 251 all_targets: bool,
aef99e0e 252 lib: LibRule,
bb643cca
AK
253 bins: FilterRule,
254 examples: FilterRule,
255 tests: FilterRule,
256 benches: FilterRule,
1e682848 257 },
a1980dc7
YKCL
258}
259
e4918c45 260pub fn compile<'a>(ws: &Workspace<'a>, options: &CompileOptions) -> CargoResult<Compilation<'a>> {
b8b7faee 261 let exec: Arc<dyn Executor> = Arc::new(DefaultExecutor);
689f412c 262 compile_with_exec(ws, options, &exec)
8c3b3605
NC
263}
264
4a64d05e 265/// Like `compile` but allows specifying a custom `Executor` that will be able to intercept build
d70f91ee 266/// calls and add custom logic. `compile` uses `DefaultExecutor` which just passes calls through.
1e682848
AC
267pub fn compile_with_exec<'a>(
268 ws: &Workspace<'a>,
e4918c45 269 options: &CompileOptions,
b8b7faee 270 exec: &Arc<dyn Executor>,
a340ba0b 271) -> CargoResult<Compilation<'a>> {
688a4fa6 272 ws.emit_warnings()?;
8a301589 273 compile_ws(ws, options, exec)
9fba127e
AC
274}
275
1e682848
AC
276pub fn compile_ws<'a>(
277 ws: &Workspace<'a>,
e4918c45 278 options: &CompileOptions,
b8b7faee 279 exec: &Arc<dyn Executor>,
a340ba0b 280) -> CargoResult<Compilation<'a>> {
e0bd9e23
EH
281 let interner = UnitInterner::new();
282 let bcx = create_bcx(ws, options, &interner)?;
283 if options.build_config.unit_graph {
284 unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph)?;
285 return Ok(Compilation::new(&bcx)?);
286 }
287
288 let _p = profile::start("compiling");
289 let cx = Context::new(&bcx)?;
290 cx.compile(exec)
291}
292
293pub fn create_bcx<'a, 'cfg>(
294 ws: &'a Workspace<'cfg>,
295 options: &'a CompileOptions,
296 interner: &'a UnitInterner,
297) -> CargoResult<BuildContext<'a, 'cfg>> {
1e682848 298 let CompileOptions {
08025169 299 ref build_config,
1e682848
AC
300 ref spec,
301 ref features,
302 all_features,
303 no_default_features,
1e682848
AC
304 ref filter,
305 ref target_rustdoc_args,
306 ref target_rustc_args,
1ef954ea 307 ref local_rustdoc_args,
e7ee237c 308 rustdoc_document_private_items,
1e682848 309 } = *options;
e4918c45 310 let config = ws.config();
3656bec8 311
d649c661 312 // Perform some pre-flight validation.
d17ccec2
HY
313 match build_config.mode {
314 CompileMode::Test
315 | CompileMode::Build
316 | CompileMode::Check { .. }
317 | CompileMode::Bench
318 | CompileMode::RunCustomBuild => {
319 if std::env::var("RUST_FLAGS").is_ok() {
320 config.shell().warn(
321 "Cargo does not read `RUST_FLAGS` environment variable. Did you mean `RUSTFLAGS`?",
322 )?;
323 }
324 }
325 CompileMode::Doc { .. } | CompileMode::Doctest => {
326 if std::env::var("RUSTDOC_FLAGS").is_ok() {
327 config.shell().warn(
328 "Cargo does not read `RUSTDOC_FLAGS` environment variable. Did you mean `RUSTDOCFLAGS`?"
329 )?;
330 }
331 }
332 }
d649c661 333 config.validate_term_config()?;
d17ccec2 334
3fd28143 335 let target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?;
47007d97 336
8947ed1f 337 let specs = spec.to_package_id_specs(ws)?;
e26ef017
EH
338 let dev_deps = ws.require_optional_deps() || filter.need_dev_deps(build_config.mode);
339 let opts = ResolveOpts::new(dev_deps, features, all_features, !no_default_features);
2b1dc3e5
AT
340 let has_dev_units = if filter.need_dev_deps(build_config.mode) {
341 HasDevUnits::Yes
342 } else {
343 HasDevUnits::No
e0d64f94 344 };
d728f386
EH
345 let resolve = ops::resolve_ws_with_opts(
346 ws,
347 &target_data,
3fd28143 348 &build_config.requested_kinds,
d728f386
EH
349 &opts,
350 &specs,
351 has_dev_units,
d267fac2 352 crate::core::resolver::features::ForceAllTargets::No,
d728f386 353 )?;
12392f6d
EH
354 let WorkspaceResolve {
355 mut pkg_set,
356 workspace_resolve,
357 targeted_resolve: resolve,
7caa1612 358 resolved_features,
12392f6d 359 } = resolve;
1f14fa31 360
7caa1612 361 let std_resolve_features = if let Some(crates) = &config.cli_unstable().build_std {
3afb5d7d
EH
362 if build_config.build_plan {
363 config
364 .shell()
365 .warn("-Zbuild-std does not currently fully support --build-plan")?;
366 }
3fd28143 367 if build_config.requested_kinds[0].is_host() {
1f14fa31
EH
368 // TODO: This should eventually be fixed. Unfortunately it is not
369 // easy to get the host triple in BuildConfig. Consider changing
370 // requested_target to an enum, or some other approach.
3a18c89a 371 anyhow::bail!("-Zbuild-std requires --target");
1f14fa31 372 }
98b6f816 373 let (std_package_set, std_resolve, std_features) =
3fd28143 374 standard_lib::resolve_std(ws, &target_data, &build_config.requested_kinds, crates)?;
12392f6d 375 pkg_set.add_set(std_package_set);
7caa1612 376 Some((std_resolve, std_features))
1f14fa31
EH
377 } else {
378 None
379 };
9c296792 380
1f14fa31
EH
381 // Find the packages in the resolver that the user wants to build (those
382 // passed in with `-p` or the defaults from the workspace), and convert
96a39371
EH
383 // Vec<PackageIdSpec> to a Vec<PackageId>.
384 let to_build_ids = resolve.specs_to_ids(&specs)?;
1f14fa31
EH
385 // Now get the `Package` for each `PackageId`. This may trigger a download
386 // if the user specified `-p` for a dependency that is not downloaded.
387 // Dependencies will be downloaded during build_unit_dependencies.
12392f6d 388 let mut to_builds = pkg_set.get_many(to_build_ids)?;
0ea0c8ba 389
7b1a0dc5
AC
390 // The ordering here affects some error messages coming out of cargo, so
391 // let's be test and CLI friendly by always printing in the same order if
392 // there's an error.
393 to_builds.sort_by_key(|p| p.package_id());
394
395 for pkg in to_builds.iter() {
1f14fa31 396 pkg.manifest().print_teapot(config);
786848a0
EH
397
398 if build_config.mode.is_any_test()
399 && !ws.is_member(pkg)
400 && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
401 {
3a18c89a 402 anyhow::bail!(
786848a0
EH
403 "package `{}` cannot be tested because it requires dev-dependencies \
404 and is not a member of the workspace",
405 pkg.name()
406 );
407 }
7b1a0dc5 408 }
f0647ea2 409
575d6e81
EH
410 let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
411 (&Some(ref args), _) => (Some(args.clone()), "rustc"),
412 (_, &Some(ref args)) => (Some(args.clone()), "rustdoc"),
413 _ => (None, ""),
f0647ea2 414 };
b3ade7c7 415
575d6e81
EH
416 if extra_args.is_some() && to_builds.len() != 1 {
417 panic!(
418 "`{}` should not accept multiple `-p` flags",
419 extra_args_name
420 );
421 }
422
e0bd9e23
EH
423 let profiles = Profiles::new(
424 ws.profiles(),
425 config,
426 build_config.requested_profile,
427 ws.features(),
428 )?;
12392f6d 429 profiles.validate_packages(
77ee608d 430 ws.profiles(),
12392f6d
EH
431 &mut config.shell(),
432 workspace_resolve.as_ref().unwrap_or(&resolve),
433 )?;
a0a880c3 434
9efa0d55
EH
435 // If `--target` has not been specified, then the unit graph is built
436 // assuming `--target $HOST` was specified. See
437 // `rebuild_unit_graph_shared` for more on why this is done.
438 let explicit_host_kind = CompileKind::Target(CompileTarget::new(&target_data.rustc.host)?);
439 let explicit_host_kinds: Vec<_> = build_config
440 .requested_kinds
441 .iter()
442 .map(|kind| match kind {
443 CompileKind::Host => explicit_host_kind,
444 CompileKind::Target(t) => CompileKind::Target(*t),
445 })
446 .collect();
447
448 let mut units = generate_targets(
73660740 449 ws,
575d6e81
EH
450 &to_builds,
451 filter,
9efa0d55 452 &explicit_host_kinds,
e0bd9e23 453 build_config.mode,
12392f6d 454 &resolve,
19a95790 455 &workspace_resolve,
7caa1612 456 &resolved_features,
e0bd9e23
EH
457 &pkg_set,
458 &profiles,
686ccfa4 459 interner,
575d6e81
EH
460 )?;
461
1f14fa31 462 let std_roots = if let Some(crates) = &config.cli_unstable().build_std {
3ba9de85
EH
463 // Only build libtest if it looks like it is needed.
464 let mut crates = crates.clone();
465 if !crates.iter().any(|c| c == "test")
466 && units
467 .iter()
468 .any(|unit| unit.mode.is_rustc_test() && unit.target.harness())
469 {
18a89732
PO
470 // Only build libtest when libstd is built (libtest depends on libstd)
471 if crates.iter().any(|c| c == "std") {
472 crates.push("test".to_string());
473 }
3ba9de85 474 }
7caa1612 475 let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
593a02f2 476 standard_lib::generate_std_roots(
593a02f2 477 &crates,
7caa1612
EH
478 std_resolve,
479 std_features,
9efa0d55 480 &explicit_host_kinds,
e0bd9e23 481 &pkg_set,
686ccfa4 482 interner,
e0bd9e23 483 &profiles,
593a02f2 484 )?
1f14fa31 485 } else {
3fd28143 486 Default::default()
1f14fa31
EH
487 };
488
9efa0d55
EH
489 let mut unit_graph = build_unit_dependencies(
490 ws,
491 &pkg_set,
492 &resolve,
493 &resolved_features,
494 std_resolve_features.as_ref(),
495 &units,
496 &std_roots,
497 build_config.mode,
498 &target_data,
499 &profiles,
500 interner,
501 )?;
502
503 if build_config
504 .requested_kinds
505 .iter()
506 .any(CompileKind::is_host)
507 {
508 // Rebuild the unit graph, replacing the explicit host targets with
509 // CompileKind::Host, merging any dependencies shared with build
510 // dependencies.
511 let new_graph = rebuild_unit_graph_shared(interner, unit_graph, &units, explicit_host_kind);
512 // This would be nicer with destructuring assignment.
513 units = new_graph.0;
514 unit_graph = new_graph.1;
515 }
516
e0bd9e23 517 let mut extra_compiler_args = HashMap::new();
575d6e81
EH
518 if let Some(args) = extra_args {
519 if units.len() != 1 {
3a18c89a 520 anyhow::bail!(
575d6e81 521 "extra arguments to `{}` can only be passed to one \
f7c91ba6
AR
522 target, consider filtering\nthe package by passing, \
523 e.g., `--lib` or `--bin NAME` to specify a single target",
575d6e81
EH
524 extra_args_name
525 );
8a8ea1e8 526 }
45d49579 527 extra_compiler_args.insert(units[0].clone(), args);
1ef954ea 528 }
e7ee237c
LK
529 for unit in &units {
530 if unit.mode.is_doc() || unit.mode.is_doc_test() {
531 let mut extra_args = local_rustdoc_args.clone();
532
533 // Add `--document-private-items` rustdoc flag if requested or if
534 // the target is a binary. Binary crates get their private items
535 // documented by default.
536 if rustdoc_document_private_items || unit.target.is_bin() {
a6a395c6 537 let mut args = extra_args.take().unwrap_or_else(|| vec![]);
e7ee237c
LK
538 args.push("--document-private-items".into());
539 extra_args = Some(args);
540 }
541
1d1b3447
JN
542 if let Some(args) = extra_args {
543 extra_compiler_args
544 .entry(unit.clone())
545 .or_default()
546 .extend(args);
1ef954ea
EH
547 }
548 }
b3ade7c7 549 }
575d6e81 550
e0bd9e23
EH
551 let bcx = BuildContext::new(
552 ws,
553 pkg_set,
554 build_config,
555 profiles,
556 extra_compiler_args,
557 target_data,
558 units,
559 unit_graph,
560 )?;
8b840393 561
e0bd9e23 562 Ok(bcx)
62bff631 563}
51c9cf0f 564
bb643cca
AK
565impl FilterRule {
566 pub fn new(targets: Vec<String>, all: bool) -> FilterRule {
2a82378a
BW
567 if all {
568 FilterRule::All
569 } else {
570 FilterRule::Just(targets)
571 }
572 }
573
12307fc2
DW
574 pub fn none() -> FilterRule {
575 FilterRule::Just(Vec::new())
576 }
577
2a82378a
BW
578 fn matches(&self, target: &Target) -> bool {
579 match *self {
580 FilterRule::All => true,
1e682848 581 FilterRule::Just(ref targets) => targets.iter().any(|x| *x == target.name()),
2a82378a
BW
582 }
583 }
584
585 fn is_specific(&self) -> bool {
586 match *self {
587 FilterRule::All => true,
bb643cca 588 FilterRule::Just(ref targets) => !targets.is_empty(),
2a82378a
BW
589 }
590 }
591
592 pub fn try_collect(&self) -> Option<Vec<String>> {
593 match *self {
594 FilterRule::All => None,
bb643cca 595 FilterRule::Just(ref targets) => Some(targets.clone()),
2a82378a
BW
596 }
597 }
42696ae2
WL
598
599 pub(crate) fn contains_glob_patterns(&self) -> bool {
600 match self {
601 FilterRule::All => false,
602 FilterRule::Just(targets) => targets.iter().any(is_glob_pattern),
603 }
604 }
2a82378a
BW
605}
606
bb643cca 607impl CompileFilter {
12307fc2
DW
608 /// Construct a CompileFilter from raw command line arguments.
609 pub fn from_raw_arguments(
1e682848
AC
610 lib_only: bool,
611 bins: Vec<String>,
612 all_bins: bool,
613 tsts: Vec<String>,
614 all_tsts: bool,
615 exms: Vec<String>,
616 all_exms: bool,
617 bens: Vec<String>,
618 all_bens: bool,
619 all_targets: bool,
620 ) -> CompileFilter {
5a59b809
EH
621 if all_targets {
622 return CompileFilter::new_all_targets();
623 }
f16efff1
AC
624 let rule_lib = if lib_only {
625 LibRule::True
626 } else {
627 LibRule::False
628 };
2a82378a
BW
629 let rule_bins = FilterRule::new(bins, all_bins);
630 let rule_tsts = FilterRule::new(tsts, all_tsts);
631 let rule_exms = FilterRule::new(exms, all_exms);
632 let rule_bens = FilterRule::new(bens, all_bens);
633
5a59b809 634 CompileFilter::new(rule_lib, rule_bins, rule_tsts, rule_exms, rule_bens)
12307fc2
DW
635 }
636
637 /// Construct a CompileFilter from underlying primitives.
638 pub fn new(
aef99e0e 639 rule_lib: LibRule,
12307fc2
DW
640 rule_bins: FilterRule,
641 rule_tsts: FilterRule,
642 rule_exms: FilterRule,
643 rule_bens: FilterRule,
644 ) -> CompileFilter {
aef99e0e 645 if rule_lib == LibRule::True
dae87a26
E
646 || rule_bins.is_specific()
647 || rule_tsts.is_specific()
648 || rule_exms.is_specific()
649 || rule_bens.is_specific()
1e682848 650 {
7a1d8d94 651 CompileFilter::Only {
b9497ab2 652 all_targets: false,
aef99e0e 653 lib: rule_lib,
1e682848
AC
654 bins: rule_bins,
655 examples: rule_exms,
656 benches: rule_bens,
2a82378a 657 tests: rule_tsts,
7a1d8d94
AC
658 }
659 } else {
6344db05 660 CompileFilter::Default {
25e50b58
JB
661 required_features_filterable: true,
662 }
7a1d8d94
AC
663 }
664 }
665
5a59b809
EH
666 pub fn new_all_targets() -> CompileFilter {
667 CompileFilter::Only {
668 all_targets: true,
669 lib: LibRule::Default,
670 bins: FilterRule::All,
671 examples: FilterRule::All,
672 benches: FilterRule::All,
673 tests: FilterRule::All,
674 }
675 }
676
89d5161d
XL
677 pub fn need_dev_deps(&self, mode: CompileMode) -> bool {
678 match mode {
679 CompileMode::Test | CompileMode::Doctest | CompileMode::Bench => true,
b112255f
EH
680 CompileMode::Check { test: true } => true,
681 CompileMode::Build | CompileMode::Doc { .. } | CompileMode::Check { test: false } => {
682 match *self {
683 CompileFilter::Default { .. } => false,
684 CompileFilter::Only {
685 ref examples,
686 ref tests,
687 ref benches,
688 ..
689 } => examples.is_specific() || tests.is_specific() || benches.is_specific(),
690 }
691 }
575d6e81 692 CompileMode::RunCustomBuild => panic!("Invalid mode"),
7de30dd2
XL
693 }
694 }
695
ce26ddfd 696 // this selects targets for "cargo run". for logic to select targets for
771fec3c 697 // other subcommands, see generate_targets and filter_default_targets
ce26ddfd 698 pub fn target_run(&self, target: &Target) -> bool {
9e779198 699 match *self {
70170d1b 700 CompileFilter::Default { .. } => true,
1e682848 701 CompileFilter::Only {
aef99e0e 702 ref lib,
1e682848
AC
703 ref bins,
704 ref examples,
705 ref tests,
706 ref benches,
707 ..
708 } => {
2a82378a 709 let rule = match *target.kind() {
9e779198
AC
710 TargetKind::Bin => bins,
711 TargetKind::Test => tests,
712 TargetKind::Bench => benches,
1e682848 713 TargetKind::ExampleBin | TargetKind::ExampleLib(..) => examples,
f16efff1
AC
714 TargetKind::Lib(..) => {
715 return match *lib {
716 LibRule::True => true,
717 LibRule::Default => true,
718 LibRule::False => false,
719 };
720 }
9e779198
AC
721 TargetKind::CustomBuild => return false,
722 };
2a82378a 723 rule.matches(target)
9e779198
AC
724 }
725 }
726 }
c98b8c4c
BW
727
728 pub fn is_specific(&self) -> bool {
729 match *self {
6344db05 730 CompileFilter::Default { .. } => false,
c98b8c4c
BW
731 CompileFilter::Only { .. } => true,
732 }
733 }
42696ae2
WL
734
735 pub(crate) fn contains_glob_patterns(&self) -> bool {
736 match self {
737 CompileFilter::Default { .. } => false,
738 CompileFilter::Only {
739 bins,
740 examples,
741 tests,
742 benches,
743 ..
744 } => {
745 bins.contains_glob_patterns()
746 || examples.contains_glob_patterns()
747 || tests.contains_glob_patterns()
748 || benches.contains_glob_patterns()
749 }
750 }
751 }
9e779198
AC
752}
753
3a1cad6f
EH
754/// A proposed target.
755///
f7c91ba6 756/// Proposed targets are later filtered into actual `Unit`s based on whether or
3a1cad6f
EH
757/// not the target requires its features to be present.
758#[derive(Debug)]
759struct Proposal<'a> {
fbd34a69 760 pkg: &'a Package,
45d49579 761 target: &'a Target,
3a1cad6f
EH
762 /// Indicates whether or not all required features *must* be present. If
763 /// false, and the features are not available, then it will be silently
764 /// skipped. Generally, targets specified by name (`--bin foo`) are
765 /// required, all others can be silently skipped if features are missing.
766 requires_features: bool,
767 mode: CompileMode,
768}
769
575d6e81 770/// Generates all the base targets for the packages the user has requested to
f7c91ba6 771/// compile. Dependencies for these targets are computed later in `unit_dependencies`.
c85ed044 772fn generate_targets(
b8b7faee 773 ws: &Workspace<'_>,
fbd34a69 774 packages: &[&Package],
575d6e81 775 filter: &CompileFilter,
3fd28143 776 requested_kinds: &[CompileKind],
e0bd9e23
EH
777 mode: CompileMode,
778 resolve: &Resolve,
19a95790 779 workspace_resolve: &Option<Resolve>,
7caa1612 780 resolved_features: &features::ResolvedFeatures,
e0bd9e23
EH
781 package_set: &PackageSet<'_>,
782 profiles: &Profiles,
c85ed044
AC
783 interner: &UnitInterner,
784) -> CargoResult<Vec<Unit>> {
e0bd9e23 785 let config = ws.config();
3fd28143
AC
786 // Helper for creating a list of `Unit` structures
787 let new_unit =
788 |units: &mut HashSet<Unit>, pkg: &Package, target: &Target, target_mode: CompileMode| {
789 let unit_for = if target_mode.is_any_test() {
790 // NOTE: the `UnitFor` here is subtle. If you have a profile
791 // with `panic` set, the `panic` flag is cleared for
792 // tests/benchmarks and their dependencies. If this
793 // was `normal`, then the lib would get compiled three
794 // times (once with panic, once without, and once with
795 // `--test`).
796 //
797 // This would cause a problem for doc tests, which would fail
798 // because `rustdoc` would attempt to link with both libraries
799 // at the same time. Also, it's probably not important (or
800 // even desirable?) for rustdoc to link with a lib with
801 // `panic` set.
802 //
803 // As a consequence, Examples and Binaries get compiled
804 // without `panic` set. This probably isn't a bad deal.
805 //
806 // Forcing the lib to be compiled three times during `cargo
807 // test` is probably also not desirable.
808 UnitFor::new_test(config)
809 } else if target.for_host() {
810 // Proc macro / plugin should not have `panic` set.
811 UnitFor::new_compiler()
812 } else {
813 UnitFor::new_normal()
814 };
815 // Custom build units are added in `build_unit_dependencies`.
816 assert!(!target.is_custom_build());
817 let target_mode = match target_mode {
818 CompileMode::Test => {
819 if target.is_example() && !filter.is_specific() && !target.tested() {
820 // Examples are included as regular binaries to verify
821 // that they compile.
822 CompileMode::Build
823 } else {
824 CompileMode::Test
825 }
2a82378a 826 }
3fd28143
AC
827 CompileMode::Build => match *target.kind() {
828 TargetKind::Test => CompileMode::Test,
829 TargetKind::Bench => CompileMode::Bench,
830 _ => CompileMode::Build,
831 },
832 // `CompileMode::Bench` is only used to inform `filter_default_targets`
833 // which command is being used (`cargo bench`). Afterwards, tests
834 // and benches are treated identically. Switching the mode allows
835 // de-duplication of units that are essentially identical. For
836 // example, `cargo build --all-targets --release` creates the units
837 // (lib profile:bench, mode:test) and (lib profile:bench, mode:bench)
838 // and since these are the same, we want them to be de-duplicated in
839 // `unit_dependencies`.
840 CompileMode::Bench => CompileMode::Test,
841 _ => target_mode,
842 };
843
844 let is_local = pkg.package_id().source_id().is_path();
845 let profile = profiles.get_profile(
846 pkg.package_id(),
847 ws.is_member(pkg),
848 is_local,
849 unit_for,
850 target_mode,
851 );
852
853 // No need to worry about build-dependencies, roots are never build dependencies.
854 let features_for = FeaturesFor::from_for_host(target.proc_macro());
855 let features = resolved_features.activated_features(pkg.package_id(), features_for);
856
857 for kind in requested_kinds {
858 let unit = interner.intern(
859 pkg,
860 target,
861 profile,
862 kind.for_target(target),
863 target_mode,
864 features.clone(),
865 /*is_std*/ false,
9efa0d55 866 /*dep_hash*/ 0,
3fd28143
AC
867 );
868 units.insert(unit);
2a82378a 869 }
575d6e81
EH
870 };
871
3a1cad6f 872 // Create a list of proposed targets.
b8b7faee 873 let mut proposals: Vec<Proposal<'_>> = Vec::new();
771fec3c
EH
874
875 match *filter {
876 CompileFilter::Default {
877 required_features_filterable,
878 } => {
879 for pkg in packages {
e0bd9e23 880 let default = filter_default_targets(pkg.targets(), mode);
3a1cad6f
EH
881 proposals.extend(default.into_iter().map(|target| Proposal {
882 pkg,
883 target,
884 requires_features: !required_features_filterable,
e0bd9e23 885 mode,
771fec3c 886 }));
e0bd9e23 887 if mode == CompileMode::Test {
dd6c6102
EH
888 if let Some(t) = pkg
889 .targets()
890 .iter()
891 .find(|t| t.is_lib() && t.doctested() && t.doctestable())
892 {
3a1cad6f
EH
893 proposals.push(Proposal {
894 pkg,
895 target: t,
896 requires_features: false,
897 mode: CompileMode::Doctest,
898 });
575d6e81 899 }
2a82378a
BW
900 }
901 }
771fec3c
EH
902 }
903 CompileFilter::Only {
904 all_targets,
aef99e0e 905 ref lib,
771fec3c
EH
906 ref bins,
907 ref examples,
908 ref tests,
909 ref benches,
910 } => {
aef99e0e 911 if *lib != LibRule::False {
771fec3c 912 let mut libs = Vec::new();
e0bd9e23 913 for proposal in filter_targets(packages, Target::is_lib, false, mode) {
df9ca871 914 let Proposal { target, pkg, .. } = proposal;
e0bd9e23 915 if mode.is_doc_test() && !target.doctestable() {
7a06be14
EH
916 let types = target.rustc_crate_types();
917 let types_str: Vec<&str> = types.iter().map(|t| t.as_str()).collect();
df9ca871
DW
918 ws.config().shell().warn(format!(
919 "doc tests are not supported for crate type(s) `{}` in package `{}`",
7a06be14 920 types_str.join(", "),
df9ca871
DW
921 pkg.name()
922 ))?;
923 } else {
924 libs.push(proposal)
575d6e81
EH
925 }
926 }
aef99e0e 927 if !all_targets && libs.is_empty() && *lib == LibRule::True {
771fec3c
EH
928 let names = packages.iter().map(|pkg| pkg.name()).collect::<Vec<_>>();
929 if names.len() == 1 {
3a18c89a 930 anyhow::bail!("no library targets found in package `{}`", names[0]);
771fec3c 931 } else {
3a18c89a 932 anyhow::bail!("no library targets found in packages: {}", names.join(", "));
771fec3c
EH
933 }
934 }
935 proposals.extend(libs);
575d6e81 936 }
3a1cad6f 937
f7c91ba6 938 // If `--tests` was specified, add all targets that would be
771fec3c 939 // generated by `cargo test`.
df9ca871 940 let test_filter = match tests {
771fec3c
EH
941 FilterRule::All => Target::tested,
942 FilterRule::Just(_) => Target::is_test,
943 };
e0bd9e23 944 let test_mode = match mode {
771fec3c
EH
945 CompileMode::Build => CompileMode::Test,
946 CompileMode::Check { .. } => CompileMode::Check { test: true },
e0bd9e23 947 _ => mode,
771fec3c 948 };
f7c91ba6 949 // If `--benches` was specified, add all targets that would be
771fec3c 950 // generated by `cargo bench`.
df9ca871 951 let bench_filter = match benches {
771fec3c
EH
952 FilterRule::All => Target::benched,
953 FilterRule::Just(_) => Target::is_bench,
954 };
e0bd9e23 955 let bench_mode = match mode {
771fec3c
EH
956 CompileMode::Build => CompileMode::Bench,
957 CompileMode::Check { .. } => CompileMode::Check { test: true },
e0bd9e23 958 _ => mode,
771fec3c
EH
959 };
960
961 proposals.extend(list_rule_targets(
962 packages,
963 bins,
964 "bin",
965 Target::is_bin,
e0bd9e23 966 mode,
771fec3c
EH
967 )?);
968 proposals.extend(list_rule_targets(
969 packages,
970 examples,
971 "example",
972 Target::is_example,
e0bd9e23 973 mode,
771fec3c
EH
974 )?);
975 proposals.extend(list_rule_targets(
976 packages,
977 tests,
978 "test",
979 test_filter,
980 test_mode,
981 )?);
982 proposals.extend(list_rule_targets(
983 packages,
984 benches,
985 "bench",
986 bench_filter,
987 bench_mode,
988 )?);
2a82378a 989 }
771fec3c 990 }
2a82378a 991
771fec3c
EH
992 // Only include targets that are libraries or have all required
993 // features available.
7caa1612
EH
994 //
995 // `features_map` is a map of &Package -> enabled_features
996 // It is computed by the set of enabled features for the package plus
997 // every enabled feature of every enabled dependency.
771fec3c 998 let mut features_map = HashMap::new();
3a1cad6f 999 let mut units = HashSet::new();
dae87a26
E
1000 for Proposal {
1001 pkg,
1002 target,
1003 requires_features,
1004 mode,
1005 } in proposals
1006 {
771fec3c
EH
1007 let unavailable_features = match target.required_features() {
1008 Some(rf) => {
19a95790
AT
1009 warn_on_missing_features(
1010 workspace_resolve,
1011 rf,
1012 pkg.summary(),
1013 &mut config.shell(),
1014 )?;
1015
7caa1612 1016 let features = features_map.entry(pkg).or_insert_with(|| {
e0bd9e23 1017 resolve_all_features(resolve, resolved_features, package_set, pkg.package_id())
7caa1612 1018 });
771fec3c 1019 rf.iter().filter(|f| !features.contains(*f)).collect()
2a82378a 1020 }
771fec3c
EH
1021 None => Vec::new(),
1022 };
1023 if target.is_lib() || unavailable_features.is_empty() {
3fd28143 1024 new_unit(&mut units, pkg, target, mode);
3a1cad6f 1025 } else if requires_features {
771fec3c
EH
1026 let required_features = target.required_features().unwrap();
1027 let quoted_required_features: Vec<String> = required_features
1028 .iter()
1029 .map(|s| format!("`{}`", s))
1030 .collect();
3a18c89a 1031 anyhow::bail!(
771fec3c 1032 "target `{}` in package `{}` requires the features: {}\n\
f7c91ba6 1033 Consider enabling them by passing, e.g., `--features=\"{}\"`",
771fec3c
EH
1034 target.name(),
1035 pkg.name(),
1036 quoted_required_features.join(", "),
1037 required_features.join(" ")
1038 );
2a82378a 1039 }
771fec3c 1040 // else, silently skip target.
2a82378a 1041 }
3a1cad6f 1042 Ok(units.into_iter().collect())
2a82378a
BW
1043}
1044
19a95790
AT
1045fn warn_on_missing_features(
1046 resolve: &Option<Resolve>,
1047 required_features: &[String],
1048 summary: &Summary,
1049 shell: &mut Shell,
1050) -> CargoResult<()> {
1051 let resolve = match resolve {
1052 None => return Ok(()),
1053 Some(resolve) => resolve,
1054 };
1055
1056 for feature in required_features {
1057 match FeatureValue::new(feature.into(), summary) {
1058 // No need to do anything here, since the feature must exist to be parsed as such
1059 FeatureValue::Feature(_) => {}
1060 // Possibly mislabeled feature that was not found
1061 FeatureValue::Crate(krate) => {
1062 if !summary
1063 .dependencies()
1064 .iter()
1065 .any(|dep| dep.name_in_toml() == krate && dep.is_optional())
1066 {
1067 shell.warn(format!(
1068 "feature `{}` is not present in [features] section.",
1069 krate
1070 ))?;
1071 }
1072 }
1073 // Handling of dependent_crate/dependent_crate_feature syntax
1074 FeatureValue::CrateFeature(krate, feature) => {
1075 match resolve
1076 .deps(summary.package_id())
1077 .find(|(_dep_id, deps)| deps.iter().any(|dep| dep.name_in_toml() == krate))
1078 {
1079 Some((dep_id, _deps)) => {
1080 let dep_summary = resolve.summary(dep_id);
1081 if !dep_summary.features().contains_key(&feature)
1082 && !dep_summary
1083 .dependencies()
1084 .iter()
1085 .any(|dep| dep.name_in_toml() == feature && dep.is_optional())
1086 {
1087 shell.warn(format!(
1088 "feature `{}` does not exist in package `{}`.",
1089 feature, dep_id
1090 ))?;
1091 }
1092 }
1093 None => {
1094 shell.warn(format!(
1095 "dependency `{}` specified in required-features as `{}/{}` \
1096 does not exist.",
1097 krate, krate, feature
1098 ))?;
1099 }
1100 }
1101 }
1102 }
1103 }
1104 Ok(())
1105}
1106
3f068230
EH
1107/// Gets all of the features enabled for a package, plus its dependencies'
1108/// features.
1109///
1110/// Dependencies are added as `dep_name/feat_name` because `required-features`
1111/// wants to support that syntax.
8b17895b 1112pub fn resolve_all_features(
575d6e81 1113 resolve_with_overrides: &Resolve,
7caa1612 1114 resolved_features: &features::ResolvedFeatures,
944f5049 1115 package_set: &PackageSet<'_>,
dae87a26 1116 package_id: PackageId,
575d6e81 1117) -> HashSet<String> {
7caa1612 1118 let mut features: HashSet<String> = resolved_features
137642c4 1119 .activated_features(package_id, FeaturesFor::NormalOrDev)
7caa1612
EH
1120 .iter()
1121 .map(|s| s.to_string())
1122 .collect();
575d6e81
EH
1123
1124 // Include features enabled for use by dependencies so targets can also use them with the
1125 // required-features field when deciding whether to be built or skipped.
cafec114 1126 for (dep_id, deps) in resolve_with_overrides.deps(package_id) {
944f5049
EH
1127 let is_proc_macro = package_set
1128 .get_one(dep_id)
1129 .expect("packages downloaded")
1130 .proc_macro();
8973b959 1131 for dep in deps {
96a39371 1132 let features_for = FeaturesFor::from_for_host(is_proc_macro || dep.is_build());
e94facea
R
1133 for feature in resolved_features
1134 .activated_features_unverified(dep_id, features_for)
1135 .unwrap_or_default()
1136 {
887ee6cc 1137 features.insert(format!("{}/{}", dep.name_in_toml(), feature));
cafec114 1138 }
575d6e81
EH
1139 }
1140 }
1141
1142 features
1143}
1144
1145/// Given a list of all targets for a package, filters out only the targets
1146/// that are automatically included when the user doesn't specify any targets.
45d49579 1147fn filter_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Target> {
575d6e81
EH
1148 match mode {
1149 CompileMode::Bench => targets.iter().filter(|t| t.benched()).collect(),
a4947c2b
EH
1150 CompileMode::Test => targets
1151 .iter()
1152 .filter(|t| t.tested() || t.is_example())
1153 .collect(),
575d6e81
EH
1154 CompileMode::Build | CompileMode::Check { .. } => targets
1155 .iter()
1156 .filter(|t| t.is_bin() || t.is_lib())
1157 .collect(),
1158 CompileMode::Doc { .. } => {
1159 // `doc` does lib and bins (bin with same name as lib is skipped).
1160 targets
1e682848 1161 .iter()
575d6e81
EH
1162 .filter(|t| {
1163 t.documented()
1164 && (!t.is_bin()
1165 || !targets.iter().any(|l| l.is_lib() && l.name() == t.name()))
1166 })
1167 .collect()
2a82378a 1168 }
00d325db 1169 CompileMode::Doctest | CompileMode::RunCustomBuild => panic!("Invalid mode {:?}", mode),
2a82378a 1170 }
2a82378a
BW
1171}
1172
df9ca871 1173/// Returns a list of proposed targets based on command-line target selection flags.
575d6e81 1174fn list_rule_targets<'a>(
fbd34a69 1175 packages: &[&'a Package],
575d6e81
EH
1176 rule: &FilterRule,
1177 target_desc: &'static str,
1178 is_expected_kind: fn(&Target) -> bool,
771fec3c 1179 mode: CompileMode,
3a1cad6f 1180) -> CargoResult<Vec<Proposal<'a>>> {
df9ca871
DW
1181 let mut proposals = Vec::new();
1182 match rule {
771fec3c 1183 FilterRule::All => {
df9ca871 1184 proposals.extend(filter_targets(packages, is_expected_kind, false, mode))
771fec3c 1185 }
df9ca871 1186 FilterRule::Just(names) => {
771fec3c 1187 for name in names {
df9ca871 1188 proposals.extend(find_named_targets(
771fec3c
EH
1189 packages,
1190 name,
1191 target_desc,
1192 is_expected_kind,
1193 mode,
1194 )?);
1195 }
1196 }
575d6e81 1197 }
df9ca871 1198 Ok(proposals)
575d6e81 1199}
235712f5 1200
f7c91ba6 1201/// Finds the targets for a specifically named target.
771fec3c 1202fn find_named_targets<'a>(
fbd34a69 1203 packages: &[&'a Package],
575d6e81
EH
1204 target_name: &str,
1205 target_desc: &'static str,
1206 is_expected_kind: fn(&Target) -> bool,
771fec3c 1207 mode: CompileMode,
3a1cad6f 1208) -> CargoResult<Vec<Proposal<'a>>> {
42696ae2
WL
1209 let is_glob = is_glob_pattern(target_name);
1210 let proposals = if is_glob {
1211 let pattern = build_glob(target_name)?;
1212 let filter = |t: &Target| is_expected_kind(t) && pattern.matches(t.name());
1213 filter_targets(packages, filter, true, mode)
1214 } else {
1215 let filter = |t: &Target| t.name() == target_name && is_expected_kind(t);
1216 filter_targets(packages, filter, true, mode)
1217 };
1218
df9ca871 1219 if proposals.is_empty() {
7d7fe679
EH
1220 let targets = packages.iter().flat_map(|pkg| {
1221 pkg.targets()
1222 .iter()
1223 .filter(|target| is_expected_kind(target))
1224 });
1225 let suggestion = closest_msg(target_name, targets, |t| t.name());
3a18c89a 1226 anyhow::bail!(
42696ae2 1227 "no {} target {} `{}`{}",
7d7fe679 1228 target_desc,
42696ae2 1229 if is_glob { "matches pattern" } else { "named" },
7d7fe679
EH
1230 target_name,
1231 suggestion
1232 );
771fec3c 1233 }
df9ca871
DW
1234 Ok(proposals)
1235}
1236
1237fn filter_targets<'a>(
fbd34a69 1238 packages: &[&'a Package],
df9ca871
DW
1239 predicate: impl Fn(&Target) -> bool,
1240 requires_features: bool,
1241 mode: CompileMode,
1242) -> Vec<Proposal<'a>> {
1243 let mut proposals = Vec::new();
1244 for pkg in packages {
1245 for target in pkg.targets().iter().filter(|t| predicate(t)) {
1246 proposals.push(Proposal {
1247 pkg,
1248 target,
1249 requires_features,
1250 mode,
1251 });
1252 }
1253 }
1254 proposals
9e779198 1255}
9efa0d55
EH
1256
1257/// This is used to rebuild the unit graph, sharing host dependencies if possible.
1258///
1259/// This will translate any unit's `CompileKind::Target(host)` to
1260/// `CompileKind::Host` if the kind is equal to `to_host`. This also handles
1261/// generating the unit `dep_hash`, and merging shared units if possible.
1262///
1263/// This is necessary because if normal dependencies used `CompileKind::Host`,
1264/// there would be no way to distinguish those units from build-dependency
1265/// units. This can cause a problem if a shared normal/build dependency needs
1266/// to link to another dependency whose features differ based on whether or
1267/// not it is a normal or build dependency. If both units used
1268/// `CompileKind::Host`, then they would end up being identical, causing a
1269/// collision in the `UnitGraph`, and Cargo would end up randomly choosing one
1270/// value or the other.
1271///
1272/// The solution is to keep normal and build dependencies separate when
1273/// building the unit graph, and then run this second pass which will try to
1274/// combine shared dependencies safely. By adding a hash of the dependencies
1275/// to the `Unit`, this allows the `CompileKind` to be changed back to `Host`
1276/// without fear of an unwanted collision.
1277fn rebuild_unit_graph_shared(
1278 interner: &UnitInterner,
1279 unit_graph: UnitGraph,
1280 roots: &[Unit],
1281 to_host: CompileKind,
1282) -> (Vec<Unit>, UnitGraph) {
1283 let mut result = UnitGraph::new();
1284 // Map of the old unit to the new unit, used to avoid recursing into units
1285 // that have already been computed to improve performance.
1286 let mut memo = HashMap::new();
1287 let new_roots = roots
1288 .iter()
1289 .map(|root| {
1290 traverse_and_share(interner, &mut memo, &mut result, &unit_graph, root, to_host)
1291 })
1292 .collect();
1293 (new_roots, result)
1294}
1295
1296/// Recursive function for rebuilding the graph.
1297///
1298/// This walks `unit_graph`, starting at the given `unit`. It inserts the new
1299/// units into `new_graph`, and returns a new updated version of the given
1300/// unit (`dep_hash` is filled in, and `kind` switched if necessary).
1301fn traverse_and_share(
1302 interner: &UnitInterner,
1303 memo: &mut HashMap<Unit, Unit>,
1304 new_graph: &mut UnitGraph,
1305 unit_graph: &UnitGraph,
1306 unit: &Unit,
1307 to_host: CompileKind,
1308) -> Unit {
1309 if let Some(new_unit) = memo.get(unit) {
1310 // Already computed, no need to recompute.
1311 return new_unit.clone();
1312 }
1313 let mut dep_hash = StableHasher::new();
1314 let new_deps: Vec<_> = unit_graph[unit]
1315 .iter()
1316 .map(|dep| {
1317 let new_dep_unit =
1318 traverse_and_share(interner, memo, new_graph, unit_graph, &dep.unit, to_host);
1319 new_dep_unit.hash(&mut dep_hash);
1320 UnitDep {
1321 unit: new_dep_unit,
1322 ..dep.clone()
1323 }
1324 })
1325 .collect();
1326 let new_dep_hash = dep_hash.finish();
1327 let new_kind = if unit.kind == to_host {
1328 CompileKind::Host
1329 } else {
1330 unit.kind
1331 };
1332 let new_unit = interner.intern(
1333 &unit.pkg,
1334 &unit.target,
1335 unit.profile,
1336 new_kind,
1337 unit.mode,
1338 unit.features.clone(),
1339 unit.is_std,
1340 new_dep_hash,
1341 );
1342 assert!(memo.insert(unit.clone(), new_unit.clone()).is_none());
1343 new_graph.entry(new_unit.clone()).or_insert(new_deps);
1344 new_unit
1345}
42696ae2 1346
2361fb0f 1347/// Build `glob::Pattern` with informative context.
42696ae2 1348fn build_glob(pat: &str) -> CargoResult<glob::Pattern> {
5e3dc467 1349 glob::Pattern::new(pat).with_context(|| format!("cannot build glob pattern from `{}`", pat))
42696ae2 1350}
2361fb0f
WL
1351
1352/// Emits "package not found" error.
1353///
1354/// > This function should be used only in package selection processes such like
1355/// `Packages::to_package_id_specs` and `Packages::get_packages`.
1356fn emit_package_not_found(
1357 ws: &Workspace<'_>,
1358 opt_names: BTreeSet<&str>,
1359 opt_out: bool,
1360) -> CargoResult<()> {
1361 if !opt_names.is_empty() {
1362 anyhow::bail!(
5e3dc467 1363 "{}package(s) `{}` not found in workspace `{}`",
2361fb0f
WL
1364 if opt_out { "excluded " } else { "" },
1365 opt_names
5e3dc467 1366 .into_iter()
2361fb0f
WL
1367 .collect::<Vec<_>>()
1368 .join(", "),
1369 ws.root().display(),
1370 )
1371 }
1372 Ok(())
1373}
1374
1375/// Emits "glob pattern not found" error.
1376///
1377/// > This function should be used only in package selection processes such like
1378/// `Packages::to_package_id_specs` and `Packages::get_packages`.
1379fn emit_pattern_not_found(
1380 ws: &Workspace<'_>,
1381 opt_patterns: Vec<(glob::Pattern, bool)>,
1382 opt_out: bool,
1383) -> CargoResult<()> {
1384 let not_matched = opt_patterns
1385 .iter()
1386 .filter(|(_, matched)| !*matched)
1387 .map(|(pat, _)| pat.as_str())
1388 .collect::<Vec<_>>();
1389 if !not_matched.is_empty() {
1390 anyhow::bail!(
5e3dc467 1391 "{}package pattern(s) `{}` not found in workspace `{}`",
2361fb0f
WL
1392 if opt_out { "excluded " } else { "" },
1393 not_matched.join(", "),
1394 ws.root().display(),
1395 )
1396 }
1397 Ok(())
1398}
1399
1400/// Checks whether a package matches any of a list of glob patterns generated
1401/// from `opt_patterns_and_names`.
1402///
1403/// > This function should be used only in package selection processes such like
1404/// `Packages::to_package_id_specs` and `Packages::get_packages`.
1405fn match_patterns(pkg: &Package, patterns: &mut Vec<(glob::Pattern, bool)>) -> bool {
1406 patterns.iter_mut().any(|(m, matched)| {
1407 let is_matched = m.matches(pkg.name().as_str());
1408 *matched |= is_matched;
1409 is_matched
1410 })
1411}
1412
1413/// Given a list opt-in or opt-out package selection strings, generates two
1414/// collections that represent glob patterns and package names respectively.
1415///
1416/// > This function should be used only in package selection processes such like
1417/// `Packages::to_package_id_specs` and `Packages::get_packages`.
1418fn opt_patterns_and_names(
1419 opt: &[String],
1420) -> CargoResult<(Vec<(glob::Pattern, bool)>, BTreeSet<&str>)> {
1421 let mut opt_patterns = Vec::new();
1422 let mut opt_names = BTreeSet::new();
1423 for x in opt.iter() {
1424 if is_glob_pattern(x) {
1425 opt_patterns.push((build_glob(x)?, false));
1426 } else {
1427 opt_names.insert(String::as_str(x));
1428 }
1429 }
1430 Ok((opt_patterns, opt_names))
1431}