cmCMakePresetsFile.cxx 33 KB

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