]>
Commit | Line | Data |
---|---|---|
2599c34d | 1 | //! The Cargo "compile" operation. |
64ff29ff | 2 | //! |
2599c34d EH |
3 | //! This module contains the entry point for starting the compilation process |
4 | //! for commands like `build`, `test`, `doc`, `rustc`, etc. | |
64ff29ff | 5 | //! |
2599c34d EH |
6 | //! The `compile` function will do all the work to compile a workspace. A |
7 | //! rough outline is: | |
64ff29ff | 8 | //! |
2599c34d | 9 | //! - Resolve the dependency graph (see `ops::resolve`). |
e0bd9e23 | 10 | //! - Download any packages needed (see `PackageSet`). |
2599c34d EH |
11 | //! - Generate a list of top-level "units" of work for the targets the user |
12 | //! requested on the command-line. Each `Unit` corresponds to a compiler | |
13 | //! invocation. This is done in this module (`generate_targets`). | |
e0bd9e23 EH |
14 | //! - Build the graph of `Unit` dependencies (see |
15 | //! `core::compiler::context::unit_dependencies`). | |
2599c34d | 16 | //! - Create a `Context` which will perform the following steps: |
2599c34d EH |
17 | //! - Prepare the `target` directory (see `Layout`). |
18 | //! - Create a job queue (see `JobQueue`). The queue checks the | |
19 | //! fingerprint of each `Unit` to determine if it should run or be | |
20 | //! skipped. | |
21 | //! - Execute the queue. Each leaf in the queue's dependency graph is | |
22 | //! executed, and then removed from the graph when finished. This | |
23 | //! repeats until the queue is empty. | |
62bff631 | 24 | |
45ce445c | 25 | use std::collections::{BTreeSet, HashMap, HashSet}; |
9efa0d55 | 26 | use std::hash::{Hash, Hasher}; |
1ab19a5a | 27 | use std::sync::Arc; |
12af54e1 | 28 | |
1f14fa31 | 29 | use crate::core::compiler::unit_dependencies::build_unit_dependencies; |
9efa0d55 | 30 | use crate::core::compiler::unit_graph::{self, UnitDep, UnitGraph}; |
39fb01c2 | 31 | use crate::core::compiler::{standard_lib, TargetInfo}; |
e4544466 | 32 | use crate::core::compiler::{BuildConfig, BuildContext, Compilation, Context}; |
39fb01c2 | 33 | use crate::core::compiler::{CompileKind, CompileMode, CompileTarget, RustcTargetData, Unit}; |
e4544466 | 34 | use crate::core::compiler::{DefaultExecutor, Executor, UnitInterner}; |
04ddd4d0 | 35 | use crate::core::profiles::{Profiles, UnitFor}; |
85854b18 EH |
36 | use crate::core::resolver::features::{self, CliFeatures, FeaturesFor}; |
37 | use crate::core::resolver::{HasDevUnits, Resolve}; | |
19a95790 | 38 | use crate::core::{FeatureValue, Package, PackageSet, Shell, Summary, Target}; |
f32f72f4 | 39 | use crate::core::{PackageId, PackageIdSpec, SourceId, TargetKind, Workspace}; |
d7034c60 | 40 | use crate::drop_println; |
04ddd4d0 | 41 | use crate::ops; |
12392f6d | 42 | use crate::ops::resolve::WorkspaceResolve; |
04ddd4d0 | 43 | use crate::util::config::Config; |
a58b0c58 | 44 | use crate::util::interning::InternedString; |
42696ae2 | 45 | use crate::util::restricted_names::is_glob_pattern; |
9efa0d55 | 46 | use crate::util::{closest_msg, profile, CargoResult, StableHasher}; |
50f110a4 | 47 | |
42696ae2 WL |
48 | use 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 | 59 | pub 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 |
84 | impl<'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)] |
103 | pub enum Packages { | |
ba7911dd | 104 | Default, |
62c04979 | 105 | All, |
bb643cca AK |
106 | OptOut(Vec<String>), |
107 | Packages(Vec<String>), | |
62c04979 AR |
108 | } |
109 | ||
bb643cca | 110 | impl 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)] |
229 | pub 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)] |
239 | pub enum FilterRule { | |
2a82378a | 240 | All, |
bb643cca | 241 | Just(Vec<String>), |
2a82378a BW |
242 | } |
243 | ||
876af7c1 | 244 | #[derive(Debug)] |
bb643cca | 245 | pub 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 | 260 | pub 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 |
267 | pub 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 |
276 | pub 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 |
292 | pub 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 |
323 | pub 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 |
629 | impl 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 | 671 | impl 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)] | |
833 | struct 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 | 846 | fn 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. | |
1146 | fn 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 | 1234 | pub 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 | 1269 | fn 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 | 1296 | fn 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 | 1324 | fn 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 | ||
1359 | fn 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. | |
1399 | fn 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). | |
1423 | fn 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 | 1470 | fn 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`. | |
1478 | fn 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`. | |
1498 | fn 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`. | |
1524 | fn 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`. | |
1537 | fn 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 |
1567 | fn 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 | } |