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