]> git.proxmox.com Git - rustc.git/blame - src/tools/cargo/src/cargo/core/profiles.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / src / tools / cargo / src / cargo / core / profiles.rs
CommitLineData
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
24use crate::core::compiler::{CompileKind, CompileTarget, Unit};
25use crate::core::dependency::Artifact;
26use crate::core::resolver::features::FeaturesFor;
27use crate::core::{PackageId, PackageIdSpec, Resolve, Shell, Target, Workspace};
28use crate::util::interning::InternedString;
49aad941
FG
29use crate::util::toml::{
30 ProfilePackageSpec, StringOrBool, TomlDebugInfo, TomlProfile, TomlProfiles,
31};
0a29b90c
FG
32use crate::util::{closest_msg, config, CargoResult, Config};
33use anyhow::{bail, Context as _};
34use std::collections::{BTreeMap, HashMap, HashSet};
35use std::hash::Hash;
36use 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)]
44pub 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
64impl 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)]
394struct 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
405impl 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`.
465fn 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`).
513fn 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)]
572pub 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)]
580pub 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
603impl 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
625compact_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
654impl fmt::Display for Profile {
655 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656 write!(f, "Profile({})", self.name)
657 }
658}
659
660impl 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
669impl cmp::PartialEq for Profile {
670 fn eq(&self, other: &Self) -> bool {
671 self.comparable() == other.comparable()
672 }
673}
674
675impl 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)]
733pub 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
750impl 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
782impl PartialEq for DebugInfo {
783 fn eq(&self, other: &DebugInfo) -> bool {
fe692bf9 784 self.into_inner().eq(&other.into_inner())
0a29b90c
FG
785 }
786}
787
788impl Eq for DebugInfo {}
789
790impl 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
796impl 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
802impl 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)]
810pub 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
820impl 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")]
836pub enum PanicStrategy {
837 Unwind,
838 Abort,
839}
840
841impl 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")]
856pub enum Strip {
857 /// Don't remove any symbols
858 None,
859 /// Named Strip settings
860 Named(InternedString),
861}
862
863impl 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)]
879pub 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)]
957enum 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
967impl 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.
1160fn 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.
1205fn 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.
1236fn 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.
1290fn 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.
1342fn is_off(s: &str) -> bool {
1343 matches!(s, "off" | "n" | "no" | "none")
1344}