]> git.proxmox.com Git - cargo.git/blame - src/cargo/ops/cargo_compile.rs
Auto merge of #9467 - ehuss:install-metadata, r=alexcrichton
[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
1f14fa31 29use crate::core::compiler::unit_dependencies::build_unit_dependencies;
9efa0d55 30use crate::core::compiler::unit_graph::{self, UnitDep, UnitGraph};
39fb01c2 31use crate::core::compiler::{standard_lib, TargetInfo};
e4544466 32use crate::core::compiler::{BuildConfig, BuildContext, Compilation, Context};
39fb01c2 33use crate::core::compiler::{CompileKind, CompileMode, CompileTarget, RustcTargetData, Unit};
e4544466 34use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner};
04ddd4d0 35use crate::core::profiles::{Profiles, UnitFor};
85854b18
EH
36use crate::core::resolver::features::{self, CliFeatures, FeaturesFor};
37use crate::core::resolver::{HasDevUnits, Resolve};
19a95790 38use crate::core::{FeatureValue, Package, PackageSet, Shell, Summary, Target};
f32f72f4 39use crate::core::{PackageId, PackageIdSpec, SourceId, TargetKind, Workspace};
d7034c60 40use crate::drop_println;
04ddd4d0 41use crate::ops;
12392f6d 42use crate::ops::resolve::WorkspaceResolve;
04ddd4d0 43use crate::util::config::Config;
a58b0c58 44use crate::util::interning::InternedString;
42696ae2 45use crate::util::restricted_names::is_glob_pattern;
9efa0d55 46use crate::util::{closest_msg, profile, CargoResult, StableHasher};
50f110a4 47
42696ae2
WL
48use anyhow::Context as _;
49
bacb6be3 50/// Contains information about how a package should be compiled.
e0bd9e23
EH
51///
52/// Note on distinction between `CompileOptions` and `BuildConfig`:
53/// `BuildConfig` contains values that need to be retained after
54/// `BuildContext` is created. The other fields are no longer necessary. Think
55/// of it as `CompileOptions` are high-level settings requested on the
56/// command-line, and `BuildConfig` are low-level settings for actually
57/// driving `rustc`.
876af7c1 58#[derive(Debug)]
e4918c45 59pub struct CompileOptions {
08025169
DO
60 /// Configuration information for a rustc build
61 pub build_config: BuildConfig,
85854b18
EH
62 /// Feature flags requested by the user.
63 pub cli_features: CliFeatures,
6b32c4d7 64 /// A set of packages to build.
bb643cca 65 pub spec: Packages,
9e779198
AC
66 /// Filter to apply to the root package to select which targets will be
67 /// built.
bb643cca 68 pub filter: CompileFilter,
1ef954ea 69 /// Extra arguments to be passed to rustdoc (single target only)
bb643cca 70 pub target_rustdoc_args: Option<Vec<String>>,
77bb01ec
SL
71 /// The specified target will be compiled with all the available arguments,
72 /// note that this only accounts for the *final* invocation of rustc
bb643cca 73 pub target_rustc_args: Option<Vec<String>>,
1ef954ea
EH
74 /// Extra arguments passed to all selected targets for rustdoc.
75 pub local_rustdoc_args: Option<Vec<String>>,
e7ee237c
LK
76 /// Whether the `--document-private-items` flags was specified and should
77 /// be forwarded to `rustdoc`.
78 pub rustdoc_document_private_items: bool,
c221fec9
DO
79 /// Whether the build process should check the minimum Rust version
80 /// defined in the cargo metadata for a crate.
81 pub honor_rust_version: bool,
9e779198
AC
82}
83
e4918c45
EH
84impl<'a> CompileOptions {
85 pub fn new(config: &Config, mode: CompileMode) -> CargoResult<CompileOptions> {
08025169 86 Ok(CompileOptions {
3fd28143 87 build_config: BuildConfig::new(config, None, &[], mode)?,
85854b18 88 cli_features: CliFeatures::new_all(false),
bb643cca 89 spec: ops::Packages::Packages(Vec::new()),
1e682848
AC
90 filter: CompileFilter::Default {
91 required_features_filterable: false,
92 },
69caa63b
NC
93 target_rustdoc_args: None,
94 target_rustc_args: None,
1ef954ea 95 local_rustdoc_args: None,
e7ee237c 96 rustdoc_document_private_items: false,
c221fec9 97 honor_rust_version: true,
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 {
4b096bea 284 unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph, ws.config())?;
c4e5670b 285 return Compilation::new(&bcx);
0018ef06 286 }
e0bd9e23
EH
287 let _p = profile::start("compiling");
288 let cx = Context::new(&bcx)?;
289 cx.compile(exec)
290}
291
13807557
CF
292pub fn print<'a>(
293 ws: &Workspace<'a>,
294 options: &CompileOptions,
295 print_opt_value: &str,
296) -> CargoResult<()> {
297 let CompileOptions {
298 ref build_config,
299 ref target_rustc_args,
300 ..
301 } = *options;
302 let config = ws.config();
303 let rustc = config.load_global_rustc(Some(ws))?;
a05dac35 304 for (index, kind) in build_config.requested_kinds.iter().enumerate() {
2cd5d9df 305 if index != 0 {
5a24ad17 306 drop_println!(config);
2cd5d9df 307 }
39fb01c2 308 let target_info = TargetInfo::new(config, &build_config.requested_kinds, &rustc, *kind)?;
13807557 309 let mut process = rustc.process();
39fb01c2 310 process.args(&target_info.rustflags);
13807557
CF
311 if let Some(args) = target_rustc_args {
312 process.args(args);
313 }
314 if let CompileKind::Target(t) = kind {
315 process.arg("--target").arg(t.short_name());
316 }
a05dac35 317 process.arg("--print").arg(print_opt_value);
fa8e9ae9 318 process.exec()?;
13807557 319 }
e078a6c9
CF
320 Ok(())
321}
322
e0bd9e23
EH
323pub fn create_bcx<'a, 'cfg>(
324 ws: &'a Workspace<'cfg>,
325 options: &'a CompileOptions,
326 interner: &'a UnitInterner,
327) -> CargoResult<BuildContext<'a, 'cfg>> {
1e682848 328 let CompileOptions {
08025169 329 ref build_config,
1e682848 330 ref spec,
85854b18 331 ref cli_features,
1e682848
AC
332 ref filter,
333 ref target_rustdoc_args,
334 ref target_rustc_args,
1ef954ea 335 ref local_rustdoc_args,
e7ee237c 336 rustdoc_document_private_items,
c221fec9 337 honor_rust_version,
1e682848 338 } = *options;
e4918c45 339 let config = ws.config();
3656bec8 340
d649c661 341 // Perform some pre-flight validation.
d17ccec2
HY
342 match build_config.mode {
343 CompileMode::Test
344 | CompileMode::Build
345 | CompileMode::Check { .. }
346 | CompileMode::Bench
347 | CompileMode::RunCustomBuild => {
348 if std::env::var("RUST_FLAGS").is_ok() {
349 config.shell().warn(
350 "Cargo does not read `RUST_FLAGS` environment variable. Did you mean `RUSTFLAGS`?",
351 )?;
352 }
353 }
354 CompileMode::Doc { .. } | CompileMode::Doctest => {
355 if std::env::var("RUSTDOC_FLAGS").is_ok() {
356 config.shell().warn(
357 "Cargo does not read `RUSTDOC_FLAGS` environment variable. Did you mean `RUSTDOCFLAGS`?"
358 )?;
359 }
360 }
361 }
d649c661 362 config.validate_term_config()?;
d17ccec2 363
3fd28143 364 let target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?;
47007d97 365
8947ed1f 366 let specs = spec.to_package_id_specs(ws)?;
2b1dc3e5
AT
367 let has_dev_units = if filter.need_dev_deps(build_config.mode) {
368 HasDevUnits::Yes
369 } else {
370 HasDevUnits::No
e0d64f94 371 };
d728f386
EH
372 let resolve = ops::resolve_ws_with_opts(
373 ws,
374 &target_data,
3fd28143 375 &build_config.requested_kinds,
85854b18 376 cli_features,
d728f386
EH
377 &specs,
378 has_dev_units,
d267fac2 379 crate::core::resolver::features::ForceAllTargets::No,
d728f386 380 )?;
12392f6d
EH
381 let WorkspaceResolve {
382 mut pkg_set,
383 workspace_resolve,
384 targeted_resolve: resolve,
7caa1612 385 resolved_features,
12392f6d 386 } = resolve;
1f14fa31 387
7caa1612 388 let std_resolve_features = if let Some(crates) = &config.cli_unstable().build_std {
3afb5d7d
EH
389 if build_config.build_plan {
390 config
391 .shell()
392 .warn("-Zbuild-std does not currently fully support --build-plan")?;
393 }
3fd28143 394 if build_config.requested_kinds[0].is_host() {
1f14fa31
EH
395 // TODO: This should eventually be fixed. Unfortunately it is not
396 // easy to get the host triple in BuildConfig. Consider changing
397 // requested_target to an enum, or some other approach.
3a18c89a 398 anyhow::bail!("-Zbuild-std requires --target");
1f14fa31 399 }
98b6f816 400 let (std_package_set, std_resolve, std_features) =
3fd28143 401 standard_lib::resolve_std(ws, &target_data, &build_config.requested_kinds, crates)?;
12392f6d 402 pkg_set.add_set(std_package_set);
7caa1612 403 Some((std_resolve, std_features))
1f14fa31
EH
404 } else {
405 None
406 };
9c296792 407
1f14fa31
EH
408 // Find the packages in the resolver that the user wants to build (those
409 // passed in with `-p` or the defaults from the workspace), and convert
96a39371
EH
410 // Vec<PackageIdSpec> to a Vec<PackageId>.
411 let to_build_ids = resolve.specs_to_ids(&specs)?;
1f14fa31
EH
412 // Now get the `Package` for each `PackageId`. This may trigger a download
413 // if the user specified `-p` for a dependency that is not downloaded.
414 // Dependencies will be downloaded during build_unit_dependencies.
12392f6d 415 let mut to_builds = pkg_set.get_many(to_build_ids)?;
0ea0c8ba 416
7b1a0dc5
AC
417 // The ordering here affects some error messages coming out of cargo, so
418 // let's be test and CLI friendly by always printing in the same order if
419 // there's an error.
420 to_builds.sort_by_key(|p| p.package_id());
421
422 for pkg in to_builds.iter() {
1f14fa31 423 pkg.manifest().print_teapot(config);
786848a0
EH
424
425 if build_config.mode.is_any_test()
426 && !ws.is_member(pkg)
427 && pkg.dependencies().iter().any(|dep| !dep.is_transitive())
428 {
3a18c89a 429 anyhow::bail!(
786848a0
EH
430 "package `{}` cannot be tested because it requires dev-dependencies \
431 and is not a member of the workspace",
432 pkg.name()
433 );
434 }
7b1a0dc5 435 }
f0647ea2 436
575d6e81
EH
437 let (extra_args, extra_args_name) = match (target_rustc_args, target_rustdoc_args) {
438 (&Some(ref args), _) => (Some(args.clone()), "rustc"),
439 (_, &Some(ref args)) => (Some(args.clone()), "rustdoc"),
440 _ => (None, ""),
f0647ea2 441 };
b3ade7c7 442
575d6e81
EH
443 if extra_args.is_some() && to_builds.len() != 1 {
444 panic!(
445 "`{}` should not accept multiple `-p` flags",
446 extra_args_name
447 );
448 }
449
ed4568e1 450 let profiles = Profiles::new(ws, build_config.requested_profile)?;
12392f6d 451 profiles.validate_packages(
77ee608d 452 ws.profiles(),
12392f6d
EH
453 &mut config.shell(),
454 workspace_resolve.as_ref().unwrap_or(&resolve),
455 )?;
a0a880c3 456
9efa0d55
EH
457 // If `--target` has not been specified, then the unit graph is built
458 // assuming `--target $HOST` was specified. See
459 // `rebuild_unit_graph_shared` for more on why this is done.
460 let explicit_host_kind = CompileKind::Target(CompileTarget::new(&target_data.rustc.host)?);
461 let explicit_host_kinds: Vec<_> = build_config
462 .requested_kinds
463 .iter()
464 .map(|kind| match kind {
465 CompileKind::Host => explicit_host_kind,
466 CompileKind::Target(t) => CompileKind::Target(*t),
467 })
468 .collect();
469
5e11afc1
LG
470 // Passing `build_config.requested_kinds` instead of
471 // `explicit_host_kinds` here so that `generate_targets` can do
472 // its own special handling of `CompileKind::Host`. It will
473 // internally replace the host kind by the `explicit_host_kind`
474 // before setting as a unit.
9efa0d55 475 let mut units = generate_targets(
73660740 476 ws,
575d6e81
EH
477 &to_builds,
478 filter,
5e11afc1
LG
479 &build_config.requested_kinds,
480 explicit_host_kind,
e0bd9e23 481 build_config.mode,
12392f6d 482 &resolve,
19a95790 483 &workspace_resolve,
7caa1612 484 &resolved_features,
e0bd9e23
EH
485 &pkg_set,
486 &profiles,
686ccfa4 487 interner,
575d6e81
EH
488 )?;
489
1f14fa31 490 let std_roots = if let Some(crates) = &config.cli_unstable().build_std {
3ba9de85
EH
491 // Only build libtest if it looks like it is needed.
492 let mut crates = crates.clone();
493 if !crates.iter().any(|c| c == "test")
494 && units
495 .iter()
496 .any(|unit| unit.mode.is_rustc_test() && unit.target.harness())
497 {
18a89732
PO
498 // Only build libtest when libstd is built (libtest depends on libstd)
499 if crates.iter().any(|c| c == "std") {
500 crates.push("test".to_string());
501 }
3ba9de85 502 }
7caa1612 503 let (std_resolve, std_features) = std_resolve_features.as_ref().unwrap();
593a02f2 504 standard_lib::generate_std_roots(
593a02f2 505 &crates,
7caa1612
EH
506 std_resolve,
507 std_features,
9efa0d55 508 &explicit_host_kinds,
e0bd9e23 509 &pkg_set,
686ccfa4 510 interner,
e0bd9e23 511 &profiles,
593a02f2 512 )?
1f14fa31 513 } else {
3fd28143 514 Default::default()
1f14fa31
EH
515 };
516
9efa0d55
EH
517 let mut unit_graph = build_unit_dependencies(
518 ws,
519 &pkg_set,
520 &resolve,
521 &resolved_features,
522 std_resolve_features.as_ref(),
523 &units,
524 &std_roots,
525 build_config.mode,
526 &target_data,
527 &profiles,
528 interner,
529 )?;
530
a58b0c58
EH
531 // TODO: In theory, Cargo should also dedupe the roots, but I'm uncertain
532 // what heuristics to use in that case.
533 if build_config.mode == (CompileMode::Doc { deps: true }) {
5ebb605c 534 remove_duplicate_doc(build_config, &units, &mut unit_graph);
a58b0c58
EH
535 }
536
9efa0d55
EH
537 if build_config
538 .requested_kinds
539 .iter()
540 .any(CompileKind::is_host)
541 {
542 // Rebuild the unit graph, replacing the explicit host targets with
543 // CompileKind::Host, merging any dependencies shared with build
544 // dependencies.
545 let new_graph = rebuild_unit_graph_shared(interner, unit_graph, &units, explicit_host_kind);
546 // This would be nicer with destructuring assignment.
547 units = new_graph.0;
548 unit_graph = new_graph.1;
549 }
550
e0bd9e23 551 let mut extra_compiler_args = HashMap::new();
575d6e81
EH
552 if let Some(args) = extra_args {
553 if units.len() != 1 {
3a18c89a 554 anyhow::bail!(
575d6e81 555 "extra arguments to `{}` can only be passed to one \
f7c91ba6
AR
556 target, consider filtering\nthe package by passing, \
557 e.g., `--lib` or `--bin NAME` to specify a single target",
575d6e81
EH
558 extra_args_name
559 );
8a8ea1e8 560 }
45d49579 561 extra_compiler_args.insert(units[0].clone(), args);
1ef954ea 562 }
e7ee237c
LK
563 for unit in &units {
564 if unit.mode.is_doc() || unit.mode.is_doc_test() {
565 let mut extra_args = local_rustdoc_args.clone();
566
567 // Add `--document-private-items` rustdoc flag if requested or if
568 // the target is a binary. Binary crates get their private items
569 // documented by default.
570 if rustdoc_document_private_items || unit.target.is_bin() {
a6a395c6 571 let mut args = extra_args.take().unwrap_or_else(|| vec![]);
e7ee237c
LK
572 args.push("--document-private-items".into());
573 extra_args = Some(args);
574 }
575
1d1b3447
JN
576 if let Some(args) = extra_args {
577 extra_compiler_args
578 .entry(unit.clone())
579 .or_default()
580 .extend(args);
1ef954ea
EH
581 }
582 }
b3ade7c7 583 }
575d6e81 584
c221fec9
DO
585 if honor_rust_version {
586 // Remove any pre-release identifiers for easier comparison
587 let current_version = &target_data.rustc.version;
588 let untagged_version = semver::Version::new(
589 current_version.major,
590 current_version.minor,
591 current_version.patch,
592 );
593
594 for unit in unit_graph.keys() {
595 let version = match unit.pkg.rust_version() {
596 Some(v) => v,
597 None => continue,
598 };
599
600 let req = semver::VersionReq::parse(version).unwrap();
601 if req.matches(&untagged_version) {
602 continue;
603 }
604
605 anyhow::bail!(
606 "package `{}` cannot be built because it requires rustc {} or newer, \
607 while the currently active rustc version is {}",
608 unit.pkg,
609 version,
610 current_version,
611 );
612 }
613 }
614
e0bd9e23
EH
615 let bcx = BuildContext::new(
616 ws,
617 pkg_set,
618 build_config,
619 profiles,
620 extra_compiler_args,
621 target_data,
622 units,
623 unit_graph,
624 )?;
8b840393 625
e0bd9e23 626 Ok(bcx)
62bff631 627}
51c9cf0f 628
bb643cca
AK
629impl FilterRule {
630 pub fn new(targets: Vec<String>, all: bool) -> FilterRule {
2a82378a
BW
631 if all {
632 FilterRule::All
633 } else {
634 FilterRule::Just(targets)
635 }
636 }
637
12307fc2
DW
638 pub fn none() -> FilterRule {
639 FilterRule::Just(Vec::new())
640 }
641
2a82378a
BW
642 fn matches(&self, target: &Target) -> bool {
643 match *self {
644 FilterRule::All => true,
1e682848 645 FilterRule::Just(ref targets) => targets.iter().any(|x| *x == target.name()),
2a82378a
BW
646 }
647 }
648
649 fn is_specific(&self) -> bool {
650 match *self {
651 FilterRule::All => true,
bb643cca 652 FilterRule::Just(ref targets) => !targets.is_empty(),
2a82378a
BW
653 }
654 }
655
656 pub fn try_collect(&self) -> Option<Vec<String>> {
657 match *self {
658 FilterRule::All => None,
bb643cca 659 FilterRule::Just(ref targets) => Some(targets.clone()),
2a82378a
BW
660 }
661 }
42696ae2
WL
662
663 pub(crate) fn contains_glob_patterns(&self) -> bool {
664 match self {
665 FilterRule::All => false,
666 FilterRule::Just(targets) => targets.iter().any(is_glob_pattern),
667 }
668 }
2a82378a
BW
669}
670
bb643cca 671impl CompileFilter {
12307fc2
DW
672 /// Construct a CompileFilter from raw command line arguments.
673 pub fn from_raw_arguments(
1e682848
AC
674 lib_only: bool,
675 bins: Vec<String>,
676 all_bins: bool,
677 tsts: Vec<String>,
678 all_tsts: bool,
679 exms: Vec<String>,
680 all_exms: bool,
681 bens: Vec<String>,
682 all_bens: bool,
683 all_targets: bool,
684 ) -> CompileFilter {
5a59b809
EH
685 if all_targets {
686 return CompileFilter::new_all_targets();
687 }
f16efff1
AC
688 let rule_lib = if lib_only {
689 LibRule::True
690 } else {
691 LibRule::False
692 };
2a82378a
BW
693 let rule_bins = FilterRule::new(bins, all_bins);
694 let rule_tsts = FilterRule::new(tsts, all_tsts);
695 let rule_exms = FilterRule::new(exms, all_exms);
696 let rule_bens = FilterRule::new(bens, all_bens);
697
5a59b809 698 CompileFilter::new(rule_lib, rule_bins, rule_tsts, rule_exms, rule_bens)
12307fc2
DW
699 }
700
701 /// Construct a CompileFilter from underlying primitives.
702 pub fn new(
aef99e0e 703 rule_lib: LibRule,
12307fc2
DW
704 rule_bins: FilterRule,
705 rule_tsts: FilterRule,
706 rule_exms: FilterRule,
707 rule_bens: FilterRule,
708 ) -> CompileFilter {
aef99e0e 709 if rule_lib == LibRule::True
dae87a26
E
710 || rule_bins.is_specific()
711 || rule_tsts.is_specific()
712 || rule_exms.is_specific()
713 || rule_bens.is_specific()
1e682848 714 {
7a1d8d94 715 CompileFilter::Only {
b9497ab2 716 all_targets: false,
aef99e0e 717 lib: rule_lib,
1e682848
AC
718 bins: rule_bins,
719 examples: rule_exms,
720 benches: rule_bens,
2a82378a 721 tests: rule_tsts,
7a1d8d94
AC
722 }
723 } else {
6344db05 724 CompileFilter::Default {
25e50b58
JB
725 required_features_filterable: true,
726 }
7a1d8d94
AC
727 }
728 }
729
5a59b809
EH
730 pub fn new_all_targets() -> CompileFilter {
731 CompileFilter::Only {
732 all_targets: true,
733 lib: LibRule::Default,
734 bins: FilterRule::All,
735 examples: FilterRule::All,
736 benches: FilterRule::All,
737 tests: FilterRule::All,
738 }
739 }
740
89d5161d
XL
741 pub fn need_dev_deps(&self, mode: CompileMode) -> bool {
742 match mode {
743 CompileMode::Test | CompileMode::Doctest | CompileMode::Bench => true,
b112255f
EH
744 CompileMode::Check { test: true } => true,
745 CompileMode::Build | CompileMode::Doc { .. } | CompileMode::Check { test: false } => {
746 match *self {
747 CompileFilter::Default { .. } => false,
748 CompileFilter::Only {
749 ref examples,
750 ref tests,
751 ref benches,
752 ..
753 } => examples.is_specific() || tests.is_specific() || benches.is_specific(),
754 }
755 }
575d6e81 756 CompileMode::RunCustomBuild => panic!("Invalid mode"),
7de30dd2
XL
757 }
758 }
759
ce26ddfd 760 // this selects targets for "cargo run". for logic to select targets for
771fec3c 761 // other subcommands, see generate_targets and filter_default_targets
ce26ddfd 762 pub fn target_run(&self, target: &Target) -> bool {
9e779198 763 match *self {
70170d1b 764 CompileFilter::Default { .. } => true,
1e682848 765 CompileFilter::Only {
aef99e0e 766 ref lib,
1e682848
AC
767 ref bins,
768 ref examples,
769 ref tests,
770 ref benches,
771 ..
772 } => {
2a82378a 773 let rule = match *target.kind() {
9e779198
AC
774 TargetKind::Bin => bins,
775 TargetKind::Test => tests,
776 TargetKind::Bench => benches,
1e682848 777 TargetKind::ExampleBin | TargetKind::ExampleLib(..) => examples,
f16efff1
AC
778 TargetKind::Lib(..) => {
779 return match *lib {
780 LibRule::True => true,
781 LibRule::Default => true,
782 LibRule::False => false,
783 };
784 }
9e779198
AC
785 TargetKind::CustomBuild => return false,
786 };
2a82378a 787 rule.matches(target)
9e779198
AC
788 }
789 }
790 }
c98b8c4c
BW
791
792 pub fn is_specific(&self) -> bool {
793 match *self {
6344db05 794 CompileFilter::Default { .. } => false,
c98b8c4c
BW
795 CompileFilter::Only { .. } => true,
796 }
797 }
42696ae2 798
501499c5 799 pub fn is_all_targets(&self) -> bool {
1b7fa21f
MK
800 matches!(
801 *self,
501499c5 802 CompileFilter::Only {
1b7fa21f
MK
803 all_targets: true,
804 ..
805 }
806 )
501499c5
EH
807 }
808
42696ae2
WL
809 pub(crate) fn contains_glob_patterns(&self) -> bool {
810 match self {
811 CompileFilter::Default { .. } => false,
812 CompileFilter::Only {
813 bins,
814 examples,
815 tests,
816 benches,
817 ..
818 } => {
819 bins.contains_glob_patterns()
820 || examples.contains_glob_patterns()
821 || tests.contains_glob_patterns()
822 || benches.contains_glob_patterns()
823 }
824 }
825 }
9e779198
AC
826}
827
3a1cad6f
EH
828/// A proposed target.
829///
f7c91ba6 830/// Proposed targets are later filtered into actual `Unit`s based on whether or
3a1cad6f
EH
831/// not the target requires its features to be present.
832#[derive(Debug)]
833struct Proposal<'a> {
fbd34a69 834 pkg: &'a Package,
45d49579 835 target: &'a Target,
3a1cad6f
EH
836 /// Indicates whether or not all required features *must* be present. If
837 /// false, and the features are not available, then it will be silently
838 /// skipped. Generally, targets specified by name (`--bin foo`) are
839 /// required, all others can be silently skipped if features are missing.
840 requires_features: bool,
841 mode: CompileMode,
842}
843
575d6e81 844/// Generates all the base targets for the packages the user has requested to
f7c91ba6 845/// compile. Dependencies for these targets are computed later in `unit_dependencies`.
c85ed044 846fn generate_targets(
b8b7faee 847 ws: &Workspace<'_>,
fbd34a69 848 packages: &[&Package],
575d6e81 849 filter: &CompileFilter,
3fd28143 850 requested_kinds: &[CompileKind],
5e11afc1 851 explicit_host_kind: CompileKind,
e0bd9e23
EH
852 mode: CompileMode,
853 resolve: &Resolve,
19a95790 854 workspace_resolve: &Option<Resolve>,
7caa1612 855 resolved_features: &features::ResolvedFeatures,
e0bd9e23
EH
856 package_set: &PackageSet<'_>,
857 profiles: &Profiles,
c85ed044
AC
858 interner: &UnitInterner,
859) -> CargoResult<Vec<Unit>> {
e0bd9e23 860 let config = ws.config();
3fd28143
AC
861 // Helper for creating a list of `Unit` structures
862 let new_unit =
863 |units: &mut HashSet<Unit>, pkg: &Package, target: &Target, target_mode: CompileMode| {
864 let unit_for = if target_mode.is_any_test() {
865 // NOTE: the `UnitFor` here is subtle. If you have a profile
866 // with `panic` set, the `panic` flag is cleared for
867 // tests/benchmarks and their dependencies. If this
868 // was `normal`, then the lib would get compiled three
869 // times (once with panic, once without, and once with
870 // `--test`).
871 //
872 // This would cause a problem for doc tests, which would fail
873 // because `rustdoc` would attempt to link with both libraries
874 // at the same time. Also, it's probably not important (or
875 // even desirable?) for rustdoc to link with a lib with
876 // `panic` set.
877 //
878 // As a consequence, Examples and Binaries get compiled
879 // without `panic` set. This probably isn't a bad deal.
880 //
881 // Forcing the lib to be compiled three times during `cargo
882 // test` is probably also not desirable.
883 UnitFor::new_test(config)
884 } else if target.for_host() {
885 // Proc macro / plugin should not have `panic` set.
886 UnitFor::new_compiler()
887 } else {
888 UnitFor::new_normal()
889 };
890 // Custom build units are added in `build_unit_dependencies`.
891 assert!(!target.is_custom_build());
892 let target_mode = match target_mode {
893 CompileMode::Test => {
894 if target.is_example() && !filter.is_specific() && !target.tested() {
895 // Examples are included as regular binaries to verify
896 // that they compile.
897 CompileMode::Build
898 } else {
899 CompileMode::Test
900 }
2a82378a 901 }
3fd28143
AC
902 CompileMode::Build => match *target.kind() {
903 TargetKind::Test => CompileMode::Test,
904 TargetKind::Bench => CompileMode::Bench,
905 _ => CompileMode::Build,
906 },
907 // `CompileMode::Bench` is only used to inform `filter_default_targets`
908 // which command is being used (`cargo bench`). Afterwards, tests
909 // and benches are treated identically. Switching the mode allows
910 // de-duplication of units that are essentially identical. For
911 // example, `cargo build --all-targets --release` creates the units
912 // (lib profile:bench, mode:test) and (lib profile:bench, mode:bench)
913 // and since these are the same, we want them to be de-duplicated in
914 // `unit_dependencies`.
915 CompileMode::Bench => CompileMode::Test,
916 _ => target_mode,
917 };
918
919 let is_local = pkg.package_id().source_id().is_path();
3fd28143
AC
920
921 // No need to worry about build-dependencies, roots are never build dependencies.
922 let features_for = FeaturesFor::from_for_host(target.proc_macro());
923 let features = resolved_features.activated_features(pkg.package_id(), features_for);
924
5e11afc1
LG
925 // If `--target` has not been specified, then the unit
926 // graph is built almost like if `--target $HOST` was
927 // specified. See `rebuild_unit_graph_shared` for more on
928 // why this is done. However, if the package has its own
929 // `package.target` key, then this gets used instead of
930 // `$HOST`
931 let explicit_kinds = if let Some(k) = pkg.manifest().forced_kind() {
932 vec![k]
933 } else {
934 requested_kinds
935 .iter()
936 .map(|kind| match kind {
937 CompileKind::Host => {
938 pkg.manifest().default_kind().unwrap_or(explicit_host_kind)
939 }
940 CompileKind::Target(t) => CompileKind::Target(*t),
941 })
942 .collect()
943 };
944
945 for kind in explicit_kinds.iter() {
a4f0988e
AC
946 let profile = profiles.get_profile(
947 pkg.package_id(),
948 ws.is_member(pkg),
949 is_local,
950 unit_for,
951 target_mode,
952 *kind,
953 );
3fd28143
AC
954 let unit = interner.intern(
955 pkg,
956 target,
957 profile,
958 kind.for_target(target),
959 target_mode,
960 features.clone(),
961 /*is_std*/ false,
9efa0d55 962 /*dep_hash*/ 0,
3fd28143
AC
963 );
964 units.insert(unit);
2a82378a 965 }
575d6e81
EH
966 };
967
3a1cad6f 968 // Create a list of proposed targets.
b8b7faee 969 let mut proposals: Vec<Proposal<'_>> = Vec::new();
771fec3c
EH
970
971 match *filter {
972 CompileFilter::Default {
973 required_features_filterable,
974 } => {
975 for pkg in packages {
e0bd9e23 976 let default = filter_default_targets(pkg.targets(), mode);
3a1cad6f
EH
977 proposals.extend(default.into_iter().map(|target| Proposal {
978 pkg,
979 target,
980 requires_features: !required_features_filterable,
e0bd9e23 981 mode,
771fec3c 982 }));
e0bd9e23 983 if mode == CompileMode::Test {
dd6c6102
EH
984 if let Some(t) = pkg
985 .targets()
986 .iter()
987 .find(|t| t.is_lib() && t.doctested() && t.doctestable())
988 {
3a1cad6f
EH
989 proposals.push(Proposal {
990 pkg,
991 target: t,
992 requires_features: false,
993 mode: CompileMode::Doctest,
994 });
575d6e81 995 }
2a82378a
BW
996 }
997 }
771fec3c
EH
998 }
999 CompileFilter::Only {
1000 all_targets,
aef99e0e 1001 ref lib,
771fec3c
EH
1002 ref bins,
1003 ref examples,
1004 ref tests,
1005 ref benches,
1006 } => {
aef99e0e 1007 if *lib != LibRule::False {
771fec3c 1008 let mut libs = Vec::new();
e0bd9e23 1009 for proposal in filter_targets(packages, Target::is_lib, false, mode) {
df9ca871 1010 let Proposal { target, pkg, .. } = proposal;
e0bd9e23 1011 if mode.is_doc_test() && !target.doctestable() {
7a06be14
EH
1012 let types = target.rustc_crate_types();
1013 let types_str: Vec<&str> = types.iter().map(|t| t.as_str()).collect();
df9ca871
DW
1014 ws.config().shell().warn(format!(
1015 "doc tests are not supported for crate type(s) `{}` in package `{}`",
7a06be14 1016 types_str.join(", "),
df9ca871
DW
1017 pkg.name()
1018 ))?;
1019 } else {
1020 libs.push(proposal)
575d6e81
EH
1021 }
1022 }
aef99e0e 1023 if !all_targets && libs.is_empty() && *lib == LibRule::True {
771fec3c
EH
1024 let names = packages.iter().map(|pkg| pkg.name()).collect::<Vec<_>>();
1025 if names.len() == 1 {
3a18c89a 1026 anyhow::bail!("no library targets found in package `{}`", names[0]);
771fec3c 1027 } else {
3a18c89a 1028 anyhow::bail!("no library targets found in packages: {}", names.join(", "));
771fec3c
EH
1029 }
1030 }
1031 proposals.extend(libs);
575d6e81 1032 }
3a1cad6f 1033
f7c91ba6 1034 // If `--tests` was specified, add all targets that would be
771fec3c 1035 // generated by `cargo test`.
df9ca871 1036 let test_filter = match tests {
771fec3c
EH
1037 FilterRule::All => Target::tested,
1038 FilterRule::Just(_) => Target::is_test,
1039 };
e0bd9e23 1040 let test_mode = match mode {
771fec3c
EH
1041 CompileMode::Build => CompileMode::Test,
1042 CompileMode::Check { .. } => CompileMode::Check { test: true },
e0bd9e23 1043 _ => mode,
771fec3c 1044 };
f7c91ba6 1045 // If `--benches` was specified, add all targets that would be
771fec3c 1046 // generated by `cargo bench`.
df9ca871 1047 let bench_filter = match benches {
771fec3c
EH
1048 FilterRule::All => Target::benched,
1049 FilterRule::Just(_) => Target::is_bench,
1050 };
e0bd9e23 1051 let bench_mode = match mode {
771fec3c
EH
1052 CompileMode::Build => CompileMode::Bench,
1053 CompileMode::Check { .. } => CompileMode::Check { test: true },
e0bd9e23 1054 _ => mode,
771fec3c
EH
1055 };
1056
1057 proposals.extend(list_rule_targets(
1058 packages,
1059 bins,
1060 "bin",
1061 Target::is_bin,
e0bd9e23 1062 mode,
771fec3c
EH
1063 )?);
1064 proposals.extend(list_rule_targets(
1065 packages,
1066 examples,
1067 "example",
1068 Target::is_example,
e0bd9e23 1069 mode,
771fec3c
EH
1070 )?);
1071 proposals.extend(list_rule_targets(
1072 packages,
1073 tests,
1074 "test",
1075 test_filter,
1076 test_mode,
1077 )?);
1078 proposals.extend(list_rule_targets(
1079 packages,
1080 benches,
1081 "bench",
1082 bench_filter,
1083 bench_mode,
1084 )?);
2a82378a 1085 }
771fec3c 1086 }
2a82378a 1087
771fec3c
EH
1088 // Only include targets that are libraries or have all required
1089 // features available.
7caa1612
EH
1090 //
1091 // `features_map` is a map of &Package -> enabled_features
1092 // It is computed by the set of enabled features for the package plus
1093 // every enabled feature of every enabled dependency.
771fec3c 1094 let mut features_map = HashMap::new();
3a1cad6f 1095 let mut units = HashSet::new();
dae87a26
E
1096 for Proposal {
1097 pkg,
1098 target,
1099 requires_features,
1100 mode,
1101 } in proposals
1102 {
771fec3c
EH
1103 let unavailable_features = match target.required_features() {
1104 Some(rf) => {
bcfdf9fb 1105 validate_required_features(
19a95790 1106 workspace_resolve,
bcfdf9fb 1107 target.name(),
19a95790
AT
1108 rf,
1109 pkg.summary(),
1110 &mut config.shell(),
1111 )?;
1112
7caa1612 1113 let features = features_map.entry(pkg).or_insert_with(|| {
e0bd9e23 1114 resolve_all_features(resolve, resolved_features, package_set, pkg.package_id())
7caa1612 1115 });
771fec3c 1116 rf.iter().filter(|f| !features.contains(*f)).collect()
2a82378a 1117 }
771fec3c
EH
1118 None => Vec::new(),
1119 };
1120 if target.is_lib() || unavailable_features.is_empty() {
3fd28143 1121 new_unit(&mut units, pkg, target, mode);
3a1cad6f 1122 } else if requires_features {
771fec3c
EH
1123 let required_features = target.required_features().unwrap();
1124 let quoted_required_features: Vec<String> = required_features
1125 .iter()
1126 .map(|s| format!("`{}`", s))
1127 .collect();
3a18c89a 1128 anyhow::bail!(
771fec3c 1129 "target `{}` in package `{}` requires the features: {}\n\
f7c91ba6 1130 Consider enabling them by passing, e.g., `--features=\"{}\"`",
771fec3c
EH
1131 target.name(),
1132 pkg.name(),
1133 quoted_required_features.join(", "),
1134 required_features.join(" ")
1135 );
2a82378a 1136 }
771fec3c 1137 // else, silently skip target.
2a82378a 1138 }
3a1cad6f 1139 Ok(units.into_iter().collect())
2a82378a
BW
1140}
1141
bcfdf9fb
EH
1142/// Warns if a target's required-features references a feature that doesn't exist.
1143///
1144/// This is a warning because historically this was not validated, and it
1145/// would cause too much breakage to make it an error.
1146fn validate_required_features(
19a95790 1147 resolve: &Option<Resolve>,
bcfdf9fb 1148 target_name: &str,
19a95790
AT
1149 required_features: &[String],
1150 summary: &Summary,
1151 shell: &mut Shell,
1152) -> CargoResult<()> {
1153 let resolve = match resolve {
1154 None => return Ok(()),
1155 Some(resolve) => resolve,
1156 };
1157
1158 for feature in required_features {
bcfdf9fb
EH
1159 let fv = FeatureValue::new(feature.into());
1160 match &fv {
1161 FeatureValue::Feature(f) => {
1162 if !summary.features().contains_key(f) {
19a95790 1163 shell.warn(format!(
bcfdf9fb
EH
1164 "invalid feature `{}` in required-features of target `{}`: \
1165 `{}` is not present in [features] section",
1166 fv, target_name, fv
19a95790
AT
1167 ))?;
1168 }
1169 }
f4ac82a0
EH
1170 FeatureValue::Dep { .. }
1171 | FeatureValue::DepFeature {
1172 dep_prefix: true, ..
8ff130bb 1173 } => {
bcfdf9fb
EH
1174 anyhow::bail!(
1175 "invalid feature `{}` in required-features of target `{}`: \
f4ac82a0 1176 `dep:` prefixed feature values are not allowed in required-features",
bcfdf9fb
EH
1177 fv,
1178 target_name
1179 );
1180 }
9ffcf690
EH
1181 FeatureValue::DepFeature { weak: true, .. } => {
1182 anyhow::bail!(
1183 "invalid feature `{}` in required-features of target `{}`: \
1184 optional dependency with `?` is not allowed in required-features",
1185 fv,
1186 target_name
1187 );
1188 }
19a95790 1189 // Handling of dependent_crate/dependent_crate_feature syntax
f4ac82a0 1190 FeatureValue::DepFeature {
bcfdf9fb
EH
1191 dep_name,
1192 dep_feature,
f4ac82a0 1193 dep_prefix: false,
9ffcf690 1194 weak: false,
bcfdf9fb 1195 } => {
19a95790
AT
1196 match resolve
1197 .deps(summary.package_id())
bcfdf9fb 1198 .find(|(_dep_id, deps)| deps.iter().any(|dep| dep.name_in_toml() == *dep_name))
19a95790
AT
1199 {
1200 Some((dep_id, _deps)) => {
1201 let dep_summary = resolve.summary(dep_id);
bcfdf9fb 1202 if !dep_summary.features().contains_key(dep_feature)
19a95790
AT
1203 && !dep_summary
1204 .dependencies()
1205 .iter()
bcfdf9fb 1206 .any(|dep| dep.name_in_toml() == *dep_feature && dep.is_optional())
19a95790
AT
1207 {
1208 shell.warn(format!(
bcfdf9fb
EH
1209 "invalid feature `{}` in required-features of target `{}`: \
1210 feature `{}` does not exist in package `{}`",
1211 fv, target_name, dep_feature, dep_id
19a95790
AT
1212 ))?;
1213 }
1214 }
1215 None => {
1216 shell.warn(format!(
bcfdf9fb
EH
1217 "invalid feature `{}` in required-features of target `{}`: \
1218 dependency `{}` does not exist",
1219 fv, target_name, dep_name
19a95790
AT
1220 ))?;
1221 }
1222 }
1223 }
1224 }
1225 }
1226 Ok(())
1227}
1228
3f068230
EH
1229/// Gets all of the features enabled for a package, plus its dependencies'
1230/// features.
1231///
1232/// Dependencies are added as `dep_name/feat_name` because `required-features`
1233/// wants to support that syntax.
8b17895b 1234pub fn resolve_all_features(
575d6e81 1235 resolve_with_overrides: &Resolve,
7caa1612 1236 resolved_features: &features::ResolvedFeatures,
944f5049 1237 package_set: &PackageSet<'_>,
dae87a26 1238 package_id: PackageId,
575d6e81 1239) -> HashSet<String> {
7caa1612 1240 let mut features: HashSet<String> = resolved_features
137642c4 1241 .activated_features(package_id, FeaturesFor::NormalOrDev)
7caa1612
EH
1242 .iter()
1243 .map(|s| s.to_string())
1244 .collect();
575d6e81
EH
1245
1246 // Include features enabled for use by dependencies so targets can also use them with the
1247 // required-features field when deciding whether to be built or skipped.
cafec114 1248 for (dep_id, deps) in resolve_with_overrides.deps(package_id) {
944f5049
EH
1249 let is_proc_macro = package_set
1250 .get_one(dep_id)
1251 .expect("packages downloaded")
1252 .proc_macro();
8973b959 1253 for dep in deps {
96a39371 1254 let features_for = FeaturesFor::from_for_host(is_proc_macro || dep.is_build());
e94facea
R
1255 for feature in resolved_features
1256 .activated_features_unverified(dep_id, features_for)
1257 .unwrap_or_default()
1258 {
887ee6cc 1259 features.insert(format!("{}/{}", dep.name_in_toml(), feature));
cafec114 1260 }
575d6e81
EH
1261 }
1262 }
1263
1264 features
1265}
1266
1267/// Given a list of all targets for a package, filters out only the targets
1268/// that are automatically included when the user doesn't specify any targets.
45d49579 1269fn filter_default_targets(targets: &[Target], mode: CompileMode) -> Vec<&Target> {
575d6e81
EH
1270 match mode {
1271 CompileMode::Bench => targets.iter().filter(|t| t.benched()).collect(),
a4947c2b
EH
1272 CompileMode::Test => targets
1273 .iter()
1274 .filter(|t| t.tested() || t.is_example())
1275 .collect(),
575d6e81
EH
1276 CompileMode::Build | CompileMode::Check { .. } => targets
1277 .iter()
1278 .filter(|t| t.is_bin() || t.is_lib())
1279 .collect(),
1280 CompileMode::Doc { .. } => {
1281 // `doc` does lib and bins (bin with same name as lib is skipped).
1282 targets
1e682848 1283 .iter()
575d6e81
EH
1284 .filter(|t| {
1285 t.documented()
1286 && (!t.is_bin()
1287 || !targets.iter().any(|l| l.is_lib() && l.name() == t.name()))
1288 })
1289 .collect()
2a82378a 1290 }
00d325db 1291 CompileMode::Doctest | CompileMode::RunCustomBuild => panic!("Invalid mode {:?}", mode),
2a82378a 1292 }
2a82378a
BW
1293}
1294
df9ca871 1295/// Returns a list of proposed targets based on command-line target selection flags.
575d6e81 1296fn list_rule_targets<'a>(
fbd34a69 1297 packages: &[&'a Package],
575d6e81
EH
1298 rule: &FilterRule,
1299 target_desc: &'static str,
1300 is_expected_kind: fn(&Target) -> bool,
771fec3c 1301 mode: CompileMode,
3a1cad6f 1302) -> CargoResult<Vec<Proposal<'a>>> {
df9ca871
DW
1303 let mut proposals = Vec::new();
1304 match rule {
771fec3c 1305 FilterRule::All => {
df9ca871 1306 proposals.extend(filter_targets(packages, is_expected_kind, false, mode))
771fec3c 1307 }
df9ca871 1308 FilterRule::Just(names) => {
771fec3c 1309 for name in names {
df9ca871 1310 proposals.extend(find_named_targets(
771fec3c
EH
1311 packages,
1312 name,
1313 target_desc,
1314 is_expected_kind,
1315 mode,
1316 )?);
1317 }
1318 }
575d6e81 1319 }
df9ca871 1320 Ok(proposals)
575d6e81 1321}
235712f5 1322
f7c91ba6 1323/// Finds the targets for a specifically named target.
771fec3c 1324fn find_named_targets<'a>(
fbd34a69 1325 packages: &[&'a Package],
575d6e81
EH
1326 target_name: &str,
1327 target_desc: &'static str,
1328 is_expected_kind: fn(&Target) -> bool,
771fec3c 1329 mode: CompileMode,
3a1cad6f 1330) -> CargoResult<Vec<Proposal<'a>>> {
42696ae2
WL
1331 let is_glob = is_glob_pattern(target_name);
1332 let proposals = if is_glob {
1333 let pattern = build_glob(target_name)?;
1334 let filter = |t: &Target| is_expected_kind(t) && pattern.matches(t.name());
1335 filter_targets(packages, filter, true, mode)
1336 } else {
1337 let filter = |t: &Target| t.name() == target_name && is_expected_kind(t);
1338 filter_targets(packages, filter, true, mode)
1339 };
1340
df9ca871 1341 if proposals.is_empty() {
7d7fe679
EH
1342 let targets = packages.iter().flat_map(|pkg| {
1343 pkg.targets()
1344 .iter()
1345 .filter(|target| is_expected_kind(target))
1346 });
1347 let suggestion = closest_msg(target_name, targets, |t| t.name());
3a18c89a 1348 anyhow::bail!(
42696ae2 1349 "no {} target {} `{}`{}",
7d7fe679 1350 target_desc,
42696ae2 1351 if is_glob { "matches pattern" } else { "named" },
7d7fe679
EH
1352 target_name,
1353 suggestion
1354 );
771fec3c 1355 }
df9ca871
DW
1356 Ok(proposals)
1357}
1358
1359fn filter_targets<'a>(
fbd34a69 1360 packages: &[&'a Package],
df9ca871
DW
1361 predicate: impl Fn(&Target) -> bool,
1362 requires_features: bool,
1363 mode: CompileMode,
1364) -> Vec<Proposal<'a>> {
1365 let mut proposals = Vec::new();
1366 for pkg in packages {
1367 for target in pkg.targets().iter().filter(|t| predicate(t)) {
1368 proposals.push(Proposal {
1369 pkg,
1370 target,
1371 requires_features,
1372 mode,
1373 });
1374 }
1375 }
1376 proposals
9e779198 1377}
9efa0d55
EH
1378
1379/// This is used to rebuild the unit graph, sharing host dependencies if possible.
1380///
1381/// This will translate any unit's `CompileKind::Target(host)` to
1382/// `CompileKind::Host` if the kind is equal to `to_host`. This also handles
1383/// generating the unit `dep_hash`, and merging shared units if possible.
1384///
1385/// This is necessary because if normal dependencies used `CompileKind::Host`,
1386/// there would be no way to distinguish those units from build-dependency
1387/// units. This can cause a problem if a shared normal/build dependency needs
1388/// to link to another dependency whose features differ based on whether or
1389/// not it is a normal or build dependency. If both units used
1390/// `CompileKind::Host`, then they would end up being identical, causing a
1391/// collision in the `UnitGraph`, and Cargo would end up randomly choosing one
1392/// value or the other.
1393///
1394/// The solution is to keep normal and build dependencies separate when
1395/// building the unit graph, and then run this second pass which will try to
1396/// combine shared dependencies safely. By adding a hash of the dependencies
1397/// to the `Unit`, this allows the `CompileKind` to be changed back to `Host`
1398/// without fear of an unwanted collision.
1399fn rebuild_unit_graph_shared(
1400 interner: &UnitInterner,
1401 unit_graph: UnitGraph,
1402 roots: &[Unit],
1403 to_host: CompileKind,
1404) -> (Vec<Unit>, UnitGraph) {
1405 let mut result = UnitGraph::new();
1406 // Map of the old unit to the new unit, used to avoid recursing into units
1407 // that have already been computed to improve performance.
1408 let mut memo = HashMap::new();
1409 let new_roots = roots
1410 .iter()
1411 .map(|root| {
1412 traverse_and_share(interner, &mut memo, &mut result, &unit_graph, root, to_host)
1413 })
1414 .collect();
1415 (new_roots, result)
1416}
1417
1418/// Recursive function for rebuilding the graph.
1419///
1420/// This walks `unit_graph`, starting at the given `unit`. It inserts the new
1421/// units into `new_graph`, and returns a new updated version of the given
1422/// unit (`dep_hash` is filled in, and `kind` switched if necessary).
1423fn traverse_and_share(
1424 interner: &UnitInterner,
1425 memo: &mut HashMap<Unit, Unit>,
1426 new_graph: &mut UnitGraph,
1427 unit_graph: &UnitGraph,
1428 unit: &Unit,
1429 to_host: CompileKind,
1430) -> Unit {
1431 if let Some(new_unit) = memo.get(unit) {
1432 // Already computed, no need to recompute.
1433 return new_unit.clone();
1434 }
1435 let mut dep_hash = StableHasher::new();
1436 let new_deps: Vec<_> = unit_graph[unit]
1437 .iter()
1438 .map(|dep| {
1439 let new_dep_unit =
1440 traverse_and_share(interner, memo, new_graph, unit_graph, &dep.unit, to_host);
1441 new_dep_unit.hash(&mut dep_hash);
1442 UnitDep {
1443 unit: new_dep_unit,
1444 ..dep.clone()
1445 }
1446 })
1447 .collect();
1448 let new_dep_hash = dep_hash.finish();
1449 let new_kind = if unit.kind == to_host {
1450 CompileKind::Host
1451 } else {
1452 unit.kind
1453 };
1454 let new_unit = interner.intern(
1455 &unit.pkg,
1456 &unit.target,
1457 unit.profile,
1458 new_kind,
1459 unit.mode,
1460 unit.features.clone(),
1461 unit.is_std,
1462 new_dep_hash,
1463 );
1464 assert!(memo.insert(unit.clone(), new_unit.clone()).is_none());
1465 new_graph.entry(new_unit.clone()).or_insert(new_deps);
1466 new_unit
1467}
42696ae2 1468
2361fb0f 1469/// Build `glob::Pattern` with informative context.
42696ae2 1470fn build_glob(pat: &str) -> CargoResult<glob::Pattern> {
5e3dc467 1471 glob::Pattern::new(pat).with_context(|| format!("cannot build glob pattern from `{}`", pat))
42696ae2 1472}
2361fb0f
WL
1473
1474/// Emits "package not found" error.
1475///
1476/// > This function should be used only in package selection processes such like
1477/// `Packages::to_package_id_specs` and `Packages::get_packages`.
1478fn emit_package_not_found(
1479 ws: &Workspace<'_>,
1480 opt_names: BTreeSet<&str>,
1481 opt_out: bool,
1482) -> CargoResult<()> {
1483 if !opt_names.is_empty() {
1484 anyhow::bail!(
5e3dc467 1485 "{}package(s) `{}` not found in workspace `{}`",
2361fb0f 1486 if opt_out { "excluded " } else { "" },
3d042688 1487 opt_names.into_iter().collect::<Vec<_>>().join(", "),
2361fb0f
WL
1488 ws.root().display(),
1489 )
1490 }
1491 Ok(())
1492}
1493
1494/// Emits "glob pattern not found" error.
1495///
1496/// > This function should be used only in package selection processes such like
1497/// `Packages::to_package_id_specs` and `Packages::get_packages`.
1498fn emit_pattern_not_found(
1499 ws: &Workspace<'_>,
1500 opt_patterns: Vec<(glob::Pattern, bool)>,
1501 opt_out: bool,
1502) -> CargoResult<()> {
1503 let not_matched = opt_patterns
1504 .iter()
1505 .filter(|(_, matched)| !*matched)
1506 .map(|(pat, _)| pat.as_str())
1507 .collect::<Vec<_>>();
1508 if !not_matched.is_empty() {
1509 anyhow::bail!(
5e3dc467 1510 "{}package pattern(s) `{}` not found in workspace `{}`",
2361fb0f
WL
1511 if opt_out { "excluded " } else { "" },
1512 not_matched.join(", "),
1513 ws.root().display(),
1514 )
1515 }
1516 Ok(())
1517}
1518
1519/// Checks whether a package matches any of a list of glob patterns generated
1520/// from `opt_patterns_and_names`.
1521///
1522/// > This function should be used only in package selection processes such like
1523/// `Packages::to_package_id_specs` and `Packages::get_packages`.
1524fn match_patterns(pkg: &Package, patterns: &mut Vec<(glob::Pattern, bool)>) -> bool {
1525 patterns.iter_mut().any(|(m, matched)| {
1526 let is_matched = m.matches(pkg.name().as_str());
1527 *matched |= is_matched;
1528 is_matched
1529 })
1530}
1531
1532/// Given a list opt-in or opt-out package selection strings, generates two
1533/// collections that represent glob patterns and package names respectively.
1534///
1535/// > This function should be used only in package selection processes such like
1536/// `Packages::to_package_id_specs` and `Packages::get_packages`.
1537fn opt_patterns_and_names(
1538 opt: &[String],
1539) -> CargoResult<(Vec<(glob::Pattern, bool)>, BTreeSet<&str>)> {
1540 let mut opt_patterns = Vec::new();
1541 let mut opt_names = BTreeSet::new();
1542 for x in opt.iter() {
1543 if is_glob_pattern(x) {
1544 opt_patterns.push((build_glob(x)?, false));
1545 } else {
1546 opt_names.insert(String::as_str(x));
1547 }
1548 }
1549 Ok((opt_patterns, opt_names))
1550}
a58b0c58
EH
1551
1552/// Removes duplicate CompileMode::Doc units that would cause problems with
1553/// filename collisions.
1554///
1555/// Rustdoc only separates units by crate name in the file directory
1556/// structure. If any two units with the same crate name exist, this would
1557/// cause a filename collision, causing different rustdoc invocations to stomp
1558/// on one another's files.
1559///
1560/// Unfortunately this does not remove all duplicates, as some of them are
1561/// either user error, or difficult to remove. Cases that I can think of:
1562///
1563/// - Same target name in different packages. See the `collision_doc` test.
1564/// - Different sources. See `collision_doc_sources` test.
1565///
1566/// Ideally this would not be necessary.
5ebb605c
EH
1567fn remove_duplicate_doc(
1568 build_config: &BuildConfig,
1569 root_units: &[Unit],
1570 unit_graph: &mut UnitGraph,
1571) {
a58b0c58
EH
1572 // First, create a mapping of crate_name -> Unit so we can see where the
1573 // duplicates are.
1574 let mut all_docs: HashMap<String, Vec<Unit>> = HashMap::new();
1575 for unit in unit_graph.keys() {
1576 if unit.mode.is_doc() {
1577 all_docs
1578 .entry(unit.target.crate_name())
1579 .or_default()
1580 .push(unit.clone());
1581 }
1582 }
c5e3f17f
EH
1583 // Keep track of units to remove so that they can be efficiently removed
1584 // from the unit_deps.
1585 let mut removed_units: HashSet<Unit> = HashSet::new();
e50292a1
EH
1586 let mut remove = |units: Vec<Unit>, reason: &str, cb: &dyn Fn(&Unit) -> bool| -> Vec<Unit> {
1587 let (to_remove, remaining_units): (Vec<Unit>, Vec<Unit>) = units
1588 .into_iter()
1589 .partition(|unit| cb(unit) && !root_units.contains(unit));
1590 for unit in to_remove {
a58b0c58
EH
1591 log::debug!(
1592 "removing duplicate doc due to {} for package {} target `{}`",
1593 reason,
1594 unit.pkg,
1595 unit.target.name()
1596 );
c5e3f17f
EH
1597 unit_graph.remove(&unit);
1598 removed_units.insert(unit);
a58b0c58 1599 }
e50292a1 1600 remaining_units
a58b0c58
EH
1601 };
1602 // Iterate over the duplicates and try to remove them from unit_graph.
1603 for (_crate_name, mut units) in all_docs {
1604 if units.len() == 1 {
1605 continue;
1606 }
1607 // Prefer target over host if --target was not specified.
1608 if build_config
1609 .requested_kinds
1610 .iter()
1611 .all(CompileKind::is_host)
1612 {
a58b0c58
EH
1613 // Note these duplicates may not be real duplicates, since they
1614 // might get merged in rebuild_unit_graph_shared. Either way, it
1615 // shouldn't hurt to remove them early (although the report in the
1616 // log might be confusing).
e50292a1 1617 units = remove(units, "host/target merger", &|unit| unit.kind.is_host());
a58b0c58
EH
1618 if units.len() == 1 {
1619 continue;
1620 }
1621 }
1622 // Prefer newer versions over older.
1623 let mut source_map: HashMap<(InternedString, SourceId, CompileKind), Vec<Unit>> =
1624 HashMap::new();
1625 for unit in units {
1626 let pkg_id = unit.pkg.package_id();
1627 // Note, this does not detect duplicates from different sources.
1628 source_map
1629 .entry((pkg_id.name(), pkg_id.source_id(), unit.kind))
1630 .or_default()
1631 .push(unit);
1632 }
1633 let mut remaining_units = Vec::new();
1634 for (_key, mut units) in source_map {
1635 if units.len() > 1 {
1636 units.sort_by(|a, b| a.pkg.version().partial_cmp(b.pkg.version()).unwrap());
1637 // Remove any entries with version < newest.
1638 let newest_version = units.last().unwrap().pkg.version().clone();
e50292a1
EH
1639 let keep_units = remove(units, "older version", &|unit| {
1640 unit.pkg.version() < &newest_version
1641 });
a58b0c58
EH
1642 remaining_units.extend(keep_units);
1643 } else {
1644 remaining_units.extend(units);
1645 }
1646 }
1647 if remaining_units.len() == 1 {
1648 continue;
1649 }
1650 // Are there other heuristics to remove duplicates that would make
1651 // sense? Maybe prefer path sources over all others?
1652 }
c5e3f17f
EH
1653 // Also remove units from the unit_deps so there aren't any dangling edges.
1654 for unit_deps in unit_graph.values_mut() {
1655 unit_deps.retain(|unit_dep| !removed_units.contains(&unit_dep.unit));
1656 }
5ebb605c
EH
1657 // Remove any orphan units that were detached from the graph.
1658 let mut visited = HashSet::new();
1659 fn visit(unit: &Unit, graph: &UnitGraph, visited: &mut HashSet<Unit>) {
1660 if !visited.insert(unit.clone()) {
1661 return;
1662 }
1663 for dep in &graph[unit] {
1664 visit(&dep.unit, graph, visited);
1665 }
1666 }
1667 for unit in root_units {
1668 visit(unit, unit_graph, &mut visited);
1669 }
1670 unit_graph.retain(|unit, _| visited.contains(unit));
a58b0c58 1671}