]>
Commit | Line | Data |
---|---|---|
fe692bf9 | 1 | //! Handles built-in and customizable compiler flag presets. |
0a29b90c FG |
2 | //! |
3 | //! [`Profiles`] is a collections of built-in profiles, and profiles defined | |
4 | //! in the root manifest and configurations. | |
5 | //! | |
6 | //! To start using a profile, most of the time you start from [`Profiles::new`], | |
7 | //! which does the followings: | |
8 | //! | |
9 | //! - Create a `Profiles` by merging profiles from configs onto the profile | |
10 | //! from root manifest (see [`merge_config_profiles`]). | |
11 | //! - Add built-in profiles onto it (see [`Profiles::add_root_profiles`]). | |
12 | //! - Process profile inheritance for each profiles. (see [`Profiles::add_maker`]). | |
13 | //! | |
14 | //! Then you can query a [`Profile`] via [`Profiles::get_profile`], which respects | |
15 | //! the profile overridden hierarchy described in below. The [`Profile`] you get | |
16 | //! is basically an immutable struct containing the compiler flag presets. | |
17 | //! | |
18 | //! ## Profile overridden hierarchy | |
19 | //! | |
20 | //! Profile settings can be overridden for specific packages and build-time crates. | |
21 | //! The precedence is explained in [`ProfileMaker`]. | |
22 | //! The algorithm happens within [`ProfileMaker::get_profile`]. | |
23 | ||
24 | use crate::core::compiler::{CompileKind, CompileTarget, Unit}; | |
25 | use crate::core::dependency::Artifact; | |
26 | use crate::core::resolver::features::FeaturesFor; | |
27 | use crate::core::{PackageId, PackageIdSpec, Resolve, Shell, Target, Workspace}; | |
28 | use crate::util::interning::InternedString; | |
49aad941 FG |
29 | use crate::util::toml::{ |
30 | ProfilePackageSpec, StringOrBool, TomlDebugInfo, TomlProfile, TomlProfiles, | |
31 | }; | |
0a29b90c FG |
32 | use crate::util::{closest_msg, config, CargoResult, Config}; |
33 | use anyhow::{bail, Context as _}; | |
34 | use std::collections::{BTreeMap, HashMap, HashSet}; | |
35 | use std::hash::Hash; | |
36 | use std::{cmp, fmt, hash}; | |
37 | ||
38 | /// Collection of all profiles. | |
39 | /// | |
40 | /// To get a specific [`Profile`], you usually create this and call [`get_profile`] then. | |
41 | /// | |
42 | /// [`get_profile`]: Profiles::get_profile | |
43 | #[derive(Clone, Debug)] | |
44 | pub struct Profiles { | |
45 | /// Incremental compilation can be overridden globally via: | |
46 | /// - `CARGO_INCREMENTAL` environment variable. | |
47 | /// - `build.incremental` config value. | |
48 | incremental: Option<bool>, | |
49 | /// Map of profile name to directory name for that profile. | |
50 | dir_names: HashMap<InternedString, InternedString>, | |
51 | /// The profile makers. Key is the profile name. | |
52 | by_name: HashMap<InternedString, ProfileMaker>, | |
53 | /// The original profiles written by the user in the manifest and config. | |
54 | /// | |
55 | /// This is here to assist with error reporting, as the `ProfileMaker` | |
56 | /// values have the inherits chains all merged together. | |
57 | original_profiles: BTreeMap<InternedString, TomlProfile>, | |
58 | /// The profile the user requested to use. | |
59 | requested_profile: InternedString, | |
60 | /// The host target for rustc being used by this `Profiles`. | |
61 | rustc_host: InternedString, | |
62 | } | |
63 | ||
64 | impl Profiles { | |
65 | pub fn new(ws: &Workspace<'_>, requested_profile: InternedString) -> CargoResult<Profiles> { | |
66 | let config = ws.config(); | |
67 | let incremental = match config.get_env_os("CARGO_INCREMENTAL") { | |
68 | Some(v) => Some(v == "1"), | |
69 | None => config.build_config()?.incremental, | |
70 | }; | |
71 | let mut profiles = merge_config_profiles(ws, requested_profile)?; | |
72 | let rustc_host = ws.config().load_global_rustc(Some(ws))?.host; | |
73 | ||
74 | let mut profile_makers = Profiles { | |
75 | incremental, | |
76 | dir_names: Self::predefined_dir_names(), | |
77 | by_name: HashMap::new(), | |
78 | original_profiles: profiles.clone(), | |
79 | requested_profile, | |
80 | rustc_host, | |
81 | }; | |
82 | ||
83 | Self::add_root_profiles(&mut profile_makers, &profiles); | |
84 | ||
85 | // Merge with predefined profiles. | |
86 | use std::collections::btree_map::Entry; | |
87 | for (predef_name, mut predef_prof) in Self::predefined_profiles().into_iter() { | |
88 | match profiles.entry(InternedString::new(predef_name)) { | |
89 | Entry::Vacant(vac) => { | |
90 | vac.insert(predef_prof); | |
91 | } | |
92 | Entry::Occupied(mut oc) => { | |
93 | // Override predefined with the user-provided Toml. | |
94 | let r = oc.get_mut(); | |
95 | predef_prof.merge(r); | |
96 | *r = predef_prof; | |
97 | } | |
98 | } | |
99 | } | |
100 | ||
101 | for (name, profile) in &profiles { | |
102 | profile_makers.add_maker(*name, profile, &profiles)?; | |
103 | } | |
104 | // Verify that the requested profile is defined *somewhere*. | |
105 | // This simplifies the API (no need for CargoResult), and enforces | |
106 | // assumptions about how config profiles are loaded. | |
107 | profile_makers.get_profile_maker(requested_profile)?; | |
108 | Ok(profile_makers) | |
109 | } | |
110 | ||
111 | /// Returns the hard-coded directory names for built-in profiles. | |
112 | fn predefined_dir_names() -> HashMap<InternedString, InternedString> { | |
113 | [ | |
114 | (InternedString::new("dev"), InternedString::new("debug")), | |
115 | (InternedString::new("test"), InternedString::new("debug")), | |
116 | (InternedString::new("bench"), InternedString::new("release")), | |
117 | ] | |
118 | .into() | |
119 | } | |
120 | ||
121 | /// Initialize `by_name` with the two "root" profiles, `dev`, and | |
122 | /// `release` given the user's definition. | |
123 | fn add_root_profiles( | |
124 | profile_makers: &mut Profiles, | |
125 | profiles: &BTreeMap<InternedString, TomlProfile>, | |
126 | ) { | |
127 | profile_makers.by_name.insert( | |
128 | InternedString::new("dev"), | |
129 | ProfileMaker::new(Profile::default_dev(), profiles.get("dev").cloned()), | |
130 | ); | |
131 | ||
132 | profile_makers.by_name.insert( | |
133 | InternedString::new("release"), | |
134 | ProfileMaker::new(Profile::default_release(), profiles.get("release").cloned()), | |
135 | ); | |
136 | } | |
137 | ||
138 | /// Returns the built-in profiles (not including dev/release, which are | |
139 | /// "root" profiles). | |
140 | fn predefined_profiles() -> Vec<(&'static str, TomlProfile)> { | |
141 | vec![ | |
142 | ( | |
143 | "bench", | |
144 | TomlProfile { | |
145 | inherits: Some(InternedString::new("release")), | |
146 | ..TomlProfile::default() | |
147 | }, | |
148 | ), | |
149 | ( | |
150 | "test", | |
151 | TomlProfile { | |
152 | inherits: Some(InternedString::new("dev")), | |
153 | ..TomlProfile::default() | |
154 | }, | |
155 | ), | |
156 | ( | |
157 | "doc", | |
158 | TomlProfile { | |
159 | inherits: Some(InternedString::new("dev")), | |
160 | ..TomlProfile::default() | |
161 | }, | |
162 | ), | |
163 | ] | |
164 | } | |
165 | ||
166 | /// Creates a `ProfileMaker`, and inserts it into `self.by_name`. | |
167 | fn add_maker( | |
168 | &mut self, | |
169 | name: InternedString, | |
170 | profile: &TomlProfile, | |
171 | profiles: &BTreeMap<InternedString, TomlProfile>, | |
172 | ) -> CargoResult<()> { | |
173 | match &profile.dir_name { | |
174 | None => {} | |
175 | Some(dir_name) => { | |
176 | self.dir_names.insert(name, dir_name.to_owned()); | |
177 | } | |
178 | } | |
179 | ||
180 | // dev/release are "roots" and don't inherit. | |
181 | if name == "dev" || name == "release" { | |
182 | if profile.inherits.is_some() { | |
183 | bail!( | |
184 | "`inherits` must not be specified in root profile `{}`", | |
185 | name | |
186 | ); | |
187 | } | |
188 | // Already inserted from `add_root_profiles`, no need to do anything. | |
189 | return Ok(()); | |
190 | } | |
191 | ||
192 | // Keep track for inherits cycles. | |
193 | let mut set = HashSet::new(); | |
194 | set.insert(name); | |
195 | let maker = self.process_chain(name, profile, &mut set, profiles)?; | |
196 | self.by_name.insert(name, maker); | |
197 | Ok(()) | |
198 | } | |
199 | ||
200 | /// Build a `ProfileMaker` by recursively following the `inherits` setting. | |
201 | /// | |
202 | /// * `name`: The name of the profile being processed. | |
203 | /// * `profile`: The TOML profile being processed. | |
204 | /// * `set`: Set of profiles that have been visited, used to detect cycles. | |
205 | /// * `profiles`: Map of all TOML profiles. | |
206 | /// | |
207 | /// Returns a `ProfileMaker` to be used for the given named profile. | |
208 | fn process_chain( | |
209 | &mut self, | |
210 | name: InternedString, | |
211 | profile: &TomlProfile, | |
212 | set: &mut HashSet<InternedString>, | |
213 | profiles: &BTreeMap<InternedString, TomlProfile>, | |
214 | ) -> CargoResult<ProfileMaker> { | |
215 | let mut maker = match profile.inherits { | |
216 | Some(inherits_name) if inherits_name == "dev" || inherits_name == "release" => { | |
217 | // These are the root profiles added in `add_root_profiles`. | |
218 | self.get_profile_maker(inherits_name).unwrap().clone() | |
219 | } | |
220 | Some(inherits_name) => { | |
221 | if !set.insert(inherits_name) { | |
222 | bail!( | |
223 | "profile inheritance loop detected with profile `{}` inheriting `{}`", | |
224 | name, | |
225 | inherits_name | |
226 | ); | |
227 | } | |
228 | ||
229 | match profiles.get(&inherits_name) { | |
230 | None => { | |
231 | bail!( | |
232 | "profile `{}` inherits from `{}`, but that profile is not defined", | |
233 | name, | |
234 | inherits_name | |
235 | ); | |
236 | } | |
237 | Some(parent) => self.process_chain(inherits_name, parent, set, profiles)?, | |
238 | } | |
239 | } | |
240 | None => { | |
241 | bail!( | |
242 | "profile `{}` is missing an `inherits` directive \ | |
243 | (`inherits` is required for all profiles except `dev` or `release`)", | |
244 | name | |
245 | ); | |
246 | } | |
247 | }; | |
248 | match &mut maker.toml { | |
249 | Some(toml) => toml.merge(profile), | |
250 | None => maker.toml = Some(profile.clone()), | |
251 | }; | |
252 | Ok(maker) | |
253 | } | |
254 | ||
255 | /// Retrieves the profile for a target. | |
256 | /// `is_member` is whether or not this package is a member of the | |
257 | /// workspace. | |
258 | pub fn get_profile( | |
259 | &self, | |
260 | pkg_id: PackageId, | |
261 | is_member: bool, | |
262 | is_local: bool, | |
263 | unit_for: UnitFor, | |
264 | kind: CompileKind, | |
265 | ) -> Profile { | |
266 | let maker = self.get_profile_maker(self.requested_profile).unwrap(); | |
267 | let mut profile = maker.get_profile(Some(pkg_id), is_member, unit_for.is_for_host()); | |
268 | ||
269 | // Dealing with `panic=abort` and `panic=unwind` requires some special | |
270 | // treatment. Be sure to process all the various options here. | |
271 | match unit_for.panic_setting() { | |
272 | PanicSetting::AlwaysUnwind => profile.panic = PanicStrategy::Unwind, | |
273 | PanicSetting::ReadProfile => {} | |
274 | } | |
275 | ||
276 | // Default macOS debug information to being stored in the "unpacked" | |
277 | // split-debuginfo format. At the time of this writing that's the only | |
278 | // platform which has a stable `-Csplit-debuginfo` option for rustc, | |
279 | // and it's typically much faster than running `dsymutil` on all builds | |
280 | // in incremental cases. | |
49aad941 FG |
281 | if profile.debuginfo.is_turned_on() && profile.split_debuginfo.is_none() { |
282 | let target = match &kind { | |
283 | CompileKind::Host => self.rustc_host.as_str(), | |
284 | CompileKind::Target(target) => target.short_name(), | |
285 | }; | |
286 | if target.contains("-apple-") { | |
287 | profile.split_debuginfo = Some(InternedString::new("unpacked")); | |
0a29b90c FG |
288 | } |
289 | } | |
290 | ||
291 | // Incremental can be globally overridden. | |
292 | if let Some(v) = self.incremental { | |
293 | profile.incremental = v; | |
294 | } | |
295 | ||
296 | // Only enable incremental compilation for sources the user can | |
297 | // modify (aka path sources). For things that change infrequently, | |
298 | // non-incremental builds yield better performance in the compiler | |
299 | // itself (aka crates.io / git dependencies) | |
300 | // | |
301 | // (see also https://github.com/rust-lang/cargo/issues/3972) | |
302 | if !is_local { | |
303 | profile.incremental = false; | |
304 | } | |
305 | profile.name = self.requested_profile; | |
306 | profile | |
307 | } | |
308 | ||
309 | /// The profile for *running* a `build.rs` script is only used for setting | |
310 | /// a few environment variables. To ensure proper de-duplication of the | |
311 | /// running `Unit`, this uses a stripped-down profile (so that unrelated | |
312 | /// profile flags don't cause `build.rs` to needlessly run multiple | |
313 | /// times). | |
314 | pub fn get_profile_run_custom_build(&self, for_unit_profile: &Profile) -> Profile { | |
315 | let mut result = Profile::default(); | |
316 | result.name = for_unit_profile.name; | |
317 | result.root = for_unit_profile.root; | |
318 | result.debuginfo = for_unit_profile.debuginfo; | |
319 | result.opt_level = for_unit_profile.opt_level; | |
320 | result | |
321 | } | |
322 | ||
323 | /// This returns the base profile. This is currently used for the | |
324 | /// `[Finished]` line. It is not entirely accurate, since it doesn't | |
325 | /// select for the package that was actually built. | |
326 | pub fn base_profile(&self) -> Profile { | |
327 | let profile_name = self.requested_profile; | |
328 | let maker = self.get_profile_maker(profile_name).unwrap(); | |
329 | maker.get_profile(None, /*is_member*/ true, /*is_for_host*/ false) | |
330 | } | |
331 | ||
332 | /// Gets the directory name for a profile, like `debug` or `release`. | |
333 | pub fn get_dir_name(&self) -> InternedString { | |
334 | *self | |
335 | .dir_names | |
336 | .get(&self.requested_profile) | |
337 | .unwrap_or(&self.requested_profile) | |
338 | } | |
339 | ||
340 | /// Used to check for overrides for non-existing packages. | |
341 | pub fn validate_packages( | |
342 | &self, | |
343 | profiles: Option<&TomlProfiles>, | |
344 | shell: &mut Shell, | |
345 | resolve: &Resolve, | |
346 | ) -> CargoResult<()> { | |
347 | for (name, profile) in &self.by_name { | |
348 | // If the user did not specify an override, skip this. This is here | |
349 | // to avoid generating errors for inherited profiles which don't | |
350 | // specify package overrides. The `by_name` profile has had the inherits | |
351 | // chain merged, so we need to look at the original source to check | |
352 | // if an override was specified. | |
353 | if self | |
354 | .original_profiles | |
355 | .get(name) | |
356 | .and_then(|orig| orig.package.as_ref()) | |
357 | .is_none() | |
358 | { | |
359 | continue; | |
360 | } | |
361 | let found = validate_packages_unique(resolve, name, &profile.toml)?; | |
362 | // We intentionally do not validate unmatched packages for config | |
363 | // profiles, in case they are defined in a central location. This | |
364 | // iterates over the manifest profiles only. | |
365 | if let Some(profiles) = profiles { | |
366 | if let Some(toml_profile) = profiles.get(name) { | |
367 | validate_packages_unmatched(shell, resolve, name, toml_profile, &found)?; | |
368 | } | |
369 | } | |
370 | } | |
371 | Ok(()) | |
372 | } | |
373 | ||
374 | /// Returns the profile maker for the given profile name. | |
375 | fn get_profile_maker(&self, name: InternedString) -> CargoResult<&ProfileMaker> { | |
376 | self.by_name | |
377 | .get(&name) | |
378 | .ok_or_else(|| anyhow::format_err!("profile `{}` is not defined", name)) | |
379 | } | |
380 | } | |
381 | ||
382 | /// An object used for handling the profile hierarchy. | |
383 | /// | |
384 | /// The precedence of profiles are (first one wins): | |
385 | /// | |
386 | /// - Profiles in `.cargo/config` files (using same order as below). | |
387 | /// - `[profile.dev.package.name]` -- a named package. | |
388 | /// - `[profile.dev.package."*"]` -- this cannot apply to workspace members. | |
389 | /// - `[profile.dev.build-override]` -- this can only apply to `build.rs` scripts | |
390 | /// and their dependencies. | |
391 | /// - `[profile.dev]` | |
392 | /// - Default (hard-coded) values. | |
393 | #[derive(Debug, Clone)] | |
394 | struct ProfileMaker { | |
395 | /// The starting, hard-coded defaults for the profile. | |
396 | default: Profile, | |
397 | /// The TOML profile defined in `Cargo.toml` or config. | |
398 | /// | |
399 | /// This is None if the user did not specify one, in which case the | |
400 | /// `default` is used. Note that the built-in defaults for test/bench/doc | |
401 | /// always set this since they need to declare the `inherits` value. | |
402 | toml: Option<TomlProfile>, | |
403 | } | |
404 | ||
405 | impl ProfileMaker { | |
406 | /// Creates a new `ProfileMaker`. | |
407 | /// | |
408 | /// Note that this does not process `inherits`, the caller is responsible for that. | |
409 | fn new(default: Profile, toml: Option<TomlProfile>) -> ProfileMaker { | |
410 | ProfileMaker { default, toml } | |
411 | } | |
412 | ||
413 | /// Generates a new `Profile`. | |
414 | fn get_profile( | |
415 | &self, | |
416 | pkg_id: Option<PackageId>, | |
417 | is_member: bool, | |
418 | is_for_host: bool, | |
419 | ) -> Profile { | |
420 | let mut profile = self.default.clone(); | |
421 | ||
422 | // First apply profile-specific settings, things like | |
423 | // `[profile.release]` | |
424 | if let Some(toml) = &self.toml { | |
425 | merge_profile(&mut profile, toml); | |
426 | } | |
427 | ||
428 | // Next start overriding those settings. First comes build dependencies | |
429 | // which default to opt-level 0... | |
430 | if is_for_host { | |
431 | // For-host units are things like procedural macros, build scripts, and | |
432 | // their dependencies. For these units most projects simply want them | |
433 | // to compile quickly and the runtime doesn't matter too much since | |
434 | // they tend to process very little data. For this reason we default | |
435 | // them to a "compile as quickly as possible" mode which for now means | |
436 | // basically turning down the optimization level and avoid limiting | |
437 | // codegen units. This ensures that we spend little time optimizing as | |
438 | // well as enabling parallelism by not constraining codegen units. | |
439 | profile.opt_level = InternedString::new("0"); | |
440 | profile.codegen_units = None; | |
441 | ||
442 | // For build dependencies, we usually don't need debuginfo, and | |
443 | // removing it will compile faster. However, that can conflict with | |
444 | // a unit graph optimization, reusing units that are shared between | |
445 | // build dependencies and runtime dependencies: when the runtime | |
446 | // target is the same as the build host, we only need to build a | |
447 | // dependency once and reuse the results, instead of building twice. | |
448 | // We defer the choice of the debuginfo level until we can check if | |
449 | // a unit is shared. If that's the case, we'll use the deferred value | |
450 | // below so the unit can be reused, otherwise we can avoid emitting | |
451 | // the unit's debuginfo. | |
fe692bf9 | 452 | profile.debuginfo = DebugInfo::Deferred(profile.debuginfo.into_inner()); |
0a29b90c FG |
453 | } |
454 | // ... and next comes any other sorts of overrides specified in | |
455 | // profiles, such as `[profile.release.build-override]` or | |
456 | // `[profile.release.package.foo]` | |
457 | if let Some(toml) = &self.toml { | |
458 | merge_toml_overrides(pkg_id, is_member, is_for_host, &mut profile, toml); | |
459 | } | |
460 | profile | |
461 | } | |
462 | } | |
463 | ||
464 | /// Merge package and build overrides from the given TOML profile into the given `Profile`. | |
465 | fn merge_toml_overrides( | |
466 | pkg_id: Option<PackageId>, | |
467 | is_member: bool, | |
468 | is_for_host: bool, | |
469 | profile: &mut Profile, | |
470 | toml: &TomlProfile, | |
471 | ) { | |
472 | if is_for_host { | |
473 | if let Some(build_override) = &toml.build_override { | |
474 | merge_profile(profile, build_override); | |
475 | } | |
476 | } | |
477 | if let Some(overrides) = toml.package.as_ref() { | |
478 | if !is_member { | |
479 | if let Some(all) = overrides.get(&ProfilePackageSpec::All) { | |
480 | merge_profile(profile, all); | |
481 | } | |
482 | } | |
483 | if let Some(pkg_id) = pkg_id { | |
484 | let mut matches = overrides | |
485 | .iter() | |
486 | .filter_map(|(key, spec_profile)| match *key { | |
487 | ProfilePackageSpec::All => None, | |
488 | ProfilePackageSpec::Spec(ref s) => { | |
489 | if s.matches(pkg_id) { | |
490 | Some(spec_profile) | |
491 | } else { | |
492 | None | |
493 | } | |
494 | } | |
495 | }); | |
496 | if let Some(spec_profile) = matches.next() { | |
497 | merge_profile(profile, spec_profile); | |
498 | // `validate_packages` should ensure that there are | |
499 | // no additional matches. | |
500 | assert!( | |
501 | matches.next().is_none(), | |
502 | "package `{}` matched multiple package profile overrides", | |
503 | pkg_id | |
504 | ); | |
505 | } | |
506 | } | |
507 | } | |
508 | } | |
509 | ||
510 | /// Merge the given TOML profile into the given `Profile`. | |
511 | /// | |
512 | /// Does not merge overrides (see `merge_toml_overrides`). | |
513 | fn merge_profile(profile: &mut Profile, toml: &TomlProfile) { | |
514 | if let Some(ref opt_level) = toml.opt_level { | |
515 | profile.opt_level = InternedString::new(&opt_level.0); | |
516 | } | |
517 | match toml.lto { | |
518 | Some(StringOrBool::Bool(b)) => profile.lto = Lto::Bool(b), | |
519 | Some(StringOrBool::String(ref n)) if is_off(n.as_str()) => profile.lto = Lto::Off, | |
520 | Some(StringOrBool::String(ref n)) => profile.lto = Lto::Named(InternedString::new(n)), | |
521 | None => {} | |
522 | } | |
523 | if toml.codegen_backend.is_some() { | |
524 | profile.codegen_backend = toml.codegen_backend; | |
525 | } | |
526 | if toml.codegen_units.is_some() { | |
527 | profile.codegen_units = toml.codegen_units; | |
528 | } | |
49aad941 | 529 | if let Some(debuginfo) = toml.debug { |
fe692bf9 | 530 | profile.debuginfo = DebugInfo::Resolved(debuginfo); |
0a29b90c FG |
531 | } |
532 | if let Some(debug_assertions) = toml.debug_assertions { | |
533 | profile.debug_assertions = debug_assertions; | |
534 | } | |
535 | if let Some(split_debuginfo) = &toml.split_debuginfo { | |
536 | profile.split_debuginfo = Some(InternedString::new(split_debuginfo)); | |
537 | } | |
538 | if let Some(rpath) = toml.rpath { | |
539 | profile.rpath = rpath; | |
540 | } | |
541 | if let Some(panic) = &toml.panic { | |
542 | profile.panic = match panic.as_str() { | |
543 | "unwind" => PanicStrategy::Unwind, | |
544 | "abort" => PanicStrategy::Abort, | |
545 | // This should be validated in TomlProfile::validate | |
546 | _ => panic!("Unexpected panic setting `{}`", panic), | |
547 | }; | |
548 | } | |
549 | if let Some(overflow_checks) = toml.overflow_checks { | |
550 | profile.overflow_checks = overflow_checks; | |
551 | } | |
552 | if let Some(incremental) = toml.incremental { | |
553 | profile.incremental = incremental; | |
554 | } | |
555 | if let Some(flags) = &toml.rustflags { | |
556 | profile.rustflags = flags.clone(); | |
557 | } | |
558 | profile.strip = match toml.strip { | |
559 | Some(StringOrBool::Bool(true)) => Strip::Named(InternedString::new("symbols")), | |
560 | None | Some(StringOrBool::Bool(false)) => Strip::None, | |
561 | Some(StringOrBool::String(ref n)) if n.as_str() == "none" => Strip::None, | |
562 | Some(StringOrBool::String(ref n)) => Strip::Named(InternedString::new(n)), | |
563 | }; | |
564 | } | |
565 | ||
566 | /// The root profile (dev/release). | |
567 | /// | |
568 | /// This is currently only used for the `PROFILE` env var for build scripts | |
569 | /// for backwards compatibility. We should probably deprecate `PROFILE` and | |
570 | /// encourage using things like `DEBUG` and `OPT_LEVEL` instead. | |
571 | #[derive(Clone, Copy, Eq, PartialOrd, Ord, PartialEq, Debug)] | |
572 | pub enum ProfileRoot { | |
573 | Release, | |
574 | Debug, | |
575 | } | |
576 | ||
577 | /// Profile settings used to determine which compiler flags to use for a | |
578 | /// target. | |
579 | #[derive(Clone, Eq, PartialOrd, Ord, serde::Serialize)] | |
580 | pub struct Profile { | |
581 | pub name: InternedString, | |
582 | pub opt_level: InternedString, | |
583 | #[serde(skip)] // named profiles are unstable | |
584 | pub root: ProfileRoot, | |
585 | pub lto: Lto, | |
586 | // `None` means use rustc default. | |
587 | pub codegen_backend: Option<InternedString>, | |
588 | // `None` means use rustc default. | |
589 | pub codegen_units: Option<u32>, | |
590 | pub debuginfo: DebugInfo, | |
591 | pub split_debuginfo: Option<InternedString>, | |
592 | pub debug_assertions: bool, | |
593 | pub overflow_checks: bool, | |
594 | pub rpath: bool, | |
595 | pub incremental: bool, | |
596 | pub panic: PanicStrategy, | |
597 | pub strip: Strip, | |
598 | #[serde(skip_serializing_if = "Vec::is_empty")] // remove when `rustflags` is stablized | |
599 | // Note that `rustflags` is used for the cargo-feature `profile_rustflags` | |
600 | pub rustflags: Vec<InternedString>, | |
601 | } | |
602 | ||
603 | impl Default for Profile { | |
604 | fn default() -> Profile { | |
605 | Profile { | |
606 | name: InternedString::new(""), | |
607 | opt_level: InternedString::new("0"), | |
608 | root: ProfileRoot::Debug, | |
609 | lto: Lto::Bool(false), | |
610 | codegen_backend: None, | |
611 | codegen_units: None, | |
fe692bf9 | 612 | debuginfo: DebugInfo::Resolved(TomlDebugInfo::None), |
0a29b90c FG |
613 | debug_assertions: false, |
614 | split_debuginfo: None, | |
615 | overflow_checks: false, | |
616 | rpath: false, | |
617 | incremental: false, | |
618 | panic: PanicStrategy::Unwind, | |
619 | strip: Strip::None, | |
620 | rustflags: vec![], | |
621 | } | |
622 | } | |
623 | } | |
624 | ||
625 | compact_debug! { | |
626 | impl fmt::Debug for Profile { | |
627 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
628 | let (default, default_name) = match self.name.as_str() { | |
629 | "dev" => (Profile::default_dev(), "default_dev()"), | |
630 | "release" => (Profile::default_release(), "default_release()"), | |
631 | _ => (Profile::default(), "default()"), | |
632 | }; | |
633 | [debug_the_fields( | |
634 | name | |
635 | opt_level | |
636 | lto | |
637 | root | |
638 | codegen_backend | |
639 | codegen_units | |
640 | debuginfo | |
641 | split_debuginfo | |
642 | debug_assertions | |
643 | overflow_checks | |
644 | rpath | |
645 | incremental | |
646 | panic | |
647 | strip | |
648 | rustflags | |
649 | )] | |
650 | } | |
651 | } | |
652 | } | |
653 | ||
654 | impl fmt::Display for Profile { | |
655 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
656 | write!(f, "Profile({})", self.name) | |
657 | } | |
658 | } | |
659 | ||
660 | impl hash::Hash for Profile { | |
661 | fn hash<H>(&self, state: &mut H) | |
662 | where | |
663 | H: hash::Hasher, | |
664 | { | |
665 | self.comparable().hash(state); | |
666 | } | |
667 | } | |
668 | ||
669 | impl cmp::PartialEq for Profile { | |
670 | fn eq(&self, other: &Self) -> bool { | |
671 | self.comparable() == other.comparable() | |
672 | } | |
673 | } | |
674 | ||
675 | impl Profile { | |
676 | /// Returns a built-in `dev` profile. | |
677 | fn default_dev() -> Profile { | |
678 | Profile { | |
679 | name: InternedString::new("dev"), | |
680 | root: ProfileRoot::Debug, | |
fe692bf9 | 681 | debuginfo: DebugInfo::Resolved(TomlDebugInfo::Full), |
0a29b90c FG |
682 | debug_assertions: true, |
683 | overflow_checks: true, | |
684 | incremental: true, | |
685 | ..Profile::default() | |
686 | } | |
687 | } | |
688 | ||
689 | /// Returns a built-in `release` profile. | |
690 | fn default_release() -> Profile { | |
691 | Profile { | |
692 | name: InternedString::new("release"), | |
693 | root: ProfileRoot::Release, | |
694 | opt_level: InternedString::new("3"), | |
695 | ..Profile::default() | |
696 | } | |
697 | } | |
698 | ||
699 | /// Compares all fields except `name`, which doesn't affect compilation. | |
700 | /// This is necessary for `Unit` deduplication for things like "test" and | |
701 | /// "dev" which are essentially the same. | |
49aad941 | 702 | fn comparable(&self) -> impl Hash + Eq + '_ { |
0a29b90c FG |
703 | ( |
704 | self.opt_level, | |
705 | self.lto, | |
706 | self.codegen_backend, | |
707 | self.codegen_units, | |
708 | self.debuginfo, | |
709 | self.split_debuginfo, | |
710 | self.debug_assertions, | |
711 | self.overflow_checks, | |
712 | self.rpath, | |
49aad941 FG |
713 | (self.incremental, self.panic, self.strip), |
714 | &self.rustflags, | |
0a29b90c FG |
715 | ) |
716 | } | |
717 | } | |
718 | ||
719 | /// The debuginfo level setting. | |
720 | /// | |
fe692bf9 FG |
721 | /// This is semantically a [`TomlDebugInfo`], and should be used as so via the |
722 | /// [`DebugInfo::into_inner`] method for all intents and purposes. | |
0a29b90c FG |
723 | /// |
724 | /// Internally, it's used to model a debuginfo level whose value can be deferred | |
725 | /// for optimization purposes: host dependencies usually don't need the same | |
726 | /// level as target dependencies. For dependencies that are shared between the | |
727 | /// two however, that value also affects reuse: different debuginfo levels would | |
728 | /// cause to build a unit twice. By deferring the choice until we know | |
729 | /// whether to choose the optimized value or the default value, we can make sure | |
730 | /// the unit is only built once and the unit graph is still optimized. | |
731 | #[derive(Debug, Copy, Clone, serde::Serialize)] | |
732 | #[serde(untagged)] | |
733 | pub enum DebugInfo { | |
fe692bf9 FG |
734 | /// A debuginfo level that is fixed and will not change. |
735 | /// | |
736 | /// This can be set by a profile, user, or default value. | |
737 | Resolved(TomlDebugInfo), | |
0a29b90c FG |
738 | /// For internal purposes: a deferred debuginfo level that can be optimized |
739 | /// away, but has this value otherwise. | |
740 | /// | |
fe692bf9 | 741 | /// Behaves like `Resolved` in all situations except for the default build |
0a29b90c FG |
742 | /// dependencies profile: whenever a build dependency is not shared with |
743 | /// runtime dependencies, this level is weakened to a lower level that is | |
fe692bf9 | 744 | /// faster to build (see [`DebugInfo::weaken`]). |
0a29b90c FG |
745 | /// |
746 | /// In all other situations, this level value will be the one to use. | |
49aad941 | 747 | Deferred(TomlDebugInfo), |
0a29b90c FG |
748 | } |
749 | ||
750 | impl DebugInfo { | |
fe692bf9 FG |
751 | /// The main way to interact with this debuginfo level, turning it into a [`TomlDebugInfo`]. |
752 | pub fn into_inner(self) -> TomlDebugInfo { | |
0a29b90c | 753 | match self { |
fe692bf9 | 754 | DebugInfo::Resolved(v) | DebugInfo::Deferred(v) => v, |
0a29b90c FG |
755 | } |
756 | } | |
757 | ||
49aad941 | 758 | /// Returns true if any debuginfo will be generated. Helper |
0a29b90c FG |
759 | /// for a common operation on the usual `Option` representation. |
760 | pub(crate) fn is_turned_on(&self) -> bool { | |
fe692bf9 | 761 | !matches!(self.into_inner(), TomlDebugInfo::None) |
0a29b90c FG |
762 | } |
763 | ||
764 | pub(crate) fn is_deferred(&self) -> bool { | |
765 | matches!(self, DebugInfo::Deferred(_)) | |
766 | } | |
767 | ||
768 | /// Force the deferred, preferred, debuginfo level to a finalized explicit value. | |
769 | pub(crate) fn finalize(self) -> Self { | |
770 | match self { | |
fe692bf9 | 771 | DebugInfo::Deferred(v) => DebugInfo::Resolved(v), |
0a29b90c FG |
772 | _ => self, |
773 | } | |
774 | } | |
775 | ||
776 | /// Reset to the lowest level: no debuginfo. | |
777 | pub(crate) fn weaken(self) -> Self { | |
fe692bf9 | 778 | DebugInfo::Resolved(TomlDebugInfo::None) |
0a29b90c FG |
779 | } |
780 | } | |
781 | ||
782 | impl PartialEq for DebugInfo { | |
783 | fn eq(&self, other: &DebugInfo) -> bool { | |
fe692bf9 | 784 | self.into_inner().eq(&other.into_inner()) |
0a29b90c FG |
785 | } |
786 | } | |
787 | ||
788 | impl Eq for DebugInfo {} | |
789 | ||
790 | impl Hash for DebugInfo { | |
791 | fn hash<H: std::hash::Hasher>(&self, state: &mut H) { | |
fe692bf9 | 792 | self.into_inner().hash(state); |
0a29b90c FG |
793 | } |
794 | } | |
795 | ||
796 | impl PartialOrd for DebugInfo { | |
797 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | |
fe692bf9 | 798 | self.into_inner().partial_cmp(&other.into_inner()) |
0a29b90c FG |
799 | } |
800 | } | |
801 | ||
802 | impl Ord for DebugInfo { | |
803 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { | |
fe692bf9 | 804 | self.into_inner().cmp(&other.into_inner()) |
0a29b90c FG |
805 | } |
806 | } | |
807 | ||
808 | /// The link-time-optimization setting. | |
809 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] | |
810 | pub enum Lto { | |
811 | /// Explicitly no LTO, disables thin-LTO. | |
812 | Off, | |
813 | /// True = "Fat" LTO | |
814 | /// False = rustc default (no args), currently "thin LTO" | |
815 | Bool(bool), | |
816 | /// Named LTO settings like "thin". | |
817 | Named(InternedString), | |
818 | } | |
819 | ||
820 | impl serde::ser::Serialize for Lto { | |
821 | fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error> | |
822 | where | |
823 | S: serde::ser::Serializer, | |
824 | { | |
825 | match self { | |
826 | Lto::Off => "off".serialize(s), | |
827 | Lto::Bool(b) => b.to_string().serialize(s), | |
828 | Lto::Named(n) => n.serialize(s), | |
829 | } | |
830 | } | |
831 | } | |
832 | ||
833 | /// The `panic` setting. | |
834 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord, serde::Serialize)] | |
835 | #[serde(rename_all = "lowercase")] | |
836 | pub enum PanicStrategy { | |
837 | Unwind, | |
838 | Abort, | |
839 | } | |
840 | ||
841 | impl fmt::Display for PanicStrategy { | |
842 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
843 | match *self { | |
844 | PanicStrategy::Unwind => "unwind", | |
845 | PanicStrategy::Abort => "abort", | |
846 | } | |
847 | .fmt(f) | |
848 | } | |
849 | } | |
850 | ||
851 | /// The setting for choosing which symbols to strip | |
852 | #[derive( | |
853 | Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize, | |
854 | )] | |
855 | #[serde(rename_all = "lowercase")] | |
856 | pub enum Strip { | |
857 | /// Don't remove any symbols | |
858 | None, | |
859 | /// Named Strip settings | |
860 | Named(InternedString), | |
861 | } | |
862 | ||
863 | impl fmt::Display for Strip { | |
864 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
865 | match *self { | |
866 | Strip::None => "none", | |
867 | Strip::Named(s) => s.as_str(), | |
868 | } | |
869 | .fmt(f) | |
870 | } | |
871 | } | |
872 | ||
873 | /// Flags used in creating `Unit`s to indicate the purpose for the target, and | |
874 | /// to ensure the target's dependencies have the correct settings. | |
875 | /// | |
876 | /// This means these are passed down from the root of the dependency tree to apply | |
877 | /// to most child dependencies. | |
878 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] | |
879 | pub struct UnitFor { | |
880 | /// A target for `build.rs` or any of its dependencies, or a proc-macro or | |
881 | /// any of its dependencies. This enables `build-override` profiles for | |
882 | /// these targets. | |
883 | /// | |
884 | /// An invariant is that if `host_features` is true, `host` must be true. | |
885 | /// | |
886 | /// Note that this is `true` for `RunCustomBuild` units, even though that | |
887 | /// unit should *not* use build-override profiles. This is a bit of a | |
888 | /// special case. When computing the `RunCustomBuild` unit, it manually | |
889 | /// uses the `get_profile_run_custom_build` method to get the correct | |
890 | /// profile information for the unit. `host` needs to be true so that all | |
891 | /// of the dependencies of that `RunCustomBuild` unit have this flag be | |
892 | /// sticky (and forced to `true` for all further dependencies) — which is | |
893 | /// the whole point of `UnitFor`. | |
894 | host: bool, | |
895 | /// A target for a build dependency or proc-macro (or any of its | |
896 | /// dependencies). This is used for computing features of build | |
897 | /// dependencies and proc-macros independently of other dependency kinds. | |
898 | /// | |
899 | /// The subtle difference between this and `host` is that the build script | |
900 | /// for a non-host package sets this to `false` because it wants the | |
901 | /// features of the non-host package (whereas `host` is true because the | |
902 | /// build script is being built for the host). `host_features` becomes | |
903 | /// `true` for build-dependencies or proc-macros, or any of their | |
904 | /// dependencies. For example, with this dependency tree: | |
905 | /// | |
906 | /// ```text | |
907 | /// foo | |
908 | /// ├── foo build.rs | |
909 | /// │ └── shared_dep (BUILD dependency) | |
910 | /// │ └── shared_dep build.rs | |
911 | /// └── shared_dep (Normal dependency) | |
912 | /// └── shared_dep build.rs | |
913 | /// ``` | |
914 | /// | |
915 | /// In this example, `foo build.rs` is HOST=true, HOST_FEATURES=false. | |
916 | /// This is so that `foo build.rs` gets the profile settings for build | |
917 | /// scripts (HOST=true) and features of foo (HOST_FEATURES=false) because | |
918 | /// build scripts need to know which features their package is being built | |
919 | /// with. | |
920 | /// | |
921 | /// But in the case of `shared_dep`, when built as a build dependency, | |
922 | /// both flags are true (it only wants the build-dependency features). | |
923 | /// When `shared_dep` is built as a normal dependency, then `shared_dep | |
924 | /// build.rs` is HOST=true, HOST_FEATURES=false for the same reasons that | |
925 | /// foo's build script is set that way. | |
926 | host_features: bool, | |
927 | /// How Cargo processes the `panic` setting or profiles. | |
928 | panic_setting: PanicSetting, | |
929 | ||
930 | /// The compile kind of the root unit for which artifact dependencies are built. | |
931 | /// This is required particularly for the `target = "target"` setting of artifact | |
932 | /// dependencies which mean to inherit the `--target` specified on the command-line. | |
933 | /// However, that is a multi-value argument and root units are already created to | |
934 | /// reflect one unit per --target. Thus we have to build one artifact with the | |
935 | /// correct target for each of these trees. | |
936 | /// Note that this will always be set as we don't initially know if there are | |
937 | /// artifacts that make use of it. | |
938 | root_compile_kind: CompileKind, | |
939 | ||
940 | /// This is only set for artifact dependencies which have their | |
941 | /// `<target-triple>|target` set. | |
942 | /// If so, this information is used as part of the key for resolving their features, | |
943 | /// allowing for target-dependent feature resolution within the entire dependency tree. | |
944 | /// Note that this target corresponds to the target used to build the units in that | |
945 | /// dependency tree, too, but this copy of it is specifically used for feature lookup. | |
946 | artifact_target_for_features: Option<CompileTarget>, | |
947 | } | |
948 | ||
949 | /// How Cargo processes the `panic` setting or profiles. | |
950 | /// | |
951 | /// This is done to handle test/benches inheriting from dev/release, | |
952 | /// as well as forcing `for_host` units to always unwind. | |
953 | /// It also interacts with [`-Z panic-abort-tests`]. | |
954 | /// | |
955 | /// [`-Z panic-abort-tests`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#panic-abort-tests | |
956 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] | |
957 | enum PanicSetting { | |
958 | /// Used to force a unit to always be compiled with the `panic=unwind` | |
959 | /// strategy, notably for build scripts, proc macros, etc. | |
960 | AlwaysUnwind, | |
961 | ||
962 | /// Indicates that this unit will read its `profile` setting and use | |
963 | /// whatever is configured there. | |
964 | ReadProfile, | |
965 | } | |
966 | ||
967 | impl UnitFor { | |
968 | /// A unit for a normal target/dependency (i.e., not custom build, | |
969 | /// proc macro/plugin, or test/bench). | |
970 | pub fn new_normal(root_compile_kind: CompileKind) -> UnitFor { | |
971 | UnitFor { | |
972 | host: false, | |
973 | host_features: false, | |
974 | panic_setting: PanicSetting::ReadProfile, | |
975 | root_compile_kind, | |
976 | artifact_target_for_features: None, | |
977 | } | |
978 | } | |
979 | ||
980 | /// A unit for a custom build script or proc-macro or its dependencies. | |
981 | /// | |
982 | /// The `host_features` parameter is whether or not this is for a build | |
983 | /// dependency or proc-macro (something that requires being built "on the | |
984 | /// host"). Build scripts for non-host units should use `false` because | |
985 | /// they want to use the features of the package they are running for. | |
986 | pub fn new_host(host_features: bool, root_compile_kind: CompileKind) -> UnitFor { | |
987 | UnitFor { | |
988 | host: true, | |
989 | host_features, | |
990 | // Force build scripts to always use `panic=unwind` for now to | |
991 | // maximally share dependencies with procedural macros. | |
992 | panic_setting: PanicSetting::AlwaysUnwind, | |
993 | root_compile_kind, | |
994 | artifact_target_for_features: None, | |
995 | } | |
996 | } | |
997 | ||
998 | /// A unit for a compiler plugin or their dependencies. | |
999 | pub fn new_compiler(root_compile_kind: CompileKind) -> UnitFor { | |
1000 | UnitFor { | |
1001 | host: false, | |
1002 | // The feature resolver doesn't know which dependencies are | |
1003 | // plugins, so for now plugins don't split features. Since plugins | |
1004 | // are mostly deprecated, just leave this as false. | |
1005 | host_features: false, | |
1006 | // Force plugins to use `panic=abort` so panics in the compiler do | |
1007 | // not abort the process but instead end with a reasonable error | |
1008 | // message that involves catching the panic in the compiler. | |
1009 | panic_setting: PanicSetting::AlwaysUnwind, | |
1010 | root_compile_kind, | |
1011 | artifact_target_for_features: None, | |
1012 | } | |
1013 | } | |
1014 | ||
1015 | /// A unit for a test/bench target or their dependencies. | |
1016 | /// | |
1017 | /// Note that `config` is taken here for unstable CLI features to detect | |
1018 | /// whether `panic=abort` is supported for tests. Historical versions of | |
1019 | /// rustc did not support this, but newer versions do with an unstable | |
1020 | /// compiler flag. | |
1021 | pub fn new_test(config: &Config, root_compile_kind: CompileKind) -> UnitFor { | |
1022 | UnitFor { | |
1023 | host: false, | |
1024 | host_features: false, | |
1025 | // We're testing out an unstable feature (`-Zpanic-abort-tests`) | |
1026 | // which inherits the panic setting from the dev/release profile | |
1027 | // (basically avoid recompiles) but historical defaults required | |
1028 | // that we always unwound. | |
1029 | panic_setting: if config.cli_unstable().panic_abort_tests { | |
1030 | PanicSetting::ReadProfile | |
1031 | } else { | |
1032 | PanicSetting::AlwaysUnwind | |
1033 | }, | |
1034 | root_compile_kind, | |
1035 | artifact_target_for_features: None, | |
1036 | } | |
1037 | } | |
1038 | ||
1039 | /// This is a special case for unit tests of a proc-macro. | |
1040 | /// | |
1041 | /// Proc-macro unit tests are forced to be run on the host. | |
1042 | pub fn new_host_test(config: &Config, root_compile_kind: CompileKind) -> UnitFor { | |
1043 | let mut unit_for = UnitFor::new_test(config, root_compile_kind); | |
1044 | unit_for.host = true; | |
1045 | unit_for.host_features = true; | |
1046 | unit_for | |
1047 | } | |
1048 | ||
1049 | /// Returns a new copy updated based on the target dependency. | |
1050 | /// | |
1051 | /// This is where the magic happens that the host/host_features settings | |
1052 | /// transition in a sticky fashion. As the dependency graph is being | |
1053 | /// built, once those flags are set, they stay set for the duration of | |
1054 | /// that portion of tree. | |
1055 | pub fn with_dependency( | |
1056 | self, | |
1057 | parent: &Unit, | |
1058 | dep_target: &Target, | |
1059 | root_compile_kind: CompileKind, | |
1060 | ) -> UnitFor { | |
1061 | // A build script or proc-macro transitions this to being built for the host. | |
1062 | let dep_for_host = dep_target.for_host(); | |
1063 | // This is where feature decoupling of host versus target happens. | |
1064 | // | |
1065 | // Once host features are desired, they are always desired. | |
1066 | // | |
1067 | // A proc-macro should always use host features. | |
1068 | // | |
1069 | // Dependencies of a build script should use host features (subtle | |
1070 | // point: the build script itself does *not* use host features, that's | |
1071 | // why the parent is checked here, and not the dependency). | |
1072 | let host_features = | |
1073 | self.host_features || parent.target.is_custom_build() || dep_target.proc_macro(); | |
1074 | // Build scripts and proc macros, and all of their dependencies are | |
1075 | // AlwaysUnwind. | |
1076 | let panic_setting = if dep_for_host { | |
1077 | PanicSetting::AlwaysUnwind | |
1078 | } else { | |
1079 | self.panic_setting | |
1080 | }; | |
1081 | UnitFor { | |
1082 | host: self.host || dep_for_host, | |
1083 | host_features, | |
1084 | panic_setting, | |
1085 | root_compile_kind, | |
1086 | artifact_target_for_features: self.artifact_target_for_features, | |
1087 | } | |
1088 | } | |
1089 | ||
1090 | pub fn for_custom_build(self) -> UnitFor { | |
1091 | UnitFor { | |
1092 | host: true, | |
1093 | host_features: self.host_features, | |
1094 | // Force build scripts to always use `panic=unwind` for now to | |
1095 | // maximally share dependencies with procedural macros. | |
1096 | panic_setting: PanicSetting::AlwaysUnwind, | |
1097 | root_compile_kind: self.root_compile_kind, | |
1098 | artifact_target_for_features: self.artifact_target_for_features, | |
1099 | } | |
1100 | } | |
1101 | ||
1102 | /// Set the artifact compile target for use in features using the given `artifact`. | |
1103 | pub(crate) fn with_artifact_features(mut self, artifact: &Artifact) -> UnitFor { | |
1104 | self.artifact_target_for_features = artifact.target().and_then(|t| t.to_compile_target()); | |
1105 | self | |
1106 | } | |
1107 | ||
1108 | /// Set the artifact compile target as determined by a resolved compile target. This is used if `target = "target"`. | |
1109 | pub(crate) fn with_artifact_features_from_resolved_compile_kind( | |
1110 | mut self, | |
1111 | kind: Option<CompileKind>, | |
1112 | ) -> UnitFor { | |
1113 | self.artifact_target_for_features = kind.and_then(|kind| match kind { | |
1114 | CompileKind::Host => None, | |
1115 | CompileKind::Target(triple) => Some(triple), | |
1116 | }); | |
1117 | self | |
1118 | } | |
1119 | ||
1120 | /// Returns `true` if this unit is for a build script or any of its | |
1121 | /// dependencies, or a proc macro or any of its dependencies. | |
1122 | pub fn is_for_host(&self) -> bool { | |
1123 | self.host | |
1124 | } | |
1125 | ||
1126 | pub fn is_for_host_features(&self) -> bool { | |
1127 | self.host_features | |
1128 | } | |
1129 | ||
1130 | /// Returns how `panic` settings should be handled for this profile | |
1131 | fn panic_setting(&self) -> PanicSetting { | |
1132 | self.panic_setting | |
1133 | } | |
1134 | ||
1135 | /// We might contain a parent artifact compile kind for features already, but will | |
1136 | /// gladly accept the one of this dependency as an override as it defines how | |
1137 | /// the artifact is built. | |
1138 | /// If we are an artifact but don't specify a `target`, we assume the default | |
1139 | /// compile kind that is suitable in this situation. | |
1140 | pub(crate) fn map_to_features_for(&self, dep_artifact: Option<&Artifact>) -> FeaturesFor { | |
1141 | FeaturesFor::from_for_host_or_artifact_target( | |
1142 | self.is_for_host_features(), | |
1143 | match dep_artifact { | |
1144 | Some(artifact) => artifact | |
1145 | .target() | |
1146 | .and_then(|t| t.to_resolved_compile_target(self.root_compile_kind)), | |
1147 | None => self.artifact_target_for_features, | |
1148 | }, | |
1149 | ) | |
1150 | } | |
1151 | ||
1152 | pub(crate) fn root_compile_kind(&self) -> CompileKind { | |
1153 | self.root_compile_kind | |
1154 | } | |
1155 | } | |
1156 | ||
1157 | /// Takes the manifest profiles, and overlays the config profiles on-top. | |
1158 | /// | |
1159 | /// Returns a new copy of the profile map with all the mergers complete. | |
1160 | fn merge_config_profiles( | |
1161 | ws: &Workspace<'_>, | |
1162 | requested_profile: InternedString, | |
1163 | ) -> CargoResult<BTreeMap<InternedString, TomlProfile>> { | |
1164 | let mut profiles = match ws.profiles() { | |
1165 | Some(profiles) => profiles.get_all().clone(), | |
1166 | None => BTreeMap::new(), | |
1167 | }; | |
1168 | // Set of profile names to check if defined in config only. | |
1169 | let mut check_to_add = HashSet::new(); | |
1170 | check_to_add.insert(requested_profile); | |
1171 | // Merge config onto manifest profiles. | |
1172 | for (name, profile) in &mut profiles { | |
1173 | if let Some(config_profile) = get_config_profile(ws, name)? { | |
1174 | profile.merge(&config_profile); | |
1175 | } | |
1176 | if let Some(inherits) = &profile.inherits { | |
1177 | check_to_add.insert(*inherits); | |
1178 | } | |
1179 | } | |
1180 | // Add the built-in profiles. This is important for things like `cargo | |
1181 | // test` which implicitly use the "dev" profile for dependencies. | |
1182 | for name in &["dev", "release", "test", "bench"] { | |
1183 | check_to_add.insert(InternedString::new(name)); | |
1184 | } | |
1185 | // Add config-only profiles. | |
1186 | // Need to iterate repeatedly to get all the inherits values. | |
1187 | let mut current = HashSet::new(); | |
1188 | while !check_to_add.is_empty() { | |
1189 | std::mem::swap(&mut current, &mut check_to_add); | |
1190 | for name in current.drain() { | |
1191 | if !profiles.contains_key(&name) { | |
1192 | if let Some(config_profile) = get_config_profile(ws, &name)? { | |
1193 | if let Some(inherits) = &config_profile.inherits { | |
1194 | check_to_add.insert(*inherits); | |
1195 | } | |
1196 | profiles.insert(name, config_profile); | |
1197 | } | |
1198 | } | |
1199 | } | |
1200 | } | |
1201 | Ok(profiles) | |
1202 | } | |
1203 | ||
1204 | /// Helper for fetching a profile from config. | |
1205 | fn get_config_profile(ws: &Workspace<'_>, name: &str) -> CargoResult<Option<TomlProfile>> { | |
1206 | let profile: Option<config::Value<TomlProfile>> = | |
1207 | ws.config().get(&format!("profile.{}", name))?; | |
781aab86 FG |
1208 | let Some(profile) = profile else { |
1209 | return Ok(None); | |
0a29b90c FG |
1210 | }; |
1211 | let mut warnings = Vec::new(); | |
1212 | profile | |
1213 | .val | |
1214 | .validate( | |
1215 | name, | |
1216 | ws.config().cli_unstable(), | |
1217 | ws.unstable_features(), | |
1218 | &mut warnings, | |
1219 | ) | |
1220 | .with_context(|| { | |
1221 | format!( | |
1222 | "config profile `{}` is not valid (defined in `{}`)", | |
1223 | name, profile.definition | |
1224 | ) | |
1225 | })?; | |
1226 | for warning in warnings { | |
1227 | ws.config().shell().warn(warning)?; | |
1228 | } | |
1229 | Ok(Some(profile.val)) | |
1230 | } | |
1231 | ||
1232 | /// Validate that a package does not match multiple package override specs. | |
1233 | /// | |
1234 | /// For example `[profile.dev.package.bar]` and `[profile.dev.package."bar:0.5.0"]` | |
1235 | /// would both match `bar:0.5.0` which would be ambiguous. | |
1236 | fn validate_packages_unique( | |
1237 | resolve: &Resolve, | |
1238 | name: &str, | |
1239 | toml: &Option<TomlProfile>, | |
1240 | ) -> CargoResult<HashSet<PackageIdSpec>> { | |
781aab86 FG |
1241 | let Some(toml) = toml else { |
1242 | return Ok(HashSet::new()); | |
0a29b90c | 1243 | }; |
781aab86 FG |
1244 | let Some(overrides) = toml.package.as_ref() else { |
1245 | return Ok(HashSet::new()); | |
0a29b90c FG |
1246 | }; |
1247 | // Verify that a package doesn't match multiple spec overrides. | |
1248 | let mut found = HashSet::new(); | |
1249 | for pkg_id in resolve.iter() { | |
1250 | let matches: Vec<&PackageIdSpec> = overrides | |
1251 | .keys() | |
1252 | .filter_map(|key| match *key { | |
1253 | ProfilePackageSpec::All => None, | |
1254 | ProfilePackageSpec::Spec(ref spec) => { | |
1255 | if spec.matches(pkg_id) { | |
1256 | Some(spec) | |
1257 | } else { | |
1258 | None | |
1259 | } | |
1260 | } | |
1261 | }) | |
1262 | .collect(); | |
1263 | match matches.len() { | |
1264 | 0 => {} | |
1265 | 1 => { | |
1266 | found.insert(matches[0].clone()); | |
1267 | } | |
1268 | _ => { | |
1269 | let specs = matches | |
1270 | .iter() | |
1271 | .map(|spec| spec.to_string()) | |
1272 | .collect::<Vec<_>>() | |
1273 | .join(", "); | |
1274 | bail!( | |
1275 | "multiple package overrides in profile `{}` match package `{}`\n\ | |
1276 | found package specs: {}", | |
1277 | name, | |
1278 | pkg_id, | |
1279 | specs | |
1280 | ); | |
1281 | } | |
1282 | } | |
1283 | } | |
1284 | Ok(found) | |
1285 | } | |
1286 | ||
1287 | /// Check for any profile override specs that do not match any known packages. | |
1288 | /// | |
1289 | /// This helps check for typos and mistakes. | |
1290 | fn validate_packages_unmatched( | |
1291 | shell: &mut Shell, | |
1292 | resolve: &Resolve, | |
1293 | name: &str, | |
1294 | toml: &TomlProfile, | |
1295 | found: &HashSet<PackageIdSpec>, | |
1296 | ) -> CargoResult<()> { | |
781aab86 FG |
1297 | let Some(overrides) = toml.package.as_ref() else { |
1298 | return Ok(()); | |
0a29b90c FG |
1299 | }; |
1300 | ||
1301 | // Verify every override matches at least one package. | |
1302 | let missing_specs = overrides.keys().filter_map(|key| { | |
1303 | if let ProfilePackageSpec::Spec(ref spec) = *key { | |
1304 | if !found.contains(spec) { | |
1305 | return Some(spec); | |
1306 | } | |
1307 | } | |
1308 | None | |
1309 | }); | |
1310 | for spec in missing_specs { | |
1311 | // See if there is an exact name match. | |
1312 | let name_matches: Vec<String> = resolve | |
1313 | .iter() | |
1314 | .filter_map(|pkg_id| { | |
1315 | if pkg_id.name() == spec.name() { | |
1316 | Some(pkg_id.to_string()) | |
1317 | } else { | |
1318 | None | |
1319 | } | |
1320 | }) | |
1321 | .collect(); | |
1322 | if name_matches.is_empty() { | |
1323 | let suggestion = closest_msg(&spec.name(), resolve.iter(), |p| p.name().as_str()); | |
1324 | shell.warn(format!( | |
1325 | "profile package spec `{}` in profile `{}` did not match any packages{}", | |
1326 | spec, name, suggestion | |
1327 | ))?; | |
1328 | } else { | |
1329 | shell.warn(format!( | |
1330 | "profile package spec `{}` in profile `{}` \ | |
1331 | has a version or URL that does not match any of the packages: {}", | |
1332 | spec, | |
1333 | name, | |
1334 | name_matches.join(", ") | |
1335 | ))?; | |
1336 | } | |
1337 | } | |
1338 | Ok(()) | |
1339 | } | |
1340 | ||
1341 | /// Returns `true` if a string is a toggle that turns an option off. | |
1342 | fn is_off(s: &str) -> bool { | |
1343 | matches!(s, "off" | "n" | "no" | "none") | |
1344 | } |