1 //! Handles built-in and customizable compiler flag presets.
3 //! [`Profiles`] is a collections of built-in profiles, and profiles defined
4 //! in the root manifest and configurations.
6 //! To start using a profile, most of the time you start from [`Profiles::new`],
7 //! which does the followings:
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`]).
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.
18 //! ## Profile overridden hierarchy
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`].
24 use crate::core
::compiler
::{CompileKind, CompileTarget, Unit}
;
25 use crate::core
::dependency
::Artifact
;
26 use crate::core
::resolver
::features
::FeaturesFor
;
27 use crate::core
::{PackageId, PackageIdSpec, Resolve, Shell, Target, Workspace}
;
28 use crate::util
::interning
::InternedString
;
29 use crate::util
::toml
::{
30 ProfilePackageSpec
, StringOrBool
, TomlDebugInfo
, TomlProfile
, TomlProfiles
,
32 use crate::util
::{closest_msg, config, CargoResult, Config}
;
33 use anyhow
::{bail, Context as _}
;
34 use std
::collections
::{BTreeMap, HashMap, HashSet}
;
36 use std
::{cmp, fmt, hash}
;
38 /// Collection of all profiles.
40 /// To get a specific [`Profile`], you usually create this and call [`get_profile`] then.
42 /// [`get_profile`]: Profiles::get_profile
43 #[derive(Clone, Debug)]
45 /// Incremental compilation can be overridden globally via:
46 /// - `CARGO_INCREMENTAL` environment variable.
47 /// - `build.incremental` config value.
48 incremental
: Option
<bool
>,
49 /// Map of profile name to directory name for that profile.
50 dir_names
: HashMap
<InternedString
, InternedString
>,
51 /// The profile makers. Key is the profile name.
52 by_name
: HashMap
<InternedString
, ProfileMaker
>,
53 /// The original profiles written by the user in the manifest and config.
55 /// This is here to assist with error reporting, as the `ProfileMaker`
56 /// values have the inherits chains all merged together.
57 original_profiles
: BTreeMap
<InternedString
, TomlProfile
>,
58 /// The profile the user requested to use.
59 requested_profile
: InternedString
,
60 /// The host target for rustc being used by this `Profiles`.
61 rustc_host
: InternedString
,
65 pub fn new(ws
: &Workspace
<'_
>, requested_profile
: InternedString
) -> CargoResult
<Profiles
> {
66 let config
= ws
.config();
67 let incremental
= match config
.get_env_os("CARGO_INCREMENTAL") {
68 Some(v
) => Some(v
== "1"),
69 None
=> config
.build_config()?
.incremental
,
71 let mut profiles
= merge_config_profiles(ws
, requested_profile
)?
;
72 let rustc_host
= ws
.config().load_global_rustc(Some(ws
))?
.host
;
74 let mut profile_makers
= Profiles
{
76 dir_names
: Self::predefined_dir_names(),
77 by_name
: HashMap
::new(),
78 original_profiles
: profiles
.clone(),
83 Self::add_root_profiles(&mut profile_makers
, &profiles
);
85 // Merge with predefined profiles.
86 use std
::collections
::btree_map
::Entry
;
87 for (predef_name
, mut predef_prof
) in Self::predefined_profiles().into_iter() {
88 match profiles
.entry(InternedString
::new(predef_name
)) {
89 Entry
::Vacant(vac
) => {
90 vac
.insert(predef_prof
);
92 Entry
::Occupied(mut oc
) => {
93 // Override predefined with the user-provided Toml.
101 for (name
, profile
) in &profiles
{
102 profile_makers
.add_maker(*name
, profile
, &profiles
)?
;
104 // Verify that the requested profile is defined *somewhere*.
105 // This simplifies the API (no need for CargoResult), and enforces
106 // assumptions about how config profiles are loaded.
107 profile_makers
.get_profile_maker(requested_profile
)?
;
111 /// Returns the hard-coded directory names for built-in profiles.
112 fn predefined_dir_names() -> HashMap
<InternedString
, InternedString
> {
114 (InternedString
::new("dev"), InternedString
::new("debug")),
115 (InternedString
::new("test"), InternedString
::new("debug")),
116 (InternedString
::new("bench"), InternedString
::new("release")),
121 /// Initialize `by_name` with the two "root" profiles, `dev`, and
122 /// `release` given the user's definition.
123 fn add_root_profiles(
124 profile_makers
: &mut Profiles
,
125 profiles
: &BTreeMap
<InternedString
, TomlProfile
>,
127 profile_makers
.by_name
.insert(
128 InternedString
::new("dev"),
129 ProfileMaker
::new(Profile
::default_dev(), profiles
.get("dev").cloned()),
132 profile_makers
.by_name
.insert(
133 InternedString
::new("release"),
134 ProfileMaker
::new(Profile
::default_release(), profiles
.get("release").cloned()),
138 /// Returns the built-in profiles (not including dev/release, which are
139 /// "root" profiles).
140 fn predefined_profiles() -> Vec
<(&'
static str, TomlProfile
)> {
145 inherits
: Some(InternedString
::new("release")),
146 ..TomlProfile
::default()
152 inherits
: Some(InternedString
::new("dev")),
153 ..TomlProfile
::default()
159 inherits
: Some(InternedString
::new("dev")),
160 ..TomlProfile
::default()
166 /// Creates a `ProfileMaker`, and inserts it into `self.by_name`.
169 name
: InternedString
,
170 profile
: &TomlProfile
,
171 profiles
: &BTreeMap
<InternedString
, TomlProfile
>,
172 ) -> CargoResult
<()> {
173 match &profile
.dir_name
{
176 self.dir_names
.insert(name
, dir_name
.to_owned());
180 // dev/release are "roots" and don't inherit.
181 if name
== "dev" || name
== "release" {
182 if profile
.inherits
.is_some() {
184 "`inherits` must not be specified in root profile `{}`",
188 // Already inserted from `add_root_profiles`, no need to do anything.
192 // Keep track for inherits cycles.
193 let mut set
= HashSet
::new();
195 let maker
= self.process_chain(name
, profile
, &mut set
, profiles
)?
;
196 self.by_name
.insert(name
, maker
);
200 /// Build a `ProfileMaker` by recursively following the `inherits` setting.
202 /// * `name`: The name of the profile being processed.
203 /// * `profile`: The TOML profile being processed.
204 /// * `set`: Set of profiles that have been visited, used to detect cycles.
205 /// * `profiles`: Map of all TOML profiles.
207 /// Returns a `ProfileMaker` to be used for the given named profile.
210 name
: InternedString
,
211 profile
: &TomlProfile
,
212 set
: &mut HashSet
<InternedString
>,
213 profiles
: &BTreeMap
<InternedString
, TomlProfile
>,
214 ) -> CargoResult
<ProfileMaker
> {
215 let mut maker
= match profile
.inherits
{
216 Some(inherits_name
) if inherits_name
== "dev" || inherits_name
== "release" => {
217 // These are the root profiles added in `add_root_profiles`.
218 self.get_profile_maker(inherits_name
).unwrap().clone()
220 Some(inherits_name
) => {
221 if !set
.insert(inherits_name
) {
223 "profile inheritance loop detected with profile `{}` inheriting `{}`",
229 match profiles
.get(&inherits_name
) {
232 "profile `{}` inherits from `{}`, but that profile is not defined",
237 Some(parent
) => self.process_chain(inherits_name
, parent
, set
, profiles
)?
,
242 "profile `{}` is missing an `inherits` directive \
243 (`inherits` is required for all profiles except `dev` or `release`)",
248 match &mut maker
.toml
{
249 Some(toml
) => toml
.merge(profile
),
250 None
=> maker
.toml
= Some(profile
.clone()),
255 /// Retrieves the profile for a target.
256 /// `is_member` is whether or not this package is a member of the
266 let maker
= self.get_profile_maker(self.requested_profile
).unwrap();
267 let mut profile
= maker
.get_profile(Some(pkg_id
), is_member
, unit_for
.is_for_host());
269 // Dealing with `panic=abort` and `panic=unwind` requires some special
270 // treatment. Be sure to process all the various options here.
271 match unit_for
.panic_setting() {
272 PanicSetting
::AlwaysUnwind
=> profile
.panic
= PanicStrategy
::Unwind
,
273 PanicSetting
::ReadProfile
=> {}
276 // Default macOS debug information to being stored in the "unpacked"
277 // split-debuginfo format. At the time of this writing that's the only
278 // platform which has a stable `-Csplit-debuginfo` option for rustc,
279 // and it's typically much faster than running `dsymutil` on all builds
280 // in incremental cases.
281 if profile
.debuginfo
.is_turned_on() && profile
.split_debuginfo
.is_none() {
282 let target
= match &kind
{
283 CompileKind
::Host
=> self.rustc_host
.as_str(),
284 CompileKind
::Target(target
) => target
.short_name(),
286 if target
.contains("-apple-") {
287 profile
.split_debuginfo
= Some(InternedString
::new("unpacked"));
291 // Incremental can be globally overridden.
292 if let Some(v
) = self.incremental
{
293 profile
.incremental
= v
;
296 // Only enable incremental compilation for sources the user can
297 // modify (aka path sources). For things that change infrequently,
298 // non-incremental builds yield better performance in the compiler
299 // itself (aka crates.io / git dependencies)
301 // (see also https://github.com/rust-lang/cargo/issues/3972)
303 profile
.incremental
= false;
305 profile
.name
= self.requested_profile
;
309 /// The profile for *running* a `build.rs` script is only used for setting
310 /// a few environment variables. To ensure proper de-duplication of the
311 /// running `Unit`, this uses a stripped-down profile (so that unrelated
312 /// profile flags don't cause `build.rs` to needlessly run multiple
314 pub fn get_profile_run_custom_build(&self, for_unit_profile
: &Profile
) -> Profile
{
315 let mut result
= Profile
::default();
316 result
.name
= for_unit_profile
.name
;
317 result
.root
= for_unit_profile
.root
;
318 result
.debuginfo
= for_unit_profile
.debuginfo
;
319 result
.opt_level
= for_unit_profile
.opt_level
;
323 /// This returns the base profile. This is currently used for the
324 /// `[Finished]` line. It is not entirely accurate, since it doesn't
325 /// select for the package that was actually built.
326 pub fn base_profile(&self) -> Profile
{
327 let profile_name
= self.requested_profile
;
328 let maker
= self.get_profile_maker(profile_name
).unwrap();
329 maker
.get_profile(None
, /*is_member*/ true, /*is_for_host*/ false)
332 /// Gets the directory name for a profile, like `debug` or `release`.
333 pub fn get_dir_name(&self) -> InternedString
{
336 .get(&self.requested_profile
)
337 .unwrap_or(&self.requested_profile
)
340 /// Used to check for overrides for non-existing packages.
341 pub fn validate_packages(
343 profiles
: Option
<&TomlProfiles
>,
346 ) -> CargoResult
<()> {
347 for (name
, profile
) in &self.by_name
{
348 // If the user did not specify an override, skip this. This is here
349 // to avoid generating errors for inherited profiles which don't
350 // specify package overrides. The `by_name` profile has had the inherits
351 // chain merged, so we need to look at the original source to check
352 // if an override was specified.
356 .and_then(|orig
| orig
.package
.as_ref())
361 let found
= validate_packages_unique(resolve
, name
, &profile
.toml
)?
;
362 // We intentionally do not validate unmatched packages for config
363 // profiles, in case they are defined in a central location. This
364 // iterates over the manifest profiles only.
365 if let Some(profiles
) = profiles
{
366 if let Some(toml_profile
) = profiles
.get(name
) {
367 validate_packages_unmatched(shell
, resolve
, name
, toml_profile
, &found
)?
;
374 /// Returns the profile maker for the given profile name.
375 fn get_profile_maker(&self, name
: InternedString
) -> CargoResult
<&ProfileMaker
> {
378 .ok_or_else(|| anyhow
::format_err
!("profile `{}` is not defined", name
))
382 /// An object used for handling the profile hierarchy.
384 /// The precedence of profiles are (first one wins):
386 /// - Profiles in `.cargo/config` files (using same order as below).
387 /// - `[profile.dev.package.name]` -- a named package.
388 /// - `[profile.dev.package."*"]` -- this cannot apply to workspace members.
389 /// - `[profile.dev.build-override]` -- this can only apply to `build.rs` scripts
390 /// and their dependencies.
391 /// - `[profile.dev]`
392 /// - Default (hard-coded) values.
393 #[derive(Debug, Clone)]
394 struct ProfileMaker
{
395 /// The starting, hard-coded defaults for the profile.
397 /// The TOML profile defined in `Cargo.toml` or config.
399 /// This is None if the user did not specify one, in which case the
400 /// `default` is used. Note that the built-in defaults for test/bench/doc
401 /// always set this since they need to declare the `inherits` value.
402 toml
: Option
<TomlProfile
>,
406 /// Creates a new `ProfileMaker`.
408 /// Note that this does not process `inherits`, the caller is responsible for that.
409 fn new(default: Profile
, toml
: Option
<TomlProfile
>) -> ProfileMaker
{
410 ProfileMaker { default, toml }
413 /// Generates a new `Profile`.
416 pkg_id
: Option
<PackageId
>,
420 let mut profile
= self.default.clone();
422 // First apply profile-specific settings, things like
423 // `[profile.release]`
424 if let Some(toml
) = &self.toml
{
425 merge_profile(&mut profile
, toml
);
428 // Next start overriding those settings. First comes build dependencies
429 // which default to opt-level 0...
431 // For-host units are things like procedural macros, build scripts, and
432 // their dependencies. For these units most projects simply want them
433 // to compile quickly and the runtime doesn't matter too much since
434 // they tend to process very little data. For this reason we default
435 // them to a "compile as quickly as possible" mode which for now means
436 // basically turning down the optimization level and avoid limiting
437 // codegen units. This ensures that we spend little time optimizing as
438 // well as enabling parallelism by not constraining codegen units.
439 profile
.opt_level
= InternedString
::new("0");
440 profile
.codegen_units
= None
;
442 // For build dependencies, we usually don't need debuginfo, and
443 // removing it will compile faster. However, that can conflict with
444 // a unit graph optimization, reusing units that are shared between
445 // build dependencies and runtime dependencies: when the runtime
446 // target is the same as the build host, we only need to build a
447 // dependency once and reuse the results, instead of building twice.
448 // We defer the choice of the debuginfo level until we can check if
449 // a unit is shared. If that's the case, we'll use the deferred value
450 // below so the unit can be reused, otherwise we can avoid emitting
451 // the unit's debuginfo.
452 profile
.debuginfo
= DebugInfo
::Deferred(profile
.debuginfo
.into_inner());
454 // ... and next comes any other sorts of overrides specified in
455 // profiles, such as `[profile.release.build-override]` or
456 // `[profile.release.package.foo]`
457 if let Some(toml
) = &self.toml
{
458 merge_toml_overrides(pkg_id
, is_member
, is_for_host
, &mut profile
, toml
);
464 /// Merge package and build overrides from the given TOML profile into the given `Profile`.
465 fn merge_toml_overrides(
466 pkg_id
: Option
<PackageId
>,
469 profile
: &mut Profile
,
473 if let Some(build_override
) = &toml
.build_override
{
474 merge_profile(profile
, build_override
);
477 if let Some(overrides
) = toml
.package
.as_ref() {
479 if let Some(all
) = overrides
.get(&ProfilePackageSpec
::All
) {
480 merge_profile(profile
, all
);
483 if let Some(pkg_id
) = pkg_id
{
484 let mut matches
= overrides
486 .filter_map(|(key
, spec_profile
)| match *key
{
487 ProfilePackageSpec
::All
=> None
,
488 ProfilePackageSpec
::Spec(ref s
) => {
489 if s
.matches(pkg_id
) {
496 if let Some(spec_profile
) = matches
.next() {
497 merge_profile(profile
, spec_profile
);
498 // `validate_packages` should ensure that there are
499 // no additional matches.
501 matches
.next().is_none(),
502 "package `{}` matched multiple package profile overrides",
510 /// Merge the given TOML profile into the given `Profile`.
512 /// Does not merge overrides (see `merge_toml_overrides`).
513 fn merge_profile(profile
: &mut Profile
, toml
: &TomlProfile
) {
514 if let Some(ref opt_level
) = toml
.opt_level
{
515 profile
.opt_level
= InternedString
::new(&opt_level
.0);
518 Some(StringOrBool
::Bool(b
)) => profile
.lto
= Lto
::Bool(b
),
519 Some(StringOrBool
::String(ref n
)) if is_off(n
.as_str()) => profile
.lto
= Lto
::Off
,
520 Some(StringOrBool
::String(ref n
)) => profile
.lto
= Lto
::Named(InternedString
::new(n
)),
523 if toml
.codegen_backend
.is_some() {
524 profile
.codegen_backend
= toml
.codegen_backend
;
526 if toml
.codegen_units
.is_some() {
527 profile
.codegen_units
= toml
.codegen_units
;
529 if let Some(debuginfo
) = toml
.debug
{
530 profile
.debuginfo
= DebugInfo
::Resolved(debuginfo
);
532 if let Some(debug_assertions
) = toml
.debug_assertions
{
533 profile
.debug_assertions
= debug_assertions
;
535 if let Some(split_debuginfo
) = &toml
.split_debuginfo
{
536 profile
.split_debuginfo
= Some(InternedString
::new(split_debuginfo
));
538 if let Some(rpath
) = toml
.rpath
{
539 profile
.rpath
= rpath
;
541 if let Some(panic
) = &toml
.panic
{
542 profile
.panic
= match panic
.as_str() {
543 "unwind" => PanicStrategy
::Unwind
,
544 "abort" => PanicStrategy
::Abort
,
545 // This should be validated in TomlProfile::validate
546 _
=> panic
!("Unexpected panic setting `{}`", panic
),
549 if let Some(overflow_checks
) = toml
.overflow_checks
{
550 profile
.overflow_checks
= overflow_checks
;
552 if let Some(incremental
) = toml
.incremental
{
553 profile
.incremental
= incremental
;
555 if let Some(flags
) = &toml
.rustflags
{
556 profile
.rustflags
= flags
.clone();
558 profile
.strip
= match toml
.strip
{
559 Some(StringOrBool
::Bool(true)) => Strip
::Named(InternedString
::new("symbols")),
560 None
| Some(StringOrBool
::Bool(false)) => Strip
::None
,
561 Some(StringOrBool
::String(ref n
)) if n
.as_str() == "none" => Strip
::None
,
562 Some(StringOrBool
::String(ref n
)) => Strip
::Named(InternedString
::new(n
)),
566 /// The root profile (dev/release).
568 /// This is currently only used for the `PROFILE` env var for build scripts
569 /// for backwards compatibility. We should probably deprecate `PROFILE` and
570 /// encourage using things like `DEBUG` and `OPT_LEVEL` instead.
571 #[derive(Clone, Copy, Eq, PartialOrd, Ord, PartialEq, Debug)]
572 pub enum ProfileRoot
{
577 /// Profile settings used to determine which compiler flags to use for a
579 #[derive(Clone, Eq, PartialOrd, Ord, serde::Serialize)]
581 pub name
: InternedString
,
582 pub opt_level
: InternedString
,
583 #[serde(skip)] // named profiles are unstable
584 pub root
: ProfileRoot
,
586 // `None` means use rustc default.
587 pub codegen_backend
: Option
<InternedString
>,
588 // `None` means use rustc default.
589 pub codegen_units
: Option
<u32>,
590 pub debuginfo
: DebugInfo
,
591 pub split_debuginfo
: Option
<InternedString
>,
592 pub debug_assertions
: bool
,
593 pub overflow_checks
: bool
,
595 pub incremental
: bool
,
596 pub panic
: PanicStrategy
,
598 #[serde(skip_serializing_if = "Vec::is_empty")] // remove when `rustflags` is stablized
599 // Note that `rustflags` is used for the cargo-feature `profile_rustflags`
600 pub rustflags
: Vec
<InternedString
>,
603 impl Default
for Profile
{
604 fn default() -> Profile
{
606 name
: InternedString
::new(""),
607 opt_level
: InternedString
::new("0"),
608 root
: ProfileRoot
::Debug
,
609 lto
: Lto
::Bool(false),
610 codegen_backend
: None
,
612 debuginfo
: DebugInfo
::Resolved(TomlDebugInfo
::None
),
613 debug_assertions
: false,
614 split_debuginfo
: None
,
615 overflow_checks
: false,
618 panic
: PanicStrategy
::Unwind
,
626 impl fmt
::Debug
for Profile
{
627 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
628 let (default, default_name
) = match self.name
.as_str() {
629 "dev" => (Profile
::default_dev(), "default_dev()"),
630 "release" => (Profile
::default_release(), "default_release()"),
631 _
=> (Profile
::default(), "default()"),
654 impl fmt
::Display
for Profile
{
655 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
656 write
!(f
, "Profile({})", self.name
)
660 impl hash
::Hash
for Profile
{
661 fn hash
<H
>(&self, state
: &mut H
)
665 self.comparable().hash(state
);
669 impl cmp
::PartialEq
for Profile
{
670 fn eq(&self, other
: &Self) -> bool
{
671 self.comparable() == other
.comparable()
676 /// Returns a built-in `dev` profile.
677 fn default_dev() -> Profile
{
679 name
: InternedString
::new("dev"),
680 root
: ProfileRoot
::Debug
,
681 debuginfo
: DebugInfo
::Resolved(TomlDebugInfo
::Full
),
682 debug_assertions
: true,
683 overflow_checks
: true,
689 /// Returns a built-in `release` profile.
690 fn default_release() -> Profile
{
692 name
: InternedString
::new("release"),
693 root
: ProfileRoot
::Release
,
694 opt_level
: InternedString
::new("3"),
699 /// Compares all fields except `name`, which doesn't affect compilation.
700 /// This is necessary for `Unit` deduplication for things like "test" and
701 /// "dev" which are essentially the same.
702 fn comparable(&self) -> impl Hash
+ Eq
+ '_
{
706 self.codegen_backend
,
709 self.split_debuginfo
,
710 self.debug_assertions
,
711 self.overflow_checks
,
713 (self.incremental
, self.panic
, self.strip
),
719 /// The debuginfo level setting.
721 /// This is semantically a [`TomlDebugInfo`], and should be used as so via the
722 /// [`DebugInfo::into_inner`] method for all intents and purposes.
724 /// Internally, it's used to model a debuginfo level whose value can be deferred
725 /// for optimization purposes: host dependencies usually don't need the same
726 /// level as target dependencies. For dependencies that are shared between the
727 /// two however, that value also affects reuse: different debuginfo levels would
728 /// cause to build a unit twice. By deferring the choice until we know
729 /// whether to choose the optimized value or the default value, we can make sure
730 /// the unit is only built once and the unit graph is still optimized.
731 #[derive(Debug, Copy, Clone, serde::Serialize)]
734 /// A debuginfo level that is fixed and will not change.
736 /// This can be set by a profile, user, or default value.
737 Resolved(TomlDebugInfo
),
738 /// For internal purposes: a deferred debuginfo level that can be optimized
739 /// away, but has this value otherwise.
741 /// Behaves like `Resolved` in all situations except for the default build
742 /// dependencies profile: whenever a build dependency is not shared with
743 /// runtime dependencies, this level is weakened to a lower level that is
744 /// faster to build (see [`DebugInfo::weaken`]).
746 /// In all other situations, this level value will be the one to use.
747 Deferred(TomlDebugInfo
),
751 /// The main way to interact with this debuginfo level, turning it into a [`TomlDebugInfo`].
752 pub fn into_inner(self) -> TomlDebugInfo
{
754 DebugInfo
::Resolved(v
) | DebugInfo
::Deferred(v
) => v
,
758 /// Returns true if any debuginfo will be generated. Helper
759 /// for a common operation on the usual `Option` representation.
760 pub(crate) fn is_turned_on(&self) -> bool
{
761 !matches
!(self.into_inner(), TomlDebugInfo
::None
)
764 pub(crate) fn is_deferred(&self) -> bool
{
765 matches
!(self, DebugInfo
::Deferred(_
))
768 /// Force the deferred, preferred, debuginfo level to a finalized explicit value.
769 pub(crate) fn finalize(self) -> Self {
771 DebugInfo
::Deferred(v
) => DebugInfo
::Resolved(v
),
776 /// Reset to the lowest level: no debuginfo.
777 pub(crate) fn weaken(self) -> Self {
778 DebugInfo
::Resolved(TomlDebugInfo
::None
)
782 impl PartialEq
for DebugInfo
{
783 fn eq(&self, other
: &DebugInfo
) -> bool
{
784 self.into_inner().eq(&other
.into_inner())
788 impl Eq
for DebugInfo {}
790 impl Hash
for DebugInfo
{
791 fn hash
<H
: std
::hash
::Hasher
>(&self, state
: &mut H
) {
792 self.into_inner().hash(state
);
796 impl PartialOrd
for DebugInfo
{
797 fn partial_cmp(&self, other
: &Self) -> Option
<std
::cmp
::Ordering
> {
798 self.into_inner().partial_cmp(&other
.into_inner())
802 impl Ord
for DebugInfo
{
803 fn cmp(&self, other
: &Self) -> std
::cmp
::Ordering
{
804 self.into_inner().cmp(&other
.into_inner())
808 /// The link-time-optimization setting.
809 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
811 /// Explicitly no LTO, disables thin-LTO.
814 /// False = rustc default (no args), currently "thin LTO"
816 /// Named LTO settings like "thin".
817 Named(InternedString
),
820 impl serde
::ser
::Serialize
for Lto
{
821 fn serialize
<S
>(&self, s
: S
) -> Result
<S
::Ok
, S
::Error
>
823 S
: serde
::ser
::Serializer
,
826 Lto
::Off
=> "off".serialize(s
),
827 Lto
::Bool(b
) => b
.to_string().serialize(s
),
828 Lto
::Named(n
) => n
.serialize(s
),
833 /// The `panic` setting.
834 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord, serde::Serialize)]
835 #[serde(rename_all = "lowercase")]
836 pub enum PanicStrategy
{
841 impl fmt
::Display
for PanicStrategy
{
842 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
844 PanicStrategy
::Unwind
=> "unwind",
845 PanicStrategy
::Abort
=> "abort",
851 /// The setting for choosing which symbols to strip
853 Clone
, Copy
, PartialEq
, Eq
, Debug
, Hash
, PartialOrd
, Ord
, serde
::Serialize
, serde
::Deserialize
,
855 #[serde(rename_all = "lowercase")]
857 /// Don't remove any symbols
859 /// Named Strip settings
860 Named(InternedString
),
863 impl fmt
::Display
for Strip
{
864 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
866 Strip
::None
=> "none",
867 Strip
::Named(s
) => s
.as_str(),
873 /// Flags used in creating `Unit`s to indicate the purpose for the target, and
874 /// to ensure the target's dependencies have the correct settings.
876 /// This means these are passed down from the root of the dependency tree to apply
877 /// to most child dependencies.
878 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
880 /// A target for `build.rs` or any of its dependencies, or a proc-macro or
881 /// any of its dependencies. This enables `build-override` profiles for
884 /// An invariant is that if `host_features` is true, `host` must be true.
886 /// Note that this is `true` for `RunCustomBuild` units, even though that
887 /// unit should *not* use build-override profiles. This is a bit of a
888 /// special case. When computing the `RunCustomBuild` unit, it manually
889 /// uses the `get_profile_run_custom_build` method to get the correct
890 /// profile information for the unit. `host` needs to be true so that all
891 /// of the dependencies of that `RunCustomBuild` unit have this flag be
892 /// sticky (and forced to `true` for all further dependencies) — which is
893 /// the whole point of `UnitFor`.
895 /// A target for a build dependency or proc-macro (or any of its
896 /// dependencies). This is used for computing features of build
897 /// dependencies and proc-macros independently of other dependency kinds.
899 /// The subtle difference between this and `host` is that the build script
900 /// for a non-host package sets this to `false` because it wants the
901 /// features of the non-host package (whereas `host` is true because the
902 /// build script is being built for the host). `host_features` becomes
903 /// `true` for build-dependencies or proc-macros, or any of their
904 /// dependencies. For example, with this dependency tree:
908 /// ├── foo build.rs
909 /// │ └── shared_dep (BUILD dependency)
910 /// │ └── shared_dep build.rs
911 /// └── shared_dep (Normal dependency)
912 /// └── shared_dep build.rs
915 /// In this example, `foo build.rs` is HOST=true, HOST_FEATURES=false.
916 /// This is so that `foo build.rs` gets the profile settings for build
917 /// scripts (HOST=true) and features of foo (HOST_FEATURES=false) because
918 /// build scripts need to know which features their package is being built
921 /// But in the case of `shared_dep`, when built as a build dependency,
922 /// both flags are true (it only wants the build-dependency features).
923 /// When `shared_dep` is built as a normal dependency, then `shared_dep
924 /// build.rs` is HOST=true, HOST_FEATURES=false for the same reasons that
925 /// foo's build script is set that way.
927 /// How Cargo processes the `panic` setting or profiles.
928 panic_setting
: PanicSetting
,
930 /// The compile kind of the root unit for which artifact dependencies are built.
931 /// This is required particularly for the `target = "target"` setting of artifact
932 /// dependencies which mean to inherit the `--target` specified on the command-line.
933 /// However, that is a multi-value argument and root units are already created to
934 /// reflect one unit per --target. Thus we have to build one artifact with the
935 /// correct target for each of these trees.
936 /// Note that this will always be set as we don't initially know if there are
937 /// artifacts that make use of it.
938 root_compile_kind
: CompileKind
,
940 /// This is only set for artifact dependencies which have their
941 /// `<target-triple>|target` set.
942 /// If so, this information is used as part of the key for resolving their features,
943 /// allowing for target-dependent feature resolution within the entire dependency tree.
944 /// Note that this target corresponds to the target used to build the units in that
945 /// dependency tree, too, but this copy of it is specifically used for feature lookup.
946 artifact_target_for_features
: Option
<CompileTarget
>,
949 /// How Cargo processes the `panic` setting or profiles.
951 /// This is done to handle test/benches inheriting from dev/release,
952 /// as well as forcing `for_host` units to always unwind.
953 /// It also interacts with [`-Z panic-abort-tests`].
955 /// [`-Z panic-abort-tests`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#panic-abort-tests
956 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
958 /// Used to force a unit to always be compiled with the `panic=unwind`
959 /// strategy, notably for build scripts, proc macros, etc.
962 /// Indicates that this unit will read its `profile` setting and use
963 /// whatever is configured there.
968 /// A unit for a normal target/dependency (i.e., not custom build,
969 /// proc macro/plugin, or test/bench).
970 pub fn new_normal(root_compile_kind
: CompileKind
) -> UnitFor
{
973 host_features
: false,
974 panic_setting
: PanicSetting
::ReadProfile
,
976 artifact_target_for_features
: None
,
980 /// A unit for a custom build script or proc-macro or its dependencies.
982 /// The `host_features` parameter is whether or not this is for a build
983 /// dependency or proc-macro (something that requires being built "on the
984 /// host"). Build scripts for non-host units should use `false` because
985 /// they want to use the features of the package they are running for.
986 pub fn new_host(host_features
: bool
, root_compile_kind
: CompileKind
) -> UnitFor
{
990 // Force build scripts to always use `panic=unwind` for now to
991 // maximally share dependencies with procedural macros.
992 panic_setting
: PanicSetting
::AlwaysUnwind
,
994 artifact_target_for_features
: None
,
998 /// A unit for a compiler plugin or their dependencies.
999 pub fn new_compiler(root_compile_kind
: CompileKind
) -> UnitFor
{
1002 // The feature resolver doesn't know which dependencies are
1003 // plugins, so for now plugins don't split features. Since plugins
1004 // are mostly deprecated, just leave this as false.
1005 host_features
: false,
1006 // Force plugins to use `panic=abort` so panics in the compiler do
1007 // not abort the process but instead end with a reasonable error
1008 // message that involves catching the panic in the compiler.
1009 panic_setting
: PanicSetting
::AlwaysUnwind
,
1011 artifact_target_for_features
: None
,
1015 /// A unit for a test/bench target or their dependencies.
1017 /// Note that `config` is taken here for unstable CLI features to detect
1018 /// whether `panic=abort` is supported for tests. Historical versions of
1019 /// rustc did not support this, but newer versions do with an unstable
1021 pub fn new_test(config
: &Config
, root_compile_kind
: CompileKind
) -> UnitFor
{
1024 host_features
: false,
1025 // We're testing out an unstable feature (`-Zpanic-abort-tests`)
1026 // which inherits the panic setting from the dev/release profile
1027 // (basically avoid recompiles) but historical defaults required
1028 // that we always unwound.
1029 panic_setting
: if config
.cli_unstable().panic_abort_tests
{
1030 PanicSetting
::ReadProfile
1032 PanicSetting
::AlwaysUnwind
1035 artifact_target_for_features
: None
,
1039 /// This is a special case for unit tests of a proc-macro.
1041 /// Proc-macro unit tests are forced to be run on the host.
1042 pub fn new_host_test(config
: &Config
, root_compile_kind
: CompileKind
) -> UnitFor
{
1043 let mut unit_for
= UnitFor
::new_test(config
, root_compile_kind
);
1044 unit_for
.host
= true;
1045 unit_for
.host_features
= true;
1049 /// Returns a new copy updated based on the target dependency.
1051 /// This is where the magic happens that the host/host_features settings
1052 /// transition in a sticky fashion. As the dependency graph is being
1053 /// built, once those flags are set, they stay set for the duration of
1054 /// that portion of tree.
1055 pub fn with_dependency(
1058 dep_target
: &Target
,
1059 root_compile_kind
: CompileKind
,
1061 // A build script or proc-macro transitions this to being built for the host.
1062 let dep_for_host
= dep_target
.for_host();
1063 // This is where feature decoupling of host versus target happens.
1065 // Once host features are desired, they are always desired.
1067 // A proc-macro should always use host features.
1069 // Dependencies of a build script should use host features (subtle
1070 // point: the build script itself does *not* use host features, that's
1071 // why the parent is checked here, and not the dependency).
1073 self.host_features
|| parent
.target
.is_custom_build() || dep_target
.proc_macro();
1074 // Build scripts and proc macros, and all of their dependencies are
1076 let panic_setting
= if dep_for_host
{
1077 PanicSetting
::AlwaysUnwind
1082 host
: self.host
|| dep_for_host
,
1086 artifact_target_for_features
: self.artifact_target_for_features
,
1090 pub fn for_custom_build(self) -> UnitFor
{
1093 host_features
: self.host_features
,
1094 // Force build scripts to always use `panic=unwind` for now to
1095 // maximally share dependencies with procedural macros.
1096 panic_setting
: PanicSetting
::AlwaysUnwind
,
1097 root_compile_kind
: self.root_compile_kind
,
1098 artifact_target_for_features
: self.artifact_target_for_features
,
1102 /// Set the artifact compile target for use in features using the given `artifact`.
1103 pub(crate) fn with_artifact_features(mut self, artifact
: &Artifact
) -> UnitFor
{
1104 self.artifact_target_for_features
= artifact
.target().and_then(|t
| t
.to_compile_target());
1108 /// Set the artifact compile target as determined by a resolved compile target. This is used if `target = "target"`.
1109 pub(crate) fn with_artifact_features_from_resolved_compile_kind(
1111 kind
: Option
<CompileKind
>,
1113 self.artifact_target_for_features
= kind
.and_then(|kind
| match kind
{
1114 CompileKind
::Host
=> None
,
1115 CompileKind
::Target(triple
) => Some(triple
),
1120 /// Returns `true` if this unit is for a build script or any of its
1121 /// dependencies, or a proc macro or any of its dependencies.
1122 pub fn is_for_host(&self) -> bool
{
1126 pub fn is_for_host_features(&self) -> bool
{
1130 /// Returns how `panic` settings should be handled for this profile
1131 fn panic_setting(&self) -> PanicSetting
{
1135 /// We might contain a parent artifact compile kind for features already, but will
1136 /// gladly accept the one of this dependency as an override as it defines how
1137 /// the artifact is built.
1138 /// If we are an artifact but don't specify a `target`, we assume the default
1139 /// compile kind that is suitable in this situation.
1140 pub(crate) fn map_to_features_for(&self, dep_artifact
: Option
<&Artifact
>) -> FeaturesFor
{
1141 FeaturesFor
::from_for_host_or_artifact_target(
1142 self.is_for_host_features(),
1143 match dep_artifact
{
1144 Some(artifact
) => artifact
1146 .and_then(|t
| t
.to_resolved_compile_target(self.root_compile_kind
)),
1147 None
=> self.artifact_target_for_features
,
1152 pub(crate) fn root_compile_kind(&self) -> CompileKind
{
1153 self.root_compile_kind
1157 /// Takes the manifest profiles, and overlays the config profiles on-top.
1159 /// Returns a new copy of the profile map with all the mergers complete.
1160 fn merge_config_profiles(
1162 requested_profile
: InternedString
,
1163 ) -> CargoResult
<BTreeMap
<InternedString
, TomlProfile
>> {
1164 let mut profiles
= match ws
.profiles() {
1165 Some(profiles
) => profiles
.get_all().clone(),
1166 None
=> BTreeMap
::new(),
1168 // Set of profile names to check if defined in config only.
1169 let mut check_to_add
= HashSet
::new();
1170 check_to_add
.insert(requested_profile
);
1171 // Merge config onto manifest profiles.
1172 for (name
, profile
) in &mut profiles
{
1173 if let Some(config_profile
) = get_config_profile(ws
, name
)?
{
1174 profile
.merge(&config_profile
);
1176 if let Some(inherits
) = &profile
.inherits
{
1177 check_to_add
.insert(*inherits
);
1180 // Add the built-in profiles. This is important for things like `cargo
1181 // test` which implicitly use the "dev" profile for dependencies.
1182 for name
in &["dev", "release", "test", "bench"] {
1183 check_to_add
.insert(InternedString
::new(name
));
1185 // Add config-only profiles.
1186 // Need to iterate repeatedly to get all the inherits values.
1187 let mut current
= HashSet
::new();
1188 while !check_to_add
.is_empty() {
1189 std
::mem
::swap(&mut current
, &mut check_to_add
);
1190 for name
in current
.drain() {
1191 if !profiles
.contains_key(&name
) {
1192 if let Some(config_profile
) = get_config_profile(ws
, &name
)?
{
1193 if let Some(inherits
) = &config_profile
.inherits
{
1194 check_to_add
.insert(*inherits
);
1196 profiles
.insert(name
, config_profile
);
1204 /// Helper for fetching a profile from config.
1205 fn get_config_profile(ws
: &Workspace
<'_
>, name
: &str) -> CargoResult
<Option
<TomlProfile
>> {
1206 let profile
: Option
<config
::Value
<TomlProfile
>> =
1207 ws
.config().get(&format
!("profile.{}", name
))?
;
1208 let Some(profile
) = profile
else {
1211 let mut warnings
= Vec
::new();
1216 ws
.config().cli_unstable(),
1217 ws
.unstable_features(),
1222 "config profile `{}` is not valid (defined in `{}`)",
1223 name
, profile
.definition
1226 for warning
in warnings
{
1227 ws
.config().shell().warn(warning
)?
;
1229 Ok(Some(profile
.val
))
1232 /// Validate that a package does not match multiple package override specs.
1234 /// For example `[profile.dev.package.bar]` and `[profile.dev.package."bar:0.5.0"]`
1235 /// would both match `bar:0.5.0` which would be ambiguous.
1236 fn validate_packages_unique(
1239 toml
: &Option
<TomlProfile
>,
1240 ) -> CargoResult
<HashSet
<PackageIdSpec
>> {
1241 let Some(toml
) = toml
else {
1242 return Ok(HashSet
::new());
1244 let Some(overrides
) = toml
.package
.as_ref() else {
1245 return Ok(HashSet
::new());
1247 // Verify that a package doesn't match multiple spec overrides.
1248 let mut found
= HashSet
::new();
1249 for pkg_id
in resolve
.iter() {
1250 let matches
: Vec
<&PackageIdSpec
> = overrides
1252 .filter_map(|key
| match *key
{
1253 ProfilePackageSpec
::All
=> None
,
1254 ProfilePackageSpec
::Spec(ref spec
) => {
1255 if spec
.matches(pkg_id
) {
1263 match matches
.len() {
1266 found
.insert(matches
[0].clone());
1271 .map(|spec
| spec
.to_string())
1272 .collect
::<Vec
<_
>>()
1275 "multiple package overrides in profile `{}` match package `{}`\n\
1276 found package specs: {}",
1287 /// Check for any profile override specs that do not match any known packages.
1289 /// This helps check for typos and mistakes.
1290 fn validate_packages_unmatched(
1295 found
: &HashSet
<PackageIdSpec
>,
1296 ) -> CargoResult
<()> {
1297 let Some(overrides
) = toml
.package
.as_ref() else {
1301 // Verify every override matches at least one package.
1302 let missing_specs
= overrides
.keys().filter_map(|key
| {
1303 if let ProfilePackageSpec
::Spec(ref spec
) = *key
{
1304 if !found
.contains(spec
) {
1310 for spec
in missing_specs
{
1311 // See if there is an exact name match.
1312 let name_matches
: Vec
<String
> = resolve
1314 .filter_map(|pkg_id
| {
1315 if pkg_id
.name() == spec
.name() {
1316 Some(pkg_id
.to_string())
1322 if name_matches
.is_empty() {
1323 let suggestion
= closest_msg(&spec
.name(), resolve
.iter(), |p
| p
.name().as_str());
1325 "profile package spec `{}` in profile `{}` did not match any packages{}",
1326 spec
, name
, suggestion
1330 "profile package spec `{}` in profile `{}` \
1331 has a version or URL that does not match any of the packages: {}",
1334 name_matches
.join(", ")
1341 /// Returns `true` if a string is a toggle that turns an option off.
1342 fn is_off(s
: &str) -> bool
{
1343 matches
!(s
, "off" | "n" | "no" | "none")