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