cargo/core/
profiles.rs

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