cmCMakePresetsFile.cxx 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmCMakePresetsFile.h"
  4. #include <algorithm>
  5. #include <cstdlib>
  6. #include <functional>
  7. #include <iostream>
  8. #include <iterator>
  9. #include <utility>
  10. #include <cm/string_view>
  11. #include "cmsys/RegularExpression.hxx"
  12. #include "cmCMakePresetsFileInternal.h"
  13. #include "cmStringAlgorithms.h"
  14. #include "cmSystemTools.h"
  15. #define CHECK_EXPAND(out, field, expanders, version) \
  16. { \
  17. switch (ExpandMacros(field, expanders, version)) { \
  18. case ExpandMacroResult::Error: \
  19. return false; \
  20. case ExpandMacroResult::Ignore: \
  21. out.reset(); \
  22. return true; \
  23. case ExpandMacroResult::Ok: \
  24. break; \
  25. } \
  26. }
  27. namespace {
  28. enum class CycleStatus
  29. {
  30. Unvisited,
  31. InProgress,
  32. Verified,
  33. };
  34. using ReadFileResult = cmCMakePresetsFile::ReadFileResult;
  35. using ConfigurePreset = cmCMakePresetsFile::ConfigurePreset;
  36. using BuildPreset = cmCMakePresetsFile::BuildPreset;
  37. using TestPreset = cmCMakePresetsFile::TestPreset;
  38. using ExpandMacroResult = cmCMakePresetsFileInternal::ExpandMacroResult;
  39. using MacroExpander = cmCMakePresetsFileInternal::MacroExpander;
  40. void InheritString(std::string& child, const std::string& parent)
  41. {
  42. if (child.empty()) {
  43. child = parent;
  44. }
  45. }
  46. template <typename T>
  47. void InheritOptionalValue(cm::optional<T>& child,
  48. const cm::optional<T>& parent)
  49. {
  50. if (!child) {
  51. child = parent;
  52. }
  53. }
  54. template <typename T>
  55. void InheritVector(std::vector<T>& child, const std::vector<T>& parent)
  56. {
  57. if (child.empty()) {
  58. child = parent;
  59. }
  60. }
  61. /**
  62. * Check preset inheritance for cycles (using a DAG check algorithm) while
  63. * also bubbling up fields through the inheritance hierarchy, then verify
  64. * that each preset has the required fields, either directly or through
  65. * inheritance.
  66. */
  67. template <class T>
  68. ReadFileResult VisitPreset(
  69. T& preset, std::map<std::string, cmCMakePresetsFile::PresetPair<T>>& presets,
  70. std::map<std::string, CycleStatus> cycleStatus, int version)
  71. {
  72. switch (cycleStatus[preset.Name]) {
  73. case CycleStatus::InProgress:
  74. return ReadFileResult::CYCLIC_PRESET_INHERITANCE;
  75. case CycleStatus::Verified:
  76. return ReadFileResult::READ_OK;
  77. default:
  78. break;
  79. }
  80. cycleStatus[preset.Name] = CycleStatus::InProgress;
  81. if (preset.Environment.count("") != 0) {
  82. return ReadFileResult::INVALID_PRESET;
  83. }
  84. CHECK_OK(preset.VisitPresetBeforeInherit())
  85. for (auto const& i : preset.Inherits) {
  86. auto parent = presets.find(i);
  87. if (parent == presets.end()) {
  88. return ReadFileResult::INVALID_PRESET;
  89. }
  90. auto& parentPreset = parent->second.Unexpanded;
  91. if (!preset.User && parentPreset.User) {
  92. return ReadFileResult::USER_PRESET_INHERITANCE;
  93. }
  94. auto result = VisitPreset(parentPreset, presets, cycleStatus, version);
  95. if (result != ReadFileResult::READ_OK) {
  96. return result;
  97. }
  98. CHECK_OK(preset.VisitPresetInherit(parentPreset))
  99. for (auto const& v : parentPreset.Environment) {
  100. preset.Environment.insert(v);
  101. }
  102. if (!preset.ConditionEvaluator) {
  103. preset.ConditionEvaluator = parentPreset.ConditionEvaluator;
  104. }
  105. }
  106. if (preset.ConditionEvaluator && preset.ConditionEvaluator->IsNull()) {
  107. preset.ConditionEvaluator.reset();
  108. }
  109. CHECK_OK(preset.VisitPresetAfterInherit(version))
  110. cycleStatus[preset.Name] = CycleStatus::Verified;
  111. return ReadFileResult::READ_OK;
  112. }
  113. template <class T>
  114. ReadFileResult ComputePresetInheritance(
  115. std::map<std::string, cmCMakePresetsFile::PresetPair<T>>& presets,
  116. const cmCMakePresetsFile& file)
  117. {
  118. std::map<std::string, CycleStatus> cycleStatus;
  119. for (auto const& it : presets) {
  120. cycleStatus[it.first] = CycleStatus::Unvisited;
  121. }
  122. for (auto& it : presets) {
  123. auto& preset = it.second.Unexpanded;
  124. auto result =
  125. VisitPreset<T>(preset, presets, cycleStatus, file.GetVersion(preset));
  126. if (result != ReadFileResult::READ_OK) {
  127. return result;
  128. }
  129. }
  130. return ReadFileResult::READ_OK;
  131. }
  132. constexpr const char* ValidPrefixes[] = {
  133. "",
  134. "env",
  135. "penv",
  136. "vendor",
  137. };
  138. bool PrefixesValidMacroNamespace(const std::string& str)
  139. {
  140. return std::any_of(
  141. std::begin(ValidPrefixes), std::end(ValidPrefixes),
  142. [&str](const char* prefix) -> bool { return cmHasPrefix(prefix, str); });
  143. }
  144. bool IsValidMacroNamespace(const std::string& str)
  145. {
  146. return std::any_of(
  147. std::begin(ValidPrefixes), std::end(ValidPrefixes),
  148. [&str](const char* prefix) -> bool { return str == prefix; });
  149. }
  150. ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status,
  151. const std::vector<MacroExpander>& macroExpanders,
  152. int version);
  153. ExpandMacroResult ExpandMacros(
  154. std::string& out, const std::vector<MacroExpander>& macroExpanders,
  155. int version);
  156. ExpandMacroResult ExpandMacro(std::string& out,
  157. const std::string& macroNamespace,
  158. const std::string& macroName,
  159. const std::vector<MacroExpander>& macroExpanders,
  160. int version);
  161. bool ExpandMacros(const cmCMakePresetsFile& file,
  162. const ConfigurePreset& preset,
  163. cm::optional<ConfigurePreset>& out,
  164. const std::vector<MacroExpander>& macroExpanders)
  165. {
  166. std::string binaryDir = preset.BinaryDir;
  167. CHECK_EXPAND(out, binaryDir, macroExpanders, file.GetVersion(preset))
  168. if (!binaryDir.empty()) {
  169. if (!cmSystemTools::FileIsFullPath(binaryDir)) {
  170. binaryDir = cmStrCat(file.SourceDir, '/', binaryDir);
  171. }
  172. out->BinaryDir = cmSystemTools::CollapseFullPath(binaryDir);
  173. cmSystemTools::ConvertToUnixSlashes(out->BinaryDir);
  174. }
  175. if (!preset.InstallDir.empty()) {
  176. std::string installDir = preset.InstallDir;
  177. CHECK_EXPAND(out, installDir, macroExpanders, file.GetVersion(preset))
  178. if (!cmSystemTools::FileIsFullPath(installDir)) {
  179. installDir = cmStrCat(file.SourceDir, '/', installDir);
  180. }
  181. out->InstallDir = cmSystemTools::CollapseFullPath(installDir);
  182. cmSystemTools::ConvertToUnixSlashes(out->InstallDir);
  183. }
  184. if (!preset.ToolchainFile.empty()) {
  185. std::string toolchain = preset.ToolchainFile;
  186. CHECK_EXPAND(out, toolchain, macroExpanders, file.GetVersion(preset))
  187. out->ToolchainFile = toolchain;
  188. }
  189. for (auto& variable : out->CacheVariables) {
  190. if (variable.second) {
  191. CHECK_EXPAND(out, variable.second->Value, macroExpanders,
  192. file.GetVersion(preset))
  193. }
  194. }
  195. return true;
  196. }
  197. bool ExpandMacros(const cmCMakePresetsFile& file, const BuildPreset& preset,
  198. cm::optional<BuildPreset>& out,
  199. const std::vector<MacroExpander>& macroExpanders)
  200. {
  201. for (auto& target : out->Targets) {
  202. CHECK_EXPAND(out, target, macroExpanders, file.GetVersion(preset))
  203. }
  204. for (auto& nativeToolOption : out->NativeToolOptions) {
  205. CHECK_EXPAND(out, nativeToolOption, macroExpanders,
  206. file.GetVersion(preset))
  207. }
  208. return true;
  209. }
  210. bool ExpandMacros(const cmCMakePresetsFile& file, const TestPreset& preset,
  211. cm::optional<TestPreset>& out,
  212. const std::vector<MacroExpander>& macroExpanders)
  213. {
  214. for (auto& overwrite : out->OverwriteConfigurationFile) {
  215. CHECK_EXPAND(out, overwrite, macroExpanders, file.GetVersion(preset));
  216. }
  217. if (out->Output) {
  218. CHECK_EXPAND(out, out->Output->OutputLogFile, macroExpanders,
  219. file.GetVersion(preset))
  220. }
  221. if (out->Filter) {
  222. if (out->Filter->Include) {
  223. CHECK_EXPAND(out, out->Filter->Include->Name, macroExpanders,
  224. file.GetVersion(preset))
  225. CHECK_EXPAND(out, out->Filter->Include->Label, macroExpanders,
  226. file.GetVersion(preset))
  227. if (out->Filter->Include->Index) {
  228. CHECK_EXPAND(out, out->Filter->Include->Index->IndexFile,
  229. macroExpanders, file.GetVersion(preset));
  230. }
  231. }
  232. if (out->Filter->Exclude) {
  233. CHECK_EXPAND(out, out->Filter->Exclude->Name, macroExpanders,
  234. file.GetVersion(preset))
  235. CHECK_EXPAND(out, out->Filter->Exclude->Label, macroExpanders,
  236. file.GetVersion(preset))
  237. if (out->Filter->Exclude->Fixtures) {
  238. CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Any, macroExpanders,
  239. file.GetVersion(preset))
  240. CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Setup,
  241. macroExpanders, file.GetVersion(preset))
  242. CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Cleanup,
  243. macroExpanders, file.GetVersion(preset))
  244. }
  245. }
  246. }
  247. if (out->Execution) {
  248. CHECK_EXPAND(out, out->Execution->ResourceSpecFile, macroExpanders,
  249. file.GetVersion(preset))
  250. }
  251. return true;
  252. }
  253. template <class T>
  254. bool ExpandMacros(const cmCMakePresetsFile& file, const T& preset,
  255. cm::optional<T>& out)
  256. {
  257. out.emplace(preset);
  258. std::map<std::string, CycleStatus> envCycles;
  259. for (auto const& v : out->Environment) {
  260. envCycles[v.first] = CycleStatus::Unvisited;
  261. }
  262. std::vector<MacroExpander> macroExpanders;
  263. MacroExpander defaultMacroExpander =
  264. [&file, &preset](const std::string& macroNamespace,
  265. const std::string& macroName, std::string& macroOut,
  266. int version) -> ExpandMacroResult {
  267. if (macroNamespace.empty()) {
  268. if (macroName == "sourceDir") {
  269. macroOut += file.SourceDir;
  270. return ExpandMacroResult::Ok;
  271. }
  272. if (macroName == "sourceParentDir") {
  273. macroOut += cmSystemTools::GetParentDirectory(file.SourceDir);
  274. return ExpandMacroResult::Ok;
  275. }
  276. if (macroName == "sourceDirName") {
  277. macroOut += cmSystemTools::GetFilenameName(file.SourceDir);
  278. return ExpandMacroResult::Ok;
  279. }
  280. if (macroName == "presetName") {
  281. macroOut += preset.Name;
  282. return ExpandMacroResult::Ok;
  283. }
  284. if (macroName == "generator") {
  285. // Generator only makes sense if preset is not hidden.
  286. if (!preset.Hidden) {
  287. macroOut += file.GetGeneratorForPreset(preset.Name);
  288. }
  289. return ExpandMacroResult::Ok;
  290. }
  291. if (macroName == "dollar") {
  292. macroOut += '$';
  293. return ExpandMacroResult::Ok;
  294. }
  295. if (macroName == "hostSystemName") {
  296. if (version < 3) {
  297. return ExpandMacroResult::Error;
  298. }
  299. macroOut += cmSystemTools::GetSystemName();
  300. return ExpandMacroResult::Ok;
  301. }
  302. }
  303. return ExpandMacroResult::Ignore;
  304. };
  305. MacroExpander environmentMacroExpander =
  306. [&macroExpanders, &out, &envCycles](
  307. const std::string& macroNamespace, const std::string& macroName,
  308. std::string& result, int version) -> ExpandMacroResult {
  309. if (macroNamespace == "env" && !macroName.empty() && out) {
  310. auto v = out->Environment.find(macroName);
  311. if (v != out->Environment.end() && v->second) {
  312. auto e =
  313. VisitEnv(*v->second, envCycles[macroName], macroExpanders, version);
  314. if (e != ExpandMacroResult::Ok) {
  315. return e;
  316. }
  317. result += *v->second;
  318. return ExpandMacroResult::Ok;
  319. }
  320. }
  321. if (macroNamespace == "env" || macroNamespace == "penv") {
  322. if (macroName.empty()) {
  323. return ExpandMacroResult::Error;
  324. }
  325. const char* value = std::getenv(macroName.c_str());
  326. if (value) {
  327. result += value;
  328. }
  329. return ExpandMacroResult::Ok;
  330. }
  331. return ExpandMacroResult::Ignore;
  332. };
  333. macroExpanders.push_back(defaultMacroExpander);
  334. macroExpanders.push_back(environmentMacroExpander);
  335. for (auto& v : out->Environment) {
  336. if (v.second) {
  337. switch (VisitEnv(*v.second, envCycles[v.first], macroExpanders,
  338. file.GetVersion(preset))) {
  339. case ExpandMacroResult::Error:
  340. return false;
  341. case ExpandMacroResult::Ignore:
  342. out.reset();
  343. return true;
  344. case ExpandMacroResult::Ok:
  345. break;
  346. }
  347. }
  348. }
  349. if (preset.ConditionEvaluator) {
  350. cm::optional<bool> result;
  351. if (!preset.ConditionEvaluator->Evaluate(
  352. macroExpanders, file.GetVersion(preset), result)) {
  353. return false;
  354. }
  355. if (!result) {
  356. out.reset();
  357. return true;
  358. }
  359. out->ConditionResult = *result;
  360. }
  361. return ExpandMacros(file, preset, out, macroExpanders);
  362. }
  363. ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status,
  364. const std::vector<MacroExpander>& macroExpanders,
  365. int version)
  366. {
  367. if (status == CycleStatus::Verified) {
  368. return ExpandMacroResult::Ok;
  369. }
  370. if (status == CycleStatus::InProgress) {
  371. return ExpandMacroResult::Error;
  372. }
  373. status = CycleStatus::InProgress;
  374. auto e = ExpandMacros(value, macroExpanders, version);
  375. if (e != ExpandMacroResult::Ok) {
  376. return e;
  377. }
  378. status = CycleStatus::Verified;
  379. return ExpandMacroResult::Ok;
  380. }
  381. ExpandMacroResult ExpandMacros(
  382. std::string& out, const std::vector<MacroExpander>& macroExpanders,
  383. int version)
  384. {
  385. std::string result;
  386. std::string macroNamespace;
  387. std::string macroName;
  388. enum class State
  389. {
  390. Default,
  391. MacroNamespace,
  392. MacroName,
  393. } state = State::Default;
  394. for (auto c : out) {
  395. switch (state) {
  396. case State::Default:
  397. if (c == '$') {
  398. state = State::MacroNamespace;
  399. } else {
  400. result += c;
  401. }
  402. break;
  403. case State::MacroNamespace:
  404. if (c == '{') {
  405. if (IsValidMacroNamespace(macroNamespace)) {
  406. state = State::MacroName;
  407. } else {
  408. result += '$';
  409. result += macroNamespace;
  410. result += '{';
  411. macroNamespace.clear();
  412. state = State::Default;
  413. }
  414. } else {
  415. macroNamespace += c;
  416. if (!PrefixesValidMacroNamespace(macroNamespace)) {
  417. result += '$';
  418. result += macroNamespace;
  419. macroNamespace.clear();
  420. state = State::Default;
  421. }
  422. }
  423. break;
  424. case State::MacroName:
  425. if (c == '}') {
  426. auto e = ExpandMacro(result, macroNamespace, macroName,
  427. macroExpanders, version);
  428. if (e != ExpandMacroResult::Ok) {
  429. return e;
  430. }
  431. macroNamespace.clear();
  432. macroName.clear();
  433. state = State::Default;
  434. } else {
  435. macroName += c;
  436. }
  437. break;
  438. }
  439. }
  440. switch (state) {
  441. case State::Default:
  442. break;
  443. case State::MacroNamespace:
  444. result += '$';
  445. result += macroNamespace;
  446. break;
  447. case State::MacroName:
  448. return ExpandMacroResult::Error;
  449. }
  450. out = std::move(result);
  451. return ExpandMacroResult::Ok;
  452. }
  453. ExpandMacroResult ExpandMacro(std::string& out,
  454. const std::string& macroNamespace,
  455. const std::string& macroName,
  456. const std::vector<MacroExpander>& macroExpanders,
  457. int version)
  458. {
  459. for (auto const& macroExpander : macroExpanders) {
  460. auto result = macroExpander(macroNamespace, macroName, out, version);
  461. if (result != ExpandMacroResult::Ignore) {
  462. return result;
  463. }
  464. }
  465. if (macroNamespace == "vendor") {
  466. return ExpandMacroResult::Ignore;
  467. }
  468. return ExpandMacroResult::Error;
  469. }
  470. }
  471. bool cmCMakePresetsFileInternal::EqualsCondition::Evaluate(
  472. const std::vector<MacroExpander>& expanders, int version,
  473. cm::optional<bool>& out) const
  474. {
  475. std::string lhs = this->Lhs;
  476. CHECK_EXPAND(out, lhs, expanders, version);
  477. std::string rhs = this->Rhs;
  478. CHECK_EXPAND(out, rhs, expanders, version);
  479. out = (lhs == rhs);
  480. return true;
  481. }
  482. bool cmCMakePresetsFileInternal::InListCondition::Evaluate(
  483. const std::vector<MacroExpander>& expanders, int version,
  484. cm::optional<bool>& out) const
  485. {
  486. std::string str = this->String;
  487. CHECK_EXPAND(out, str, expanders, version);
  488. for (auto item : this->List) {
  489. CHECK_EXPAND(out, item, expanders, version);
  490. if (str == item) {
  491. out = true;
  492. return true;
  493. }
  494. }
  495. out = false;
  496. return true;
  497. }
  498. bool cmCMakePresetsFileInternal::MatchesCondition::Evaluate(
  499. const std::vector<MacroExpander>& expanders, int version,
  500. cm::optional<bool>& out) const
  501. {
  502. std::string str = this->String;
  503. CHECK_EXPAND(out, str, expanders, version);
  504. std::string regexStr = this->Regex;
  505. CHECK_EXPAND(out, regexStr, expanders, version);
  506. cmsys::RegularExpression regex;
  507. if (!regex.compile(regexStr)) {
  508. return false;
  509. }
  510. out = regex.find(str);
  511. return true;
  512. }
  513. bool cmCMakePresetsFileInternal::AnyAllOfCondition::Evaluate(
  514. const std::vector<MacroExpander>& expanders, int version,
  515. cm::optional<bool>& out) const
  516. {
  517. for (auto const& condition : this->Conditions) {
  518. cm::optional<bool> result;
  519. if (!condition->Evaluate(expanders, version, result)) {
  520. out.reset();
  521. return false;
  522. }
  523. if (!result) {
  524. out.reset();
  525. return true;
  526. }
  527. if (result == this->StopValue) {
  528. out = result;
  529. return true;
  530. }
  531. }
  532. out = !this->StopValue;
  533. return true;
  534. }
  535. bool cmCMakePresetsFileInternal::NotCondition::Evaluate(
  536. const std::vector<MacroExpander>& expanders, int version,
  537. cm::optional<bool>& out) const
  538. {
  539. out.reset();
  540. if (!this->SubCondition->Evaluate(expanders, version, out)) {
  541. out.reset();
  542. return false;
  543. }
  544. if (out) {
  545. *out = !*out;
  546. }
  547. return true;
  548. }
  549. cmCMakePresetsFile::ReadFileResult
  550. cmCMakePresetsFile::ConfigurePreset::VisitPresetInherit(
  551. const cmCMakePresetsFile::Preset& parentPreset)
  552. {
  553. auto& preset = *this;
  554. const ConfigurePreset& parent =
  555. static_cast<const ConfigurePreset&>(parentPreset);
  556. InheritString(preset.Generator, parent.Generator);
  557. InheritString(preset.Architecture, parent.Architecture);
  558. InheritString(preset.Toolset, parent.Toolset);
  559. if (!preset.ArchitectureStrategy) {
  560. preset.ArchitectureStrategy = parent.ArchitectureStrategy;
  561. }
  562. if (!preset.ToolsetStrategy) {
  563. preset.ToolsetStrategy = parent.ToolsetStrategy;
  564. }
  565. InheritString(preset.BinaryDir, parent.BinaryDir);
  566. InheritString(preset.InstallDir, parent.InstallDir);
  567. InheritString(preset.ToolchainFile, parent.ToolchainFile);
  568. InheritOptionalValue(preset.WarnDev, parent.WarnDev);
  569. InheritOptionalValue(preset.ErrorDev, parent.ErrorDev);
  570. InheritOptionalValue(preset.WarnDeprecated, parent.WarnDeprecated);
  571. InheritOptionalValue(preset.ErrorDeprecated, parent.ErrorDeprecated);
  572. InheritOptionalValue(preset.WarnUninitialized, parent.WarnUninitialized);
  573. InheritOptionalValue(preset.WarnUnusedCli, parent.WarnUnusedCli);
  574. InheritOptionalValue(preset.WarnSystemVars, parent.WarnSystemVars);
  575. for (auto const& v : parent.CacheVariables) {
  576. preset.CacheVariables.insert(v);
  577. }
  578. return ReadFileResult::READ_OK;
  579. }
  580. cmCMakePresetsFile::ReadFileResult
  581. cmCMakePresetsFile::ConfigurePreset::VisitPresetBeforeInherit()
  582. {
  583. auto& preset = *this;
  584. if (preset.Environment.count("") != 0) {
  585. return ReadFileResult::INVALID_PRESET;
  586. }
  587. return ReadFileResult::READ_OK;
  588. }
  589. cmCMakePresetsFile::ReadFileResult
  590. cmCMakePresetsFile::ConfigurePreset::VisitPresetAfterInherit(int version)
  591. {
  592. auto& preset = *this;
  593. if (!preset.Hidden) {
  594. if (version < 3) {
  595. if (preset.Generator.empty()) {
  596. return ReadFileResult::INVALID_PRESET;
  597. }
  598. if (preset.BinaryDir.empty()) {
  599. return ReadFileResult::INVALID_PRESET;
  600. }
  601. }
  602. if (preset.WarnDev == false && preset.ErrorDev == true) {
  603. return ReadFileResult::INVALID_PRESET;
  604. }
  605. if (preset.WarnDeprecated == false && preset.ErrorDeprecated == true) {
  606. return ReadFileResult::INVALID_PRESET;
  607. }
  608. if (preset.CacheVariables.count("") != 0) {
  609. return ReadFileResult::INVALID_PRESET;
  610. }
  611. }
  612. return ReadFileResult::READ_OK;
  613. }
  614. cmCMakePresetsFile::ReadFileResult
  615. cmCMakePresetsFile::BuildPreset::VisitPresetInherit(
  616. const cmCMakePresetsFile::Preset& parentPreset)
  617. {
  618. auto& preset = *this;
  619. const BuildPreset& parent = static_cast<const BuildPreset&>(parentPreset);
  620. InheritString(preset.ConfigurePreset, parent.ConfigurePreset);
  621. InheritOptionalValue(preset.InheritConfigureEnvironment,
  622. parent.InheritConfigureEnvironment);
  623. InheritOptionalValue(preset.Jobs, parent.Jobs);
  624. InheritVector(preset.Targets, parent.Targets);
  625. InheritString(preset.Configuration, parent.Configuration);
  626. InheritOptionalValue(preset.CleanFirst, parent.CleanFirst);
  627. InheritOptionalValue(preset.Verbose, parent.Verbose);
  628. InheritVector(preset.NativeToolOptions, parent.NativeToolOptions);
  629. return ReadFileResult::READ_OK;
  630. }
  631. cmCMakePresetsFile::ReadFileResult
  632. cmCMakePresetsFile::BuildPreset::VisitPresetAfterInherit(int /* version */)
  633. {
  634. auto& preset = *this;
  635. if (!preset.Hidden && preset.ConfigurePreset.empty()) {
  636. return ReadFileResult::INVALID_PRESET;
  637. }
  638. return ReadFileResult::READ_OK;
  639. }
  640. cmCMakePresetsFile::ReadFileResult
  641. cmCMakePresetsFile::TestPreset::VisitPresetInherit(
  642. const cmCMakePresetsFile::Preset& parentPreset)
  643. {
  644. auto& preset = *this;
  645. const TestPreset& parent = static_cast<const TestPreset&>(parentPreset);
  646. InheritString(preset.ConfigurePreset, parent.ConfigurePreset);
  647. InheritOptionalValue(preset.InheritConfigureEnvironment,
  648. parent.InheritConfigureEnvironment);
  649. InheritString(preset.Configuration, parent.Configuration);
  650. InheritVector(preset.OverwriteConfigurationFile,
  651. parent.OverwriteConfigurationFile);
  652. if (parent.Output) {
  653. if (preset.Output) {
  654. auto& output = preset.Output.value();
  655. const auto& parentOutput = parent.Output.value();
  656. InheritOptionalValue(output.ShortProgress, parentOutput.ShortProgress);
  657. InheritOptionalValue(output.Verbosity, parentOutput.Verbosity);
  658. InheritOptionalValue(output.Debug, parentOutput.Debug);
  659. InheritOptionalValue(output.OutputOnFailure,
  660. parentOutput.OutputOnFailure);
  661. InheritOptionalValue(output.Quiet, parentOutput.Quiet);
  662. InheritString(output.OutputLogFile, parentOutput.OutputLogFile);
  663. InheritOptionalValue(output.LabelSummary, parentOutput.LabelSummary);
  664. InheritOptionalValue(output.SubprojectSummary,
  665. parentOutput.SubprojectSummary);
  666. InheritOptionalValue(output.MaxPassedTestOutputSize,
  667. parentOutput.MaxPassedTestOutputSize);
  668. InheritOptionalValue(output.MaxFailedTestOutputSize,
  669. parentOutput.MaxFailedTestOutputSize);
  670. InheritOptionalValue(output.MaxTestNameWidth,
  671. parentOutput.MaxTestNameWidth);
  672. } else {
  673. preset.Output = parent.Output;
  674. }
  675. }
  676. if (parent.Filter) {
  677. if (parent.Filter->Include) {
  678. if (preset.Filter && preset.Filter->Include) {
  679. auto& include = *preset.Filter->Include;
  680. const auto& parentInclude = *parent.Filter->Include;
  681. InheritString(include.Name, parentInclude.Name);
  682. InheritString(include.Label, parentInclude.Label);
  683. InheritOptionalValue(include.Index, parentInclude.Index);
  684. } else {
  685. if (!preset.Filter) {
  686. preset.Filter.emplace();
  687. }
  688. preset.Filter->Include = parent.Filter->Include;
  689. }
  690. }
  691. if (parent.Filter->Exclude) {
  692. if (preset.Filter && preset.Filter->Exclude) {
  693. auto& exclude = *preset.Filter->Exclude;
  694. const auto& parentExclude = *parent.Filter->Exclude;
  695. InheritString(exclude.Name, parentExclude.Name);
  696. InheritString(exclude.Label, parentExclude.Label);
  697. InheritOptionalValue(exclude.Fixtures, parentExclude.Fixtures);
  698. } else {
  699. if (!preset.Filter) {
  700. preset.Filter.emplace();
  701. }
  702. preset.Filter->Exclude = parent.Filter->Exclude;
  703. }
  704. }
  705. }
  706. if (parent.Execution) {
  707. if (preset.Execution) {
  708. auto& execution = *preset.Execution;
  709. const auto& parentExecution = *parent.Execution;
  710. InheritOptionalValue(execution.StopOnFailure,
  711. parentExecution.StopOnFailure);
  712. InheritOptionalValue(execution.EnableFailover,
  713. parentExecution.EnableFailover);
  714. InheritOptionalValue(execution.Jobs, parentExecution.Jobs);
  715. InheritString(execution.ResourceSpecFile,
  716. parentExecution.ResourceSpecFile);
  717. InheritOptionalValue(execution.TestLoad, parentExecution.TestLoad);
  718. InheritOptionalValue(execution.ShowOnly, parentExecution.ShowOnly);
  719. InheritOptionalValue(execution.Repeat, parentExecution.Repeat);
  720. InheritOptionalValue(execution.InteractiveDebugging,
  721. parentExecution.InteractiveDebugging);
  722. InheritOptionalValue(execution.ScheduleRandom,
  723. parentExecution.ScheduleRandom);
  724. InheritOptionalValue(execution.Timeout, parentExecution.Timeout);
  725. InheritOptionalValue(execution.NoTestsAction,
  726. parentExecution.NoTestsAction);
  727. } else {
  728. preset.Execution = parent.Execution;
  729. }
  730. }
  731. return ReadFileResult::READ_OK;
  732. }
  733. cmCMakePresetsFile::ReadFileResult
  734. cmCMakePresetsFile::TestPreset::VisitPresetAfterInherit(int /* version */)
  735. {
  736. auto& preset = *this;
  737. if (!preset.Hidden && preset.ConfigurePreset.empty()) {
  738. return ReadFileResult::INVALID_PRESET;
  739. }
  740. return ReadFileResult::READ_OK;
  741. }
  742. std::string cmCMakePresetsFile::GetFilename(const std::string& sourceDir)
  743. {
  744. return cmStrCat(sourceDir, "/CMakePresets.json");
  745. }
  746. std::string cmCMakePresetsFile::GetUserFilename(const std::string& sourceDir)
  747. {
  748. return cmStrCat(sourceDir, "/CMakeUserPresets.json");
  749. }
  750. cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadProjectPresets(
  751. const std::string& sourceDir, bool allowNoFiles)
  752. {
  753. this->SourceDir = sourceDir;
  754. this->ClearPresets();
  755. auto result = this->ReadProjectPresetsInternal(allowNoFiles);
  756. if (result != ReadFileResult::READ_OK) {
  757. this->ClearPresets();
  758. }
  759. return result;
  760. }
  761. cmCMakePresetsFile::ReadFileResult
  762. cmCMakePresetsFile::ReadProjectPresetsInternal(bool allowNoFiles)
  763. {
  764. bool haveOneFile = false;
  765. std::string filename = GetUserFilename(this->SourceDir);
  766. if (cmSystemTools::FileExists(filename)) {
  767. auto result = this->ReadJSONFile(filename, true);
  768. if (result != ReadFileResult::READ_OK) {
  769. return result;
  770. }
  771. haveOneFile = true;
  772. }
  773. filename = GetFilename(this->SourceDir);
  774. if (cmSystemTools::FileExists(filename)) {
  775. auto result = this->ReadJSONFile(filename, false);
  776. if (result != ReadFileResult::READ_OK) {
  777. return result;
  778. }
  779. haveOneFile = true;
  780. }
  781. if (!haveOneFile) {
  782. return allowNoFiles ? ReadFileResult::READ_OK
  783. : ReadFileResult::FILE_NOT_FOUND;
  784. }
  785. CHECK_OK(ComputePresetInheritance(this->ConfigurePresets, *this))
  786. CHECK_OK(ComputePresetInheritance(this->BuildPresets, *this))
  787. CHECK_OK(ComputePresetInheritance(this->TestPresets, *this))
  788. for (auto& it : this->ConfigurePresets) {
  789. if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
  790. return ReadFileResult::INVALID_MACRO_EXPANSION;
  791. }
  792. }
  793. for (auto& it : this->BuildPresets) {
  794. if (!it.second.Unexpanded.Hidden) {
  795. const auto configurePreset =
  796. this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
  797. if (configurePreset == this->ConfigurePresets.end()) {
  798. return ReadFileResult::INVALID_CONFIGURE_PRESET;
  799. }
  800. if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
  801. it.second.Unexpanded.Environment.insert(
  802. configurePreset->second.Unexpanded.Environment.begin(),
  803. configurePreset->second.Unexpanded.Environment.end());
  804. }
  805. }
  806. if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
  807. return ReadFileResult::INVALID_MACRO_EXPANSION;
  808. }
  809. }
  810. for (auto& it : this->TestPresets) {
  811. if (!it.second.Unexpanded.Hidden) {
  812. const auto configurePreset =
  813. this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
  814. if (configurePreset == this->ConfigurePresets.end()) {
  815. return ReadFileResult::INVALID_CONFIGURE_PRESET;
  816. }
  817. if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
  818. it.second.Unexpanded.Environment.insert(
  819. configurePreset->second.Unexpanded.Environment.begin(),
  820. configurePreset->second.Unexpanded.Environment.end());
  821. }
  822. }
  823. if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
  824. return ReadFileResult::INVALID_MACRO_EXPANSION;
  825. }
  826. }
  827. return ReadFileResult::READ_OK;
  828. }
  829. const char* cmCMakePresetsFile::ResultToString(ReadFileResult result)
  830. {
  831. switch (result) {
  832. case ReadFileResult::READ_OK:
  833. return "OK";
  834. case ReadFileResult::FILE_NOT_FOUND:
  835. return "File not found";
  836. case ReadFileResult::JSON_PARSE_ERROR:
  837. return "JSON parse error";
  838. case ReadFileResult::INVALID_ROOT:
  839. return "Invalid root object";
  840. case ReadFileResult::NO_VERSION:
  841. return "No \"version\" field";
  842. case ReadFileResult::INVALID_VERSION:
  843. return "Invalid \"version\" field";
  844. case ReadFileResult::UNRECOGNIZED_VERSION:
  845. return "Unrecognized \"version\" field";
  846. case ReadFileResult::INVALID_CMAKE_VERSION:
  847. return "Invalid \"cmakeMinimumRequired\" field";
  848. case ReadFileResult::UNRECOGNIZED_CMAKE_VERSION:
  849. return "\"cmakeMinimumRequired\" version too new";
  850. case ReadFileResult::INVALID_PRESETS:
  851. return "Invalid \"configurePresets\" field";
  852. case ReadFileResult::INVALID_PRESET:
  853. return "Invalid preset";
  854. case ReadFileResult::INVALID_VARIABLE:
  855. return "Invalid CMake variable definition";
  856. case ReadFileResult::DUPLICATE_PRESETS:
  857. return "Duplicate presets";
  858. case ReadFileResult::CYCLIC_PRESET_INHERITANCE:
  859. return "Cyclic preset inheritance";
  860. case ReadFileResult::USER_PRESET_INHERITANCE:
  861. return "Project preset inherits from user preset";
  862. case ReadFileResult::INVALID_MACRO_EXPANSION:
  863. return "Invalid macro expansion";
  864. case ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED:
  865. return "File version must be 2 or higher for build and test preset "
  866. "support.";
  867. case ReadFileResult::INVALID_CONFIGURE_PRESET:
  868. return "Invalid \"configurePreset\" field";
  869. case ReadFileResult::INSTALL_PREFIX_UNSUPPORTED:
  870. return "File version must be 3 or higher for installDir preset "
  871. "support.";
  872. case ReadFileResult::INVALID_CONDITION:
  873. return "Invalid preset condition";
  874. case ReadFileResult::CONDITION_UNSUPPORTED:
  875. return "File version must be 3 or higher for condition support";
  876. case ReadFileResult::TOOLCHAIN_FILE_UNSUPPORTED:
  877. return "File version must be 3 or higher for toolchainFile preset "
  878. "support.";
  879. }
  880. return "Unknown error";
  881. }
  882. void cmCMakePresetsFile::ClearPresets()
  883. {
  884. this->ConfigurePresets.clear();
  885. this->BuildPresets.clear();
  886. this->TestPresets.clear();
  887. this->ConfigurePresetOrder.clear();
  888. this->BuildPresetOrder.clear();
  889. this->TestPresetOrder.clear();
  890. }
  891. void cmCMakePresetsFile::PrintPresets(
  892. const std::vector<const cmCMakePresetsFile::Preset*>& presets)
  893. {
  894. if (presets.empty()) {
  895. return;
  896. }
  897. auto longestPresetName =
  898. std::max_element(presets.begin(), presets.end(),
  899. [](const cmCMakePresetsFile::Preset* a,
  900. const cmCMakePresetsFile::Preset* b) {
  901. return a->Name.length() < b->Name.length();
  902. });
  903. auto longestLength = (*longestPresetName)->Name.length();
  904. for (const auto* preset : presets) {
  905. std::cout << " \"" << preset->Name << '"';
  906. const auto& description = preset->DisplayName;
  907. if (!description.empty()) {
  908. for (std::size_t i = 0; i < longestLength - preset->Name.length(); ++i) {
  909. std::cout << ' ';
  910. }
  911. std::cout << " - " << description;
  912. }
  913. std::cout << '\n';
  914. }
  915. }
  916. void cmCMakePresetsFile::PrintConfigurePresetList() const
  917. {
  918. PrintConfigurePresetList([](const ConfigurePreset&) { return true; });
  919. }
  920. void cmCMakePresetsFile::PrintConfigurePresetList(
  921. const std::function<bool(const ConfigurePreset&)>& filter) const
  922. {
  923. std::vector<const cmCMakePresetsFile::Preset*> presets;
  924. for (auto const& p : this->ConfigurePresetOrder) {
  925. auto const& preset = this->ConfigurePresets.at(p);
  926. if (!preset.Unexpanded.Hidden && preset.Expanded &&
  927. preset.Expanded->ConditionResult && filter(preset.Unexpanded)) {
  928. presets.push_back(
  929. static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
  930. }
  931. }
  932. if (!presets.empty()) {
  933. std::cout << "Available configure presets:\n\n";
  934. cmCMakePresetsFile::PrintPresets(presets);
  935. }
  936. }
  937. void cmCMakePresetsFile::PrintBuildPresetList() const
  938. {
  939. std::vector<const cmCMakePresetsFile::Preset*> presets;
  940. for (auto const& p : this->BuildPresetOrder) {
  941. auto const& preset = this->BuildPresets.at(p);
  942. if (!preset.Unexpanded.Hidden && preset.Expanded &&
  943. preset.Expanded->ConditionResult) {
  944. presets.push_back(
  945. static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
  946. }
  947. }
  948. if (!presets.empty()) {
  949. std::cout << "Available build presets:\n\n";
  950. cmCMakePresetsFile::PrintPresets(presets);
  951. }
  952. }
  953. void cmCMakePresetsFile::PrintTestPresetList() const
  954. {
  955. std::vector<const cmCMakePresetsFile::Preset*> presets;
  956. for (auto const& p : this->TestPresetOrder) {
  957. auto const& preset = this->TestPresets.at(p);
  958. if (!preset.Unexpanded.Hidden && preset.Expanded &&
  959. preset.Expanded->ConditionResult) {
  960. presets.push_back(
  961. static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
  962. }
  963. }
  964. if (!presets.empty()) {
  965. std::cout << "Available test presets:\n\n";
  966. cmCMakePresetsFile::PrintPresets(presets);
  967. }
  968. }
  969. void cmCMakePresetsFile::PrintAllPresets() const
  970. {
  971. this->PrintConfigurePresetList();
  972. std::cout << std::endl;
  973. this->PrintBuildPresetList();
  974. std::cout << std::endl;
  975. this->PrintTestPresetList();
  976. }