bootstrap/core/config/toml/
mod.rs1use serde::Deserialize;
11use serde_derive::Deserialize;
12pub mod build;
13pub mod change_id;
14pub mod dist;
15pub mod gcc;
16pub mod install;
17pub mod llvm;
18pub mod pgo;
19pub mod rust;
20pub mod target;
21
22use build::Build;
23use change_id::{ChangeId, ChangeIdWrapper};
24use dist::Dist;
25use gcc::Gcc;
26use install::Install;
27use llvm::Llvm;
28use rust::Rust;
29use target::TomlTarget;
30
31use crate::core::config::toml::pgo::Pgo;
32use crate::core::config::{Merge, ReplaceOpt};
33use crate::{Config, HashMap, HashSet, Path, PathBuf, exit, fs, t};
34
35#[derive(Deserialize, Default)]
41#[serde(deny_unknown_fields, rename_all = "kebab-case")]
42pub(crate) struct TomlConfig {
43 #[serde(flatten)]
44 pub(crate) change_id: ChangeIdWrapper,
45 pub(super) build: Option<Build>,
46 pub(super) install: Option<Install>,
47 pub(super) llvm: Option<Llvm>,
48 pub(super) gcc: Option<Gcc>,
49 pub(super) rust: Option<Rust>,
50 pub(super) target: Option<HashMap<String, TomlTarget>>,
51 pub(super) dist: Option<Dist>,
52 pub(super) pgo: Option<Pgo>,
53 pub(super) profile: Option<String>,
54 pub(super) include: Option<Vec<PathBuf>>,
55}
56
57impl Merge for TomlConfig {
58 fn merge(
59 &mut self,
60 parent_config_path: Option<PathBuf>,
61 included_extensions: &mut HashSet<PathBuf>,
62 TomlConfig {
63 build,
64 install,
65 llvm,
66 gcc,
67 rust,
68 dist,
69 target,
70 pgo,
71 profile,
72 change_id,
73 include,
74 }: Self,
75 replace: ReplaceOpt,
76 ) {
77 fn do_merge<T: Merge>(x: &mut Option<T>, y: Option<T>, replace: ReplaceOpt) {
78 if let Some(new) = y {
79 if let Some(original) = x {
80 original.merge(None, &mut Default::default(), new, replace);
81 } else {
82 *x = Some(new);
83 }
84 }
85 }
86
87 self.change_id.inner.merge(None, &mut Default::default(), change_id.inner, replace);
88 self.profile.merge(None, &mut Default::default(), profile, replace);
89
90 do_merge(&mut self.build, build, replace);
91 do_merge(&mut self.install, install, replace);
92 do_merge(&mut self.llvm, llvm, replace);
93 do_merge(&mut self.gcc, gcc, replace);
94 do_merge(&mut self.rust, rust, replace);
95 do_merge(&mut self.dist, dist, replace);
96 do_merge(&mut self.pgo, pgo, replace);
97
98 match (self.target.as_mut(), target) {
99 (_, None) => {}
100 (None, Some(target)) => self.target = Some(target),
101 (Some(original_target), Some(new_target)) => {
102 for (triple, new) in new_target {
103 if let Some(original) = original_target.get_mut(&triple) {
104 original.merge(None, &mut Default::default(), new, replace);
105 } else {
106 original_target.insert(triple, new);
107 }
108 }
109 }
110 }
111
112 let parent_dir = parent_config_path
113 .as_ref()
114 .and_then(|p| p.parent().map(ToOwned::to_owned))
115 .unwrap_or_default();
116
117 for include_path in include.clone().unwrap_or_default().iter().rev() {
120 let include_path = parent_dir.join(include_path);
121 let include_path = include_path.canonicalize().unwrap_or_else(|e| {
122 eprintln!("ERROR: Failed to canonicalize '{}' path: {e}", include_path.display());
123 exit!(2);
124 });
125
126 let included_toml = Config::get_toml_inner(&include_path).unwrap_or_else(|e| {
127 eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display());
128 exit!(2);
129 });
130
131 assert!(
132 included_extensions.insert(include_path.clone()),
133 "Cyclic inclusion detected: '{}' is being included again before its previous inclusion was fully processed.",
134 include_path.display()
135 );
136
137 self.merge(
138 Some(include_path.clone()),
139 included_extensions,
140 included_toml,
141 ReplaceOpt::IgnoreDuplicate,
144 );
145
146 included_extensions.remove(&include_path);
147 }
148 }
149}
150
151pub const BUILDER_CONFIG_FILENAME: &str = "builder-config";
158
159impl Config {
160 pub(crate) fn get_builder_toml(&self, build_name: &str) -> Result<TomlConfig, toml::de::Error> {
161 if self.dry_run() {
162 return Ok(TomlConfig::default());
163 }
164
165 let builder_config_path =
166 self.out.join(self.host_target.triple).join(build_name).join(BUILDER_CONFIG_FILENAME);
167 Self::get_toml(&builder_config_path)
168 }
169
170 pub(crate) fn get_toml(file: &Path) -> Result<TomlConfig, toml::de::Error> {
171 Self::get_toml_inner(file)
172 }
173
174 pub(crate) fn get_toml_inner(file: &Path) -> Result<TomlConfig, toml::de::Error> {
175 let contents =
176 t!(fs::read_to_string(file), format!("config file {} not found", file.display()));
177 toml::from_str(&contents)
180 .and_then(|table: toml::Value| TomlConfig::deserialize(table))
181 .inspect_err(|_| {
182 if let Ok(ChangeIdWrapper { inner: Some(ChangeId::Id(id)) }) =
183 toml::from_str::<toml::Value>(&contents)
184 .and_then(|table: toml::Value| ChangeIdWrapper::deserialize(table))
185 {
186 let changes = crate::find_recent_config_change_ids(id);
187 if !changes.is_empty() {
188 println!(
189 "WARNING: There have been changes to x.py since you last updated:\n{}",
190 crate::human_readable_changes(changes)
191 );
192 }
193 }
194 })
195 }
196}