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