1use crate::core::compiler::CompileMode;
2use crate::core::interning::InternedString;
3use crate::core::resolver::features::FeaturesFor;
4use crate::core::{Feature, Features, PackageId, PackageIdSpec, Resolve, Shell};
5use crate::util::errors::CargoResultExt;
6use crate::util::toml::{ProfilePackageSpec, StringOrBool, TomlProfile, TomlProfiles, U32OrBool};
7use crate::util::{closest_msg, config, CargoResult, Config};
8use anyhow::bail;
9use std::collections::{BTreeMap, HashMap, HashSet};
10use std::{cmp, env, fmt, hash};
11
12#[derive(Clone, Debug)]
14pub struct Profiles {
15 incremental: Option<bool>,
19 dir_names: HashMap<InternedString, InternedString>,
21 by_name: HashMap<InternedString, ProfileMaker>,
23 named_profiles_enabled: bool,
25 requested_profile: InternedString,
27}
28
29impl Profiles {
30 pub fn new(
31 profiles: Option<&TomlProfiles>,
32 config: &Config,
33 requested_profile: InternedString,
34 features: &Features,
35 ) -> CargoResult<Profiles> {
36 let incremental = match env::var_os("CARGO_INCREMENTAL") {
37 Some(v) => Some(v == "1"),
38 None => config.build_config()?.incremental,
39 };
40 let mut profiles = merge_config_profiles(profiles, config, requested_profile, features)?;
41
42 if !features.is_enabled(Feature::named_profiles()) {
43 let mut profile_makers = Profiles {
44 incremental,
45 named_profiles_enabled: false,
46 dir_names: Self::predefined_dir_names(),
47 by_name: HashMap::new(),
48 requested_profile,
49 };
50
51 profile_makers.by_name.insert(
52 InternedString::new("dev"),
53 ProfileMaker::new(Profile::default_dev(), profiles.remove("dev")),
54 );
55 profile_makers
56 .dir_names
57 .insert(InternedString::new("dev"), InternedString::new("debug"));
58
59 profile_makers.by_name.insert(
60 InternedString::new("release"),
61 ProfileMaker::new(Profile::default_release(), profiles.remove("release")),
62 );
63 profile_makers.dir_names.insert(
64 InternedString::new("release"),
65 InternedString::new("release"),
66 );
67
68 profile_makers.by_name.insert(
69 InternedString::new("test"),
70 ProfileMaker::new(Profile::default_test(), profiles.remove("test")),
71 );
72 profile_makers
73 .dir_names
74 .insert(InternedString::new("test"), InternedString::new("debug"));
75
76 profile_makers.by_name.insert(
77 InternedString::new("bench"),
78 ProfileMaker::new(Profile::default_bench(), profiles.remove("bench")),
79 );
80 profile_makers
81 .dir_names
82 .insert(InternedString::new("bench"), InternedString::new("release"));
83
84 profile_makers.by_name.insert(
85 InternedString::new("doc"),
86 ProfileMaker::new(Profile::default_doc(), profiles.remove("doc")),
87 );
88 profile_makers
89 .dir_names
90 .insert(InternedString::new("doc"), InternedString::new("debug"));
91
92 return Ok(profile_makers);
93 }
94
95 let mut profile_makers = Profiles {
96 incremental,
97 named_profiles_enabled: true,
98 dir_names: Self::predefined_dir_names(),
99 by_name: HashMap::new(),
100 requested_profile,
101 };
102
103 Self::add_root_profiles(&mut profile_makers, &profiles)?;
104
105 use std::collections::btree_map::Entry;
107 for (predef_name, mut predef_prof) in Self::predefined_profiles().into_iter() {
108 match profiles.entry(InternedString::new(predef_name)) {
109 Entry::Vacant(vac) => {
110 vac.insert(predef_prof);
111 }
112 Entry::Occupied(mut oc) => {
113 let r = oc.get_mut();
115 predef_prof.merge(r);
116 *r = predef_prof;
117 }
118 }
119 }
120
121 for (name, profile) in &profiles {
122 profile_makers.add_maker(*name, profile, &profiles)?;
123 }
124 profile_makers.get_profile_maker(requested_profile)?;
128 Ok(profile_makers)
129 }
130
131 fn predefined_dir_names() -> HashMap<InternedString, InternedString> {
133 let mut dir_names = HashMap::new();
134 dir_names.insert(InternedString::new("dev"), InternedString::new("debug"));
135 dir_names.insert(InternedString::new("check"), InternedString::new("debug"));
136 dir_names.insert(InternedString::new("test"), InternedString::new("debug"));
137 dir_names.insert(InternedString::new("bench"), InternedString::new("release"));
138 dir_names
139 }
140
141 fn add_root_profiles(
144 profile_makers: &mut Profiles,
145 profiles: &BTreeMap<InternedString, TomlProfile>,
146 ) -> CargoResult<()> {
147 profile_makers.by_name.insert(
148 InternedString::new("dev"),
149 ProfileMaker::new(Profile::default_dev(), profiles.get("dev").cloned()),
150 );
151
152 profile_makers.by_name.insert(
153 InternedString::new("release"),
154 ProfileMaker::new(Profile::default_release(), profiles.get("release").cloned()),
155 );
156 Ok(())
157 }
158
159 fn predefined_profiles() -> Vec<(&'static str, TomlProfile)> {
162 vec![
163 (
164 "bench",
165 TomlProfile {
166 inherits: Some(InternedString::new("release")),
167 ..TomlProfile::default()
168 },
169 ),
170 (
171 "test",
172 TomlProfile {
173 inherits: Some(InternedString::new("dev")),
174 ..TomlProfile::default()
175 },
176 ),
177 (
178 "check",
179 TomlProfile {
180 inherits: Some(InternedString::new("dev")),
181 ..TomlProfile::default()
182 },
183 ),
184 (
185 "doc",
186 TomlProfile {
187 inherits: Some(InternedString::new("dev")),
188 ..TomlProfile::default()
189 },
190 ),
191 ]
192 }
193
194 fn add_maker(
196 &mut self,
197 name: InternedString,
198 profile: &TomlProfile,
199 profiles: &BTreeMap<InternedString, TomlProfile>,
200 ) -> CargoResult<()> {
201 match &profile.dir_name {
202 None => {}
203 Some(dir_name) => {
204 self.dir_names.insert(name, dir_name.to_owned());
205 }
206 }
207
208 if name == "dev" || name == "release" {
210 if profile.inherits.is_some() {
211 bail!(
212 "`inherits` must not be specified in root profile `{}`",
213 name
214 );
215 }
216 return Ok(());
218 }
219
220 let mut set = HashSet::new();
222 set.insert(name);
223 let maker = self.process_chain(name, profile, &mut set, profiles)?;
224 self.by_name.insert(name, maker);
225 Ok(())
226 }
227
228 fn process_chain(
237 &mut self,
238 name: InternedString,
239 profile: &TomlProfile,
240 set: &mut HashSet<InternedString>,
241 profiles: &BTreeMap<InternedString, TomlProfile>,
242 ) -> CargoResult<ProfileMaker> {
243 let mut maker = match profile.inherits {
244 Some(inherits_name) if inherits_name == "dev" || inherits_name == "release" => {
245 self.get_profile_maker(inherits_name).unwrap().clone()
247 }
248 Some(inherits_name) => {
249 if !set.insert(inherits_name) {
250 bail!(
251 "profile inheritance loop detected with profile `{}` inheriting `{}`",
252 name,
253 inherits_name
254 );
255 }
256
257 match profiles.get(&inherits_name) {
258 None => {
259 bail!(
260 "profile `{}` inherits from `{}`, but that profile is not defined",
261 name,
262 inherits_name
263 );
264 }
265 Some(parent) => self.process_chain(inherits_name, parent, set, profiles)?,
266 }
267 }
268 None => {
269 bail!(
270 "profile `{}` is missing an `inherits` directive \
271 (`inherits` is required for all profiles except `dev` or `release`)",
272 name
273 );
274 }
275 };
276 match &mut maker.toml {
277 Some(toml) => toml.merge(profile),
278 None => maker.toml = Some(profile.clone()),
279 };
280 Ok(maker)
281 }
282
283 pub fn get_profile(
287 &self,
288 pkg_id: PackageId,
289 is_member: bool,
290 unit_for: UnitFor,
291 mode: CompileMode,
292 ) -> Profile {
293 let (profile_name, inherits) = if !self.named_profiles_enabled {
294 let release = match self.requested_profile.as_str() {
299 "release" | "bench" => true,
300 _ => false,
301 };
302
303 match mode {
304 CompileMode::Test | CompileMode::Bench => {
305 if release {
306 (
307 InternedString::new("bench"),
308 Some(InternedString::new("release")),
309 )
310 } else {
311 (
312 InternedString::new("test"),
313 Some(InternedString::new("dev")),
314 )
315 }
316 }
317 CompileMode::Build
318 | CompileMode::Check { .. }
319 | CompileMode::Doctest
320 | CompileMode::RunCustomBuild => {
321 if release {
326 (InternedString::new("release"), None)
327 } else {
328 (InternedString::new("dev"), None)
329 }
330 }
331 CompileMode::Doc { .. } => (InternedString::new("doc"), None),
332 }
333 } else {
334 (self.requested_profile, None)
335 };
336 let maker = self.get_profile_maker(profile_name).unwrap();
337 let mut profile = maker.get_profile(Some(pkg_id), is_member, unit_for);
338
339 match unit_for.panic_setting() {
342 PanicSetting::AlwaysUnwind => profile.panic = PanicStrategy::Unwind,
343 PanicSetting::ReadProfile => {}
344 PanicSetting::Inherit => {
345 if let Some(inherits) = inherits {
346 let maker = self.get_profile_maker(inherits).unwrap();
348 profile.panic = maker.get_profile(Some(pkg_id), is_member, unit_for).panic;
349 }
350 }
351 }
352
353 if let Some(v) = self.incremental {
355 profile.incremental = v;
356 }
357 if !pkg_id.source_id().is_path() {
364 profile.incremental = false;
365 }
366 profile.name = profile_name;
367 profile
368 }
369
370 pub fn get_profile_run_custom_build(&self, for_unit_profile: &Profile) -> Profile {
376 let mut result = Profile::default();
377 result.name = for_unit_profile.name;
378 result.root = for_unit_profile.root;
379 result.debuginfo = for_unit_profile.debuginfo;
380 result.opt_level = for_unit_profile.opt_level;
381 result
382 }
383
384 pub fn base_profile(&self) -> Profile {
388 let profile_name = if !self.named_profiles_enabled {
389 match self.requested_profile.as_str() {
390 "release" | "bench" => self.requested_profile,
391 _ => InternedString::new("dev"),
392 }
393 } else {
394 self.requested_profile
395 };
396
397 let maker = self.get_profile_maker(profile_name).unwrap();
398 maker.get_profile(None, true, UnitFor::new_normal())
399 }
400
401 pub fn get_dir_name(&self) -> InternedString {
403 *self
404 .dir_names
405 .get(&self.requested_profile)
406 .unwrap_or(&self.requested_profile)
407 }
408
409 pub fn validate_packages(
411 &self,
412 profiles: Option<&TomlProfiles>,
413 shell: &mut Shell,
414 resolve: &Resolve,
415 ) -> CargoResult<()> {
416 for (name, profile) in &self.by_name {
417 let found = validate_packages_unique(resolve, name, &profile.toml)?;
418 if let Some(profiles) = profiles {
422 if let Some(toml_profile) = profiles.get(name) {
423 validate_packages_unmatched(shell, resolve, name, toml_profile, &found)?;
424 }
425 }
426 }
427 Ok(())
428 }
429
430 fn get_profile_maker(&self, name: InternedString) -> CargoResult<&ProfileMaker> {
432 self.by_name
433 .get(&name)
434 .ok_or_else(|| anyhow::format_err!("profile `{}` is not defined", name))
435 }
436}
437
438#[derive(Debug, Clone)]
449struct ProfileMaker {
450 default: Profile,
452 toml: Option<TomlProfile>,
454}
455
456impl ProfileMaker {
457 fn new(default: Profile, toml: Option<TomlProfile>) -> ProfileMaker {
461 ProfileMaker { default, toml }
462 }
463
464 fn get_profile(
466 &self,
467 pkg_id: Option<PackageId>,
468 is_member: bool,
469 unit_for: UnitFor,
470 ) -> Profile {
471 let mut profile = self.default;
472 if let Some(toml) = &self.toml {
473 merge_profile(&mut profile, toml);
474 merge_toml_overrides(pkg_id, is_member, unit_for, &mut profile, toml);
475 }
476 profile
477 }
478}
479
480fn merge_toml_overrides(
482 pkg_id: Option<PackageId>,
483 is_member: bool,
484 unit_for: UnitFor,
485 profile: &mut Profile,
486 toml: &TomlProfile,
487) {
488 if unit_for.is_for_host() {
489 if let Some(ref build_override) = toml.build_override {
490 merge_profile(profile, build_override);
491 }
492 }
493 if let Some(overrides) = toml.package.as_ref() {
494 if !is_member {
495 if let Some(all) = overrides.get(&ProfilePackageSpec::All) {
496 merge_profile(profile, all);
497 }
498 }
499 if let Some(pkg_id) = pkg_id {
500 let mut matches = overrides
501 .iter()
502 .filter_map(|(key, spec_profile)| match *key {
503 ProfilePackageSpec::All => None,
504 ProfilePackageSpec::Spec(ref s) => {
505 if s.matches(pkg_id) {
506 Some(spec_profile)
507 } else {
508 None
509 }
510 }
511 });
512 if let Some(spec_profile) = matches.next() {
513 merge_profile(profile, spec_profile);
514 assert!(
517 matches.next().is_none(),
518 "package `{}` matched multiple package profile overrides",
519 pkg_id
520 );
521 }
522 }
523 }
524}
525
526fn merge_profile(profile: &mut Profile, toml: &TomlProfile) {
530 if let Some(ref opt_level) = toml.opt_level {
531 profile.opt_level = InternedString::new(&opt_level.0);
532 }
533 match toml.lto {
534 Some(StringOrBool::Bool(b)) => profile.lto = Lto::Bool(b),
535 Some(StringOrBool::String(ref n)) => profile.lto = Lto::Named(InternedString::new(n)),
536 None => {}
537 }
538 if toml.codegen_units.is_some() {
539 profile.codegen_units = toml.codegen_units;
540 }
541 match toml.debug {
542 Some(U32OrBool::U32(debug)) => profile.debuginfo = Some(debug),
543 Some(U32OrBool::Bool(true)) => profile.debuginfo = Some(2),
544 Some(U32OrBool::Bool(false)) => profile.debuginfo = None,
545 None => {}
546 }
547 if let Some(debug_assertions) = toml.debug_assertions {
548 profile.debug_assertions = debug_assertions;
549 }
550 if let Some(rpath) = toml.rpath {
551 profile.rpath = rpath;
552 }
553 if let Some(panic) = &toml.panic {
554 profile.panic = match panic.as_str() {
555 "unwind" => PanicStrategy::Unwind,
556 "abort" => PanicStrategy::Abort,
557 _ => panic!("Unexpected panic setting `{}`", panic),
559 };
560 }
561 if let Some(overflow_checks) = toml.overflow_checks {
562 profile.overflow_checks = overflow_checks;
563 }
564 if let Some(incremental) = toml.incremental {
565 profile.incremental = incremental;
566 }
567}
568
569#[derive(Clone, Copy, Eq, PartialOrd, Ord, PartialEq, Debug)]
575pub enum ProfileRoot {
576 Release,
577 Debug,
578}
579
580#[derive(Clone, Copy, Eq, PartialOrd, Ord, serde::Serialize)]
583pub struct Profile {
584 pub name: InternedString,
585 pub opt_level: InternedString,
586 #[serde(skip)] pub root: ProfileRoot,
588 pub lto: Lto,
589 pub codegen_units: Option<u32>,
591 pub debuginfo: Option<u32>,
592 pub debug_assertions: bool,
593 pub overflow_checks: bool,
594 pub rpath: bool,
595 pub incremental: bool,
596 pub panic: PanicStrategy,
597}
598
599impl Default for Profile {
600 fn default() -> Profile {
601 Profile {
602 name: InternedString::new(""),
603 opt_level: InternedString::new("0"),
604 root: ProfileRoot::Debug,
605 lto: Lto::Bool(false),
606 codegen_units: None,
607 debuginfo: None,
608 debug_assertions: false,
609 overflow_checks: false,
610 rpath: false,
611 incremental: false,
612 panic: PanicStrategy::Unwind,
613 }
614 }
615}
616
617compact_debug! {
618 impl fmt::Debug for Profile {
619 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
620 let (default, default_name) = match self.name.as_str() {
621 "dev" => (Profile::default_dev(), "default_dev()"),
622 "release" => (Profile::default_release(), "default_release()"),
623 _ => (Profile::default(), "default()"),
624 };
625 [debug_the_fields(
626 name
627 opt_level
628 lto
629 root
630 codegen_units
631 debuginfo
632 debug_assertions
633 overflow_checks
634 rpath
635 incremental
636 panic
637 )]
638 }
639 }
640}
641
642impl fmt::Display for Profile {
643 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644 write!(f, "Profile({})", self.name)
645 }
646}
647
648impl hash::Hash for Profile {
649 fn hash<H>(&self, state: &mut H)
650 where
651 H: hash::Hasher,
652 {
653 self.comparable().hash(state);
654 }
655}
656
657impl cmp::PartialEq for Profile {
658 fn eq(&self, other: &Self) -> bool {
659 self.comparable() == other.comparable()
660 }
661}
662
663impl Profile {
664 fn default_dev() -> Profile {
665 Profile {
666 name: InternedString::new("dev"),
667 root: ProfileRoot::Debug,
668 debuginfo: Some(2),
669 debug_assertions: true,
670 overflow_checks: true,
671 incremental: true,
672 ..Profile::default()
673 }
674 }
675
676 fn default_release() -> Profile {
677 Profile {
678 name: InternedString::new("release"),
679 root: ProfileRoot::Release,
680 opt_level: InternedString::new("3"),
681 ..Profile::default()
682 }
683 }
684
685 fn default_test() -> Profile {
688 Profile {
689 name: InternedString::new("test"),
690 ..Profile::default_dev()
691 }
692 }
693
694 fn default_bench() -> Profile {
695 Profile {
696 name: InternedString::new("bench"),
697 ..Profile::default_release()
698 }
699 }
700
701 fn default_doc() -> Profile {
702 Profile {
703 name: InternedString::new("doc"),
704 ..Profile::default_dev()
705 }
706 }
707
708 fn comparable(
712 &self,
713 ) -> (
714 InternedString,
715 Lto,
716 Option<u32>,
717 Option<u32>,
718 bool,
719 bool,
720 bool,
721 bool,
722 PanicStrategy,
723 ) {
724 (
725 self.opt_level,
726 self.lto,
727 self.codegen_units,
728 self.debuginfo,
729 self.debug_assertions,
730 self.overflow_checks,
731 self.rpath,
732 self.incremental,
733 self.panic,
734 )
735 }
736}
737
738#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
740pub enum Lto {
741 Bool(bool),
744 Named(InternedString),
746}
747
748impl serde::ser::Serialize for Lto {
749 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
750 where
751 S: serde::ser::Serializer,
752 {
753 match self {
754 Lto::Bool(b) => b.to_string().serialize(s),
755 Lto::Named(n) => n.serialize(s),
756 }
757 }
758}
759
760#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord, serde::Serialize)]
762#[serde(rename_all = "lowercase")]
763pub enum PanicStrategy {
764 Unwind,
765 Abort,
766}
767
768impl fmt::Display for PanicStrategy {
769 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
770 match *self {
771 PanicStrategy::Unwind => "unwind",
772 PanicStrategy::Abort => "abort",
773 }
774 .fmt(f)
775 }
776}
777
778#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
781pub struct UnitFor {
782 host: bool,
797 host_features: bool,
829 panic_setting: PanicSetting,
833}
834
835#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
836enum PanicSetting {
837 AlwaysUnwind,
840
841 ReadProfile,
844
845 Inherit,
848}
849
850impl UnitFor {
851 pub fn new_normal() -> UnitFor {
854 UnitFor {
855 host: false,
856 host_features: false,
857 panic_setting: PanicSetting::ReadProfile,
858 }
859 }
860
861 pub fn new_host(host_features: bool) -> UnitFor {
868 UnitFor {
869 host: true,
870 host_features,
871 panic_setting: PanicSetting::AlwaysUnwind,
874 }
875 }
876
877 pub fn new_compiler() -> UnitFor {
879 UnitFor {
880 host: false,
881 host_features: false,
885 panic_setting: PanicSetting::AlwaysUnwind,
889 }
890 }
891
892 pub fn new_test(config: &Config) -> UnitFor {
899 UnitFor {
900 host: false,
901 host_features: false,
902 panic_setting: if config.cli_unstable().panic_abort_tests {
907 PanicSetting::Inherit
908 } else {
909 PanicSetting::AlwaysUnwind
910 },
911 }
912 }
913
914 pub fn with_for_host(self, for_host: bool) -> UnitFor {
922 UnitFor {
923 host: self.host || for_host,
924 host_features: self.host_features,
925 panic_setting: if for_host {
926 PanicSetting::AlwaysUnwind
927 } else {
928 self.panic_setting
929 },
930 }
931 }
932
933 pub fn with_host_features(mut self, host_features: bool) -> UnitFor {
939 if host_features {
940 assert!(self.host);
941 }
942 self.host_features = self.host_features || host_features;
943 self
944 }
945
946 pub fn is_for_host(&self) -> bool {
949 self.host
950 }
951
952 pub fn is_for_host_features(&self) -> bool {
953 self.host_features
954 }
955
956 fn panic_setting(&self) -> PanicSetting {
958 self.panic_setting
959 }
960
961 pub fn all_values() -> &'static [UnitFor] {
963 static ALL: &[UnitFor] = &[
964 UnitFor {
965 host: false,
966 host_features: false,
967 panic_setting: PanicSetting::ReadProfile,
968 },
969 UnitFor {
970 host: true,
971 host_features: false,
972 panic_setting: PanicSetting::AlwaysUnwind,
973 },
974 UnitFor {
975 host: false,
976 host_features: false,
977 panic_setting: PanicSetting::AlwaysUnwind,
978 },
979 UnitFor {
980 host: false,
981 host_features: false,
982 panic_setting: PanicSetting::Inherit,
983 },
984 UnitFor {
987 host: true,
988 host_features: true,
989 panic_setting: PanicSetting::ReadProfile,
990 },
991 UnitFor {
992 host: true,
993 host_features: true,
994 panic_setting: PanicSetting::AlwaysUnwind,
995 },
996 ];
997 ALL
998 }
999
1000 pub(crate) fn map_to_features_for(&self) -> FeaturesFor {
1001 if self.is_for_host_features() {
1002 FeaturesFor::HostDep
1003 } else {
1004 FeaturesFor::NormalOrDev
1005 }
1006 }
1007}
1008
1009fn merge_config_profiles(
1013 profiles: Option<&TomlProfiles>,
1014 config: &Config,
1015 requested_profile: InternedString,
1016 features: &Features,
1017) -> CargoResult<BTreeMap<InternedString, TomlProfile>> {
1018 let mut profiles = match profiles {
1019 Some(profiles) => profiles.get_all().clone(),
1020 None => BTreeMap::new(),
1021 };
1022 let mut check_to_add = HashSet::new();
1024 check_to_add.insert(requested_profile);
1025 for (name, profile) in &mut profiles {
1027 if let Some(config_profile) = get_config_profile(name, config, features)? {
1028 profile.merge(&config_profile);
1029 }
1030 if let Some(inherits) = &profile.inherits {
1031 check_to_add.insert(*inherits);
1032 }
1033 }
1034 for name in &["dev", "release", "test", "bench"] {
1037 check_to_add.insert(InternedString::new(name));
1038 }
1039 let mut current = HashSet::new();
1042 while !check_to_add.is_empty() {
1043 std::mem::swap(&mut current, &mut check_to_add);
1044 for name in current.drain() {
1045 if !profiles.contains_key(&name) {
1046 if let Some(config_profile) = get_config_profile(&name, config, features)? {
1047 if let Some(inherits) = &config_profile.inherits {
1048 check_to_add.insert(*inherits);
1049 }
1050 profiles.insert(name, config_profile);
1051 }
1052 }
1053 }
1054 }
1055 Ok(profiles)
1056}
1057
1058fn get_config_profile(
1060 name: &str,
1061 config: &Config,
1062 features: &Features,
1063) -> CargoResult<Option<TomlProfile>> {
1064 let profile: Option<config::Value<TomlProfile>> = config.get(&format!("profile.{}", name))?;
1065 let profile = match profile {
1066 Some(profile) => profile,
1067 None => return Ok(None),
1068 };
1069 let mut warnings = Vec::new();
1070 profile
1071 .val
1072 .validate(name, features, &mut warnings)
1073 .chain_err(|| {
1074 anyhow::format_err!(
1075 "config profile `{}` is not valid (defined in `{}`)",
1076 name,
1077 profile.definition
1078 )
1079 })?;
1080 for warning in warnings {
1081 config.shell().warn(warning)?;
1082 }
1083 Ok(Some(profile.val))
1084}
1085
1086fn validate_packages_unique(
1091 resolve: &Resolve,
1092 name: &str,
1093 toml: &Option<TomlProfile>,
1094) -> CargoResult<HashSet<PackageIdSpec>> {
1095 let toml = match toml {
1096 Some(ref toml) => toml,
1097 None => return Ok(HashSet::new()),
1098 };
1099 let overrides = match toml.package.as_ref() {
1100 Some(overrides) => overrides,
1101 None => return Ok(HashSet::new()),
1102 };
1103 let mut found = HashSet::new();
1105 for pkg_id in resolve.iter() {
1106 let matches: Vec<&PackageIdSpec> = overrides
1107 .keys()
1108 .filter_map(|key| match *key {
1109 ProfilePackageSpec::All => None,
1110 ProfilePackageSpec::Spec(ref spec) => {
1111 if spec.matches(pkg_id) {
1112 Some(spec)
1113 } else {
1114 None
1115 }
1116 }
1117 })
1118 .collect();
1119 match matches.len() {
1120 0 => {}
1121 1 => {
1122 found.insert(matches[0].clone());
1123 }
1124 _ => {
1125 let specs = matches
1126 .iter()
1127 .map(|spec| spec.to_string())
1128 .collect::<Vec<_>>()
1129 .join(", ");
1130 bail!(
1131 "multiple package overrides in profile `{}` match package `{}`\n\
1132 found package specs: {}",
1133 name,
1134 pkg_id,
1135 specs
1136 );
1137 }
1138 }
1139 }
1140 Ok(found)
1141}
1142
1143fn validate_packages_unmatched(
1147 shell: &mut Shell,
1148 resolve: &Resolve,
1149 name: &str,
1150 toml: &TomlProfile,
1151 found: &HashSet<PackageIdSpec>,
1152) -> CargoResult<()> {
1153 let overrides = match toml.package.as_ref() {
1154 Some(overrides) => overrides,
1155 None => return Ok(()),
1156 };
1157
1158 let missing_specs = overrides.keys().filter_map(|key| {
1160 if let ProfilePackageSpec::Spec(ref spec) = *key {
1161 if !found.contains(spec) {
1162 return Some(spec);
1163 }
1164 }
1165 None
1166 });
1167 for spec in missing_specs {
1168 let name_matches: Vec<String> = resolve
1170 .iter()
1171 .filter_map(|pkg_id| {
1172 if pkg_id.name() == spec.name() {
1173 Some(pkg_id.to_string())
1174 } else {
1175 None
1176 }
1177 })
1178 .collect();
1179 if name_matches.is_empty() {
1180 let suggestion = closest_msg(&spec.name(), resolve.iter(), |p| p.name().as_str());
1181 shell.warn(format!(
1182 "profile package spec `{}` in profile `{}` did not match any packages{}",
1183 spec, name, suggestion
1184 ))?;
1185 } else {
1186 shell.warn(format!(
1187 "profile package spec `{}` in profile `{}` \
1188 has a version or URL that does not match any of the packages: {}",
1189 spec,
1190 name,
1191 name_matches.join(", ")
1192 ))?;
1193 }
1194 }
1195 Ok(())
1196}