1// SPDX-License-Identifier: GPL-3.0-or-later
2#include "common/ConfigurationManager.hpp"
3
4#include "global.hpp"
5#include "logging.hpp"
6#include "units/inherited.hpp"
7#include "units/template.hpp"
8
9#include <algorithm>
10#include <iostream>
11#include <iterator>
12#include <memory>
13#include <set>
14#include <string>
15
16void ConfigurationManagerImpl::LoadConfiguration(std::vector<toml::table> &&tables_)
17{
18 auto tables = tables_;
19 {
20 auto &main = tables.front();
21 defaultTarget = main["default_target"].value_or(default_value: "normal.target");
22 main.erase(key: "default_target");
23 }
24
25 for (const auto &table : tables)
26 {
27 for (const auto &[key, value] : table)
28 {
29 if (!value.is_table())
30 {
31 std::cerr << RED("bad table") << " " << key << std::endl;
32 continue;
33 }
34
35 for (const auto &[subkey, subvalue] : *value.as_table())
36 {
37 const auto id = key.data() + "."s + subkey.data();
38 if (HasUnit(id))
39 {
40 std::cerr << "unit " << id << " " RED("already exists") << std::endl;
41 continue;
42 }
43
44 const auto data = subvalue.as_table();
45
46 if (!data || !data->is_table() || data->empty())
47 {
48 std::cerr << RED("bad unit, expect table") << std::endl;
49 continue;
50 }
51
52 if (id.ends_with(x: TEMPLATE_SUFFIX))
53 {
54 const auto [templates, lock] = templateConfig_.BeginWrite();
55 templates[id] = *data;
56 Debug << "loaded template " << id << std::endl;
57 }
58 else
59 {
60 const auto [units, lock] = unitConfig_.BeginWrite();
61 units[id] = *data;
62 Debug << "created unit " << id << std::endl;
63 }
64 }
65 }
66 }
67}
68
69static void FillArguments(const toml::table *arg_table, ArgumentMap &out_args)
70{
71 // remove corresponding template_params from out_table
72 if (!arg_table)
73 {
74 std::cerr << RED("inherits_args isn't a valid table") << std::endl;
75 return;
76 }
77
78 for (const auto &[arg_key, arg_value] : *arg_table)
79 {
80 if (arg_value.is_string())
81 out_args[arg_key.data()] = arg_value.as_string()->get();
82 else
83 std::cerr << RED("bad template args") << " " << arg_key << std::endl;
84 }
85}
86
87static void MergeTemplateTable(const toml::table &template_table, toml::table &out_table, ArgumentMap &out_args)
88{
89 for (const auto &[in_key, in_value] : template_table)
90 {
91 // don't merge these keys
92 if (in_key == "inherits")
93 continue;
94 else if (in_key == "inherits_args")
95 {
96 FillArguments(arg_table: in_value.as_table(), out_args);
97 continue;
98 }
99 else if (in_key == "template_params")
100 {
101 const auto in_array = in_value.as_array();
102 if (!in_array)
103 {
104 std::cerr << RED("template_params isn't a valid array") << std::endl;
105 continue;
106 }
107
108 std::set<std::string> keys;
109
110 // append incoming template_params to keys
111 for (const auto &e : *in_array)
112 {
113 if (!e.is_string())
114 {
115 std::cerr << RED("template_params isn't a valid string") << std::endl;
116 continue;
117 }
118 keys.insert(x: e.as_string()->get());
119 }
120
121 // add existing template_params to keys
122 const auto existing = out_table["template_params"].as_array();
123 if (existing)
124 {
125 for (const auto &e : *existing)
126 {
127 if (!e.is_string())
128 {
129 std::cerr << RED("template_params isn't a valid string") << std::endl;
130 continue;
131 }
132 keys.insert(x: e.as_string()->get());
133 }
134 }
135 // create new array
136 auto new_array = toml::array();
137 for (const auto &key : keys)
138 new_array.push_back(val: key);
139 out_table.emplace(key: "template_params", args: std::move(new_array));
140 }
141 else
142 {
143 // directly replace the value
144 out_table.insert_or_assign(key: in_key.str(), val: in_value);
145 }
146 }
147}
148
149bool ConfigurationManagerImpl::FillInheritanceRecursive(const std::string &inherits, toml::table &out_table, ArgumentMap &out_args) const
150{
151 const auto templateConfig = GetTemplateConfig(id: inherits);
152 if (!templateConfig)
153 {
154 std::cerr << RED("template not found") << " " << inherits << std::endl;
155 return false;
156 }
157
158 if (!templateConfig->contains(key: "inherits"))
159 {
160 // this template no longer inherits others, safe to fill current configuration to output
161 out_table = *templateConfig;
162 MergeTemplateTable(template_table: *templateConfig, out_table, out_args);
163 out_table.erase(key: "inherits");
164 out_table.erase(key: "inherits_args");
165 return true;
166 }
167
168 if (!FillInheritanceRecursive(inherits: **templateConfig->at(key: "inherits").as_string(), out_table, out_args))
169 return false;
170
171 // now merge templateConfig with out_table
172 MergeTemplateTable(template_table: *templateConfig, out_table, out_args);
173 return true;
174}
175
176std::shared_ptr<Template> ConfigurationManagerImpl::DoCreateTemplate(const std::string &id) const
177{
178 if (HasTemplate(id))
179 {
180 std::cerr << RED("template already exists") << " " << id << std::endl;
181 return nullptr;
182 }
183
184 const auto templateConfig = GetTemplateConfig(id);
185 if (!templateConfig)
186 {
187 std::cerr << RED("template not found") << " " << id << std::endl;
188 return nullptr;
189 }
190
191 toml::table table;
192 ArgumentMap args;
193
194 if (const auto it = templateConfig->find(key: "inherits"); it != templateConfig->end())
195 {
196 if (!FillInheritanceRecursive(inherits: id, out_table&: table, out_args&: args))
197 return nullptr;
198 }
199 else
200 {
201 // this template doesn't inherit anything, just copy the table
202 table = *templateConfig;
203 }
204
205 if (const auto inherits_args = templateConfig->find(key: "inherits_args"); inherits_args != templateConfig->end())
206 {
207 if (inherits_args->second.is_table())
208 FillArguments(arg_table: inherits_args->second.as_table(), out_args&: args);
209 else
210 std::cerr << RED("inherits_args isn't a valid table") << std::endl;
211 }
212 return std::make_shared<Template>(args: id, args&: table, args&: args);
213}
214
215void ConfigurationManagerImpl::FinaliseConfiguration()
216{
217 // create all templates
218 {
219 const auto [templateConfig, lock] = templateConfig_.BeginRead();
220 const auto [templateOverrides, lock2] = templateOverrides_.BeginWrite();
221
222 std::set<std::string> leafTemplates;
223
224 for (const auto &[id, table] : templateConfig)
225 {
226 const auto it = table.find("inherits");
227 if (it == table.end())
228 continue;
229
230 const auto &value = it->second;
231 if (!value.is_string())
232 {
233 std::cout << RED("bad table: ") << id << std::endl;
234 continue;
235 }
236
237 ArgumentMap inheritArgs;
238 if (const auto it = table.find("inherits_args"); it != table.end())
239 {
240 if (it->second.is_table())
241 FillArguments(it->second.as_table(), inheritArgs);
242 else
243 std::cerr << RED("inherits_args isn't a valid table") << std::endl;
244 }
245
246 const auto key = std::make_pair(**value.as_string(), inheritArgs);
247 templateOverrides.insert_or_assign(key, id);
248 leafTemplates.emplace(id);
249 }
250
251 for (const auto &[leaf, _] : templateConfig)
252 {
253 if (HasTemplate(leaf))
254 __builtin_unreachable();
255
256 const auto template_ = DoCreateTemplate(leaf);
257 if (!template_)
258 continue;
259
260 const auto [templates, lock] = templates_.BeginWrite();
261 templates[leaf] = template_;
262 }
263 }
264
265 // create all units
266 {
267 const auto [unitConfig, lock] = unitConfig_.BeginRead();
268 for (const auto &[id, data] : unitConfig)
269 {
270 if (HasUnit(id))
271 continue;
272
273 const auto unit = Unit::Create(id, data);
274 if (!unit)
275 {
276 std::cerr << RED("failed to create unit") << " " << id << std::endl;
277 continue;
278 }
279
280 const auto [units, lock] = units_.BeginWrite();
281 units[id] = unit;
282 }
283 }
284
285 // organise dependencies
286 {
287 const auto [units, lock] = units_.BeginRead();
288 for (const auto &[id, unit] : units)
289 {
290 for (const auto &partid : unit->GetPartOf())
291 {
292 if (!units.contains(partid))
293 {
294 std::cerr << "unit " << unit->id << " is part of non-existent unit " << partid << std::endl;
295 continue;
296 }
297 const auto target = units.at(partid);
298 if (target->GetType() != UnitType::Target)
299 {
300 std::cerr << "unit " << unit->id << " is part of non-target unit " << partid << std::endl;
301 continue;
302 }
303
304 // target->AddDependency(unit->id);
305 const auto targetUnit = std::dynamic_pointer_cast<Target>(target);
306 if (!targetUnit)
307 {
308 std::cerr << "unit " << partid << " is not a target" << std::endl;
309 continue;
310 }
311 targetUnit->AddMember(unit->id);
312 Debug << "unit " << unit->id << " is now part of target " << partid << std::endl;
313 }
314 }
315 }
316}
317
318std::vector<std::pair<std::string, ArgumentMap>> ConfigurationManagerImpl::LookupTemplate(const std::string &id, const ArgumentMap &args)
319{
320 std::vector<std::pair<std::string, ArgumentMap>> intermediateTemplates{ { id, args } };
321
322 {
323 const auto [templateOverrides, lock] = templateOverrides_.BeginRead();
324 std::string currentId = id;
325 ArgumentMap currentArgs = args;
326 while (true)
327 {
328 bool found = false;
329 for (const auto &[pair, overridden] : templateOverrides)
330 {
331 if (pair.first != currentId)
332 continue;
333
334 // if all pair.second keys and values are in args, use that template
335 auto argsCopy = currentArgs;
336
337 const auto argsAreValid = [&](const auto &pair)
338 {
339 const auto &[key, value] = pair;
340 if (!argsCopy.contains(key) || argsCopy.at(key) != value)
341 return false;
342 argsCopy.erase(key); // already searched
343 return true;
344 };
345
346 found = std::all_of(pair.second.begin(), pair.second.end(), argsAreValid);
347 if (found)
348 {
349 currentId = overridden;
350 currentArgs = argsCopy;
351 intermediateTemplates.emplace_back(currentId, currentArgs);
352 break;
353 }
354 }
355
356 if (!found)
357 break;
358 }
359 }
360
361 int level = 0;
362 for (const auto &[id, args] : intermediateTemplates)
363 {
364 // print the template tree
365 Debug << std::string(level * 2, ' ') << WHITE(+id +) << ", ";
366 if (args.empty())
367 Debug << BLUE("no args");
368 else
369 {
370 Debug << BLUE("args: ");
371 for (const auto &[key, value] : args)
372 Debug << YELLOW(+key +) << " = " << GREEN(+value +) << ", ";
373 }
374 Debug << std::endl;
375 level++;
376 }
377
378 return intermediateTemplates;
379}
380
381std::shared_ptr<IUnit> ConfigurationManagerImpl::InstantiateUnit(const std::string &template_id, const ArgumentMap &parameters)
382{
383 const auto intermediateTemplates = LookupTemplate(id: template_id, args: parameters);
384 if (intermediateTemplates.empty())
385 return nullptr;
386
387 // use the last template in the chain
388 const auto &[found_template_id, found_template_args] = intermediateTemplates.back();
389
390 const auto template_ = GetTemplate(id: found_template_id);
391 if (!template_)
392 return nullptr;
393
394 const auto result = template_->Instantiate(args: found_template_args);
395 if (!result)
396 return nullptr;
397
398 const auto [units, lock] = units_.BeginWrite();
399 const auto [unit_id, unit] = *result;
400 if (units.contains(unit_id))
401 return nullptr;
402
403 units[unit_id] = unit;
404
405 if (intermediateTemplates.size() > 1)
406 {
407 std::shared_ptr<IUnit> childUnit = unit;
408 for (auto it = intermediateTemplates.rbegin() + 1; it != intermediateTemplates.rend(); ++it)
409 {
410 const auto newId = Template::GetID(id: it->first, args: it->second);
411 auto inheritedUnit = std::make_shared<InheritedUnit>(args: newId, args&: childUnit);
412 if (units.contains(newId))
413 {
414 std::cerr << RED("inherited unit already exists") << " " << newId << std::endl;
415 continue;
416 }
417 units[newId] = inheritedUnit;
418 childUnit = inheritedUnit;
419 }
420 }
421
422 return unit;
423}
424
425void ConfigurationManagerImpl::AddUnit(std::shared_ptr<IUnit> unit)
426{
427 if (!unit)
428 return;
429
430 const auto [units, lock] = units_.BeginWrite();
431 if (units.contains(unit->id))
432 {
433 std::cerr << RED("unit already exists") << " " << unit->id << std::endl;
434 return;
435 }
436 units[unit->id] = unit;
437}
438