cmCMakePresetsFile.cxx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  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 <iterator>
  8. #include <utility>
  9. #include <cmext/string_view>
  10. #include <cm3p/json/reader.h>
  11. #include <cm3p/json/value.h>
  12. #include "cmsys/FStream.hxx"
  13. #include "cmJSONHelpers.h"
  14. #include "cmStringAlgorithms.h"
  15. #include "cmSystemTools.h"
  16. #include "cmVersion.h"
  17. namespace {
  18. enum class CycleStatus
  19. {
  20. Unvisited,
  21. InProgress,
  22. Verified,
  23. };
  24. using ReadFileResult = cmCMakePresetsFile::ReadFileResult;
  25. using CacheVariable = cmCMakePresetsFile::CacheVariable;
  26. using UnexpandedPreset = cmCMakePresetsFile::UnexpandedPreset;
  27. using ExpandedPreset = cmCMakePresetsFile::ExpandedPreset;
  28. using ArchToolsetStrategy = cmCMakePresetsFile::ArchToolsetStrategy;
  29. constexpr int MIN_VERSION = 1;
  30. constexpr int MAX_VERSION = 1;
  31. struct CMakeVersion
  32. {
  33. unsigned int Major = 0;
  34. unsigned int Minor = 0;
  35. unsigned int Patch = 0;
  36. };
  37. struct RootPresets
  38. {
  39. CMakeVersion CMakeMinimumRequired;
  40. std::vector<cmCMakePresetsFile::UnexpandedPreset> Presets;
  41. };
  42. cmJSONHelper<std::nullptr_t, ReadFileResult> VendorHelper(ReadFileResult error)
  43. {
  44. return [error](std::nullptr_t& /*out*/,
  45. const Json::Value* value) -> ReadFileResult {
  46. if (!value) {
  47. return ReadFileResult::READ_OK;
  48. }
  49. if (!value->isObject()) {
  50. return error;
  51. }
  52. return ReadFileResult::READ_OK;
  53. };
  54. }
  55. auto const VersionIntHelper = cmJSONIntHelper<ReadFileResult>(
  56. ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
  57. auto const VersionHelper = cmJSONRequiredHelper<int, ReadFileResult>(
  58. ReadFileResult::NO_VERSION, VersionIntHelper);
  59. auto const RootVersionHelper =
  60. cmJSONObjectHelper<int, ReadFileResult>(ReadFileResult::READ_OK,
  61. ReadFileResult::INVALID_ROOT)
  62. .Bind("version"_s, VersionHelper, false);
  63. auto const VariableStringHelper = cmJSONStringHelper<ReadFileResult>(
  64. ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE);
  65. ReadFileResult VariableValueHelper(std::string& out, const Json::Value* value)
  66. {
  67. if (!value) {
  68. out.clear();
  69. return ReadFileResult::READ_OK;
  70. }
  71. if (value->isBool()) {
  72. out = value->asBool() ? "TRUE" : "FALSE";
  73. return ReadFileResult::READ_OK;
  74. }
  75. return VariableStringHelper(out, value);
  76. }
  77. auto const VariableObjectHelper =
  78. cmJSONObjectHelper<CacheVariable, ReadFileResult>(
  79. ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE, false)
  80. .Bind("type"_s, &CacheVariable::Type, VariableStringHelper, false)
  81. .Bind("value"_s, &CacheVariable::Value, VariableValueHelper);
  82. ReadFileResult VariableHelper(cm::optional<CacheVariable>& out,
  83. const Json::Value* value)
  84. {
  85. if (value->isBool()) {
  86. out = CacheVariable{
  87. /*Type=*/"BOOL",
  88. /*Value=*/value->asBool() ? "TRUE" : "FALSE",
  89. };
  90. return ReadFileResult::READ_OK;
  91. }
  92. if (value->isString()) {
  93. out = CacheVariable{
  94. /*Type=*/"",
  95. /*Value=*/value->asString(),
  96. };
  97. return ReadFileResult::READ_OK;
  98. }
  99. if (value->isObject()) {
  100. out.emplace();
  101. return VariableObjectHelper(*out, value);
  102. }
  103. if (value->isNull()) {
  104. out = cm::nullopt;
  105. return ReadFileResult::READ_OK;
  106. }
  107. return ReadFileResult::INVALID_VARIABLE;
  108. }
  109. auto const VariablesHelper =
  110. cmJSONMapHelper<cm::optional<CacheVariable>, ReadFileResult>(
  111. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, VariableHelper);
  112. auto const PresetStringHelper = cmJSONStringHelper<ReadFileResult>(
  113. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
  114. ReadFileResult EnvironmentHelper(cm::optional<std::string>& out,
  115. const Json::Value* value)
  116. {
  117. if (!value || value->isNull()) {
  118. out = cm::nullopt;
  119. return ReadFileResult::READ_OK;
  120. }
  121. if (value->isString()) {
  122. out = value->asString();
  123. return ReadFileResult::READ_OK;
  124. }
  125. return ReadFileResult::INVALID_PRESET;
  126. }
  127. auto const EnvironmentMapHelper =
  128. cmJSONMapHelper<cm::optional<std::string>, ReadFileResult>(
  129. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
  130. EnvironmentHelper);
  131. auto const PresetVectorStringHelper =
  132. cmJSONVectorHelper<std::string, ReadFileResult>(
  133. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
  134. PresetStringHelper);
  135. ReadFileResult PresetInheritsHelper(std::vector<std::string>& out,
  136. const Json::Value* value)
  137. {
  138. out.clear();
  139. if (!value) {
  140. return ReadFileResult::READ_OK;
  141. }
  142. if (value->isString()) {
  143. out.push_back(value->asString());
  144. return ReadFileResult::READ_OK;
  145. }
  146. return PresetVectorStringHelper(out, value);
  147. }
  148. auto const PresetBoolHelper = cmJSONBoolHelper<ReadFileResult>(
  149. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
  150. auto const PresetOptionalBoolHelper =
  151. cmJSONOptionalHelper<bool, ReadFileResult>(ReadFileResult::READ_OK,
  152. PresetBoolHelper);
  153. auto const PresetWarningsHelper =
  154. cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>(
  155. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
  156. .Bind("dev"_s, &UnexpandedPreset::WarnDev, PresetOptionalBoolHelper, false)
  157. .Bind("deprecated"_s, &UnexpandedPreset::WarnDeprecated,
  158. PresetOptionalBoolHelper, false)
  159. .Bind("uninitialized"_s, &UnexpandedPreset::WarnUninitialized,
  160. PresetOptionalBoolHelper, false)
  161. .Bind("unusedCli"_s, &UnexpandedPreset::WarnUnusedCli,
  162. PresetOptionalBoolHelper, false)
  163. .Bind("systemVars"_s, &UnexpandedPreset::WarnSystemVars,
  164. PresetOptionalBoolHelper, false);
  165. auto const PresetErrorsHelper =
  166. cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>(
  167. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
  168. .Bind("dev"_s, &UnexpandedPreset::ErrorDev, PresetOptionalBoolHelper,
  169. false)
  170. .Bind("deprecated"_s, &UnexpandedPreset::ErrorDeprecated,
  171. PresetOptionalBoolHelper, false);
  172. auto const PresetDebugHelper =
  173. cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>(
  174. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
  175. .Bind("output"_s, &UnexpandedPreset::DebugOutput, PresetOptionalBoolHelper,
  176. false)
  177. .Bind("tryCompile"_s, &UnexpandedPreset::DebugTryCompile,
  178. PresetOptionalBoolHelper, false)
  179. .Bind("find"_s, &UnexpandedPreset::DebugFind, PresetOptionalBoolHelper,
  180. false);
  181. ReadFileResult ArchToolsetStrategyHelper(
  182. cm::optional<ArchToolsetStrategy>& out, const Json::Value* value)
  183. {
  184. if (!value) {
  185. out = cm::nullopt;
  186. return ReadFileResult::READ_OK;
  187. }
  188. if (!value->isString()) {
  189. return ReadFileResult::INVALID_PRESET;
  190. }
  191. if (value->asString() == "set") {
  192. out = ArchToolsetStrategy::Set;
  193. return ReadFileResult::READ_OK;
  194. }
  195. if (value->asString() == "external") {
  196. out = ArchToolsetStrategy::External;
  197. return ReadFileResult::READ_OK;
  198. }
  199. return ReadFileResult::INVALID_PRESET;
  200. }
  201. std::function<ReadFileResult(UnexpandedPreset&, const Json::Value*)>
  202. ArchToolsetHelper(
  203. std::string UnexpandedPreset::*valueField,
  204. cm::optional<ArchToolsetStrategy> UnexpandedPreset::*strategyField)
  205. {
  206. auto const objectHelper =
  207. cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>(
  208. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
  209. .Bind("value", valueField, PresetStringHelper, false)
  210. .Bind("strategy", strategyField, ArchToolsetStrategyHelper, false);
  211. return [valueField, strategyField, objectHelper](
  212. UnexpandedPreset& out, const Json::Value* value) -> ReadFileResult {
  213. if (!value) {
  214. (out.*valueField).clear();
  215. out.*strategyField = cm::nullopt;
  216. return ReadFileResult::READ_OK;
  217. }
  218. if (value->isString()) {
  219. out.*valueField = value->asString();
  220. out.*strategyField = cm::nullopt;
  221. return ReadFileResult::READ_OK;
  222. }
  223. if (value->isObject()) {
  224. return objectHelper(out, value);
  225. }
  226. return ReadFileResult::INVALID_PRESET;
  227. };
  228. }
  229. auto const ArchitectureHelper = ArchToolsetHelper(
  230. &UnexpandedPreset::Architecture, &UnexpandedPreset::ArchitectureStrategy);
  231. auto const ToolsetHelper = ArchToolsetHelper(
  232. &UnexpandedPreset::Toolset, &UnexpandedPreset::ToolsetStrategy);
  233. auto const PresetHelper =
  234. cmJSONObjectHelper<UnexpandedPreset, ReadFileResult>(
  235. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
  236. .Bind("name"_s, &UnexpandedPreset::Name, PresetStringHelper)
  237. .Bind("inherits"_s, &UnexpandedPreset::Inherits, PresetInheritsHelper,
  238. false)
  239. .Bind("hidden"_s, &UnexpandedPreset::Hidden, PresetBoolHelper, false)
  240. .Bind<std::nullptr_t>("vendor"_s, nullptr,
  241. VendorHelper(ReadFileResult::INVALID_PRESET), false)
  242. .Bind("displayName"_s, &UnexpandedPreset::DisplayName, PresetStringHelper,
  243. false)
  244. .Bind("description"_s, &UnexpandedPreset::Description, PresetStringHelper,
  245. false)
  246. .Bind("generator"_s, &UnexpandedPreset::Generator, PresetStringHelper,
  247. false)
  248. .Bind("architecture"_s, ArchitectureHelper, false)
  249. .Bind("toolset"_s, ToolsetHelper, false)
  250. .Bind("binaryDir"_s, &UnexpandedPreset::BinaryDir, PresetStringHelper,
  251. false)
  252. .Bind<std::string>("cmakeExecutable"_s, nullptr, PresetStringHelper, false)
  253. .Bind("cacheVariables"_s, &UnexpandedPreset::CacheVariables,
  254. VariablesHelper, false)
  255. .Bind("environment"_s, &UnexpandedPreset::Environment,
  256. EnvironmentMapHelper, false)
  257. .Bind("warnings"_s, PresetWarningsHelper, false)
  258. .Bind("errors"_s, PresetErrorsHelper, false)
  259. .Bind("debug"_s, PresetDebugHelper, false);
  260. auto const PresetsHelper =
  261. cmJSONVectorHelper<UnexpandedPreset, ReadFileResult>(
  262. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, PresetHelper);
  263. auto const CMakeVersionUIntHelper = cmJSONUIntHelper<ReadFileResult>(
  264. ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
  265. auto const CMakeVersionHelper =
  266. cmJSONObjectHelper<CMakeVersion, ReadFileResult>(
  267. ReadFileResult::READ_OK, ReadFileResult::INVALID_CMAKE_VERSION, false)
  268. .Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false)
  269. .Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false)
  270. .Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
  271. auto const RootPresetsHelper =
  272. cmJSONObjectHelper<RootPresets, ReadFileResult>(
  273. ReadFileResult::READ_OK, ReadFileResult::INVALID_ROOT, false)
  274. .Bind<int>("version"_s, nullptr, VersionHelper)
  275. .Bind("configurePresets"_s, &RootPresets::Presets, PresetsHelper, false)
  276. .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
  277. CMakeVersionHelper, false)
  278. .Bind<std::nullptr_t>("vendor"_s, nullptr,
  279. VendorHelper(ReadFileResult::INVALID_ROOT), false);
  280. void InheritString(std::string& child, const std::string& parent)
  281. {
  282. if (child.empty()) {
  283. child = parent;
  284. }
  285. }
  286. void InheritOptionalBool(cm::optional<bool>& child,
  287. const cm::optional<bool>& parent)
  288. {
  289. if (!child) {
  290. child = parent;
  291. }
  292. }
  293. /**
  294. * Check preset inheritance for cycles (using a DAG check algorithm) while
  295. * also bubbling up fields through the inheritance hierarchy, then verify
  296. * that each preset has the required fields, either directly or through
  297. * inheritance.
  298. */
  299. ReadFileResult VisitPreset(
  300. std::map<std::string, cmCMakePresetsFile::PresetPair>& presets,
  301. UnexpandedPreset& preset, std::map<std::string, CycleStatus> cycleStatus)
  302. {
  303. switch (cycleStatus[preset.Name]) {
  304. case CycleStatus::InProgress:
  305. return ReadFileResult::CYCLIC_PRESET_INHERITANCE;
  306. case CycleStatus::Verified:
  307. return ReadFileResult::READ_OK;
  308. default:
  309. break;
  310. }
  311. cycleStatus[preset.Name] = CycleStatus::InProgress;
  312. if (preset.CacheVariables.count("") != 0) {
  313. return ReadFileResult::INVALID_PRESET;
  314. }
  315. if (preset.Environment.count("") != 0) {
  316. return ReadFileResult::INVALID_PRESET;
  317. }
  318. for (auto const& i : preset.Inherits) {
  319. auto parent = presets.find(i);
  320. if (parent == presets.end()) {
  321. return ReadFileResult::INVALID_PRESET;
  322. }
  323. if (!preset.User && parent->second.Unexpanded.User) {
  324. return ReadFileResult::USER_PRESET_INHERITANCE;
  325. }
  326. auto result = VisitPreset(presets, parent->second.Unexpanded, cycleStatus);
  327. if (result != ReadFileResult::READ_OK) {
  328. return result;
  329. }
  330. InheritString(preset.Generator, parent->second.Unexpanded.Generator);
  331. InheritString(preset.Architecture, parent->second.Unexpanded.Architecture);
  332. InheritString(preset.Toolset, parent->second.Unexpanded.Toolset);
  333. if (!preset.ArchitectureStrategy) {
  334. preset.ArchitectureStrategy =
  335. parent->second.Unexpanded.ArchitectureStrategy;
  336. }
  337. if (!preset.ToolsetStrategy) {
  338. preset.ToolsetStrategy = parent->second.Unexpanded.ToolsetStrategy;
  339. }
  340. InheritString(preset.BinaryDir, parent->second.Unexpanded.BinaryDir);
  341. InheritOptionalBool(preset.WarnDev, parent->second.Unexpanded.WarnDev);
  342. InheritOptionalBool(preset.ErrorDev, parent->second.Unexpanded.ErrorDev);
  343. InheritOptionalBool(preset.WarnDeprecated,
  344. parent->second.Unexpanded.WarnDeprecated);
  345. InheritOptionalBool(preset.ErrorDeprecated,
  346. parent->second.Unexpanded.ErrorDeprecated);
  347. InheritOptionalBool(preset.WarnUninitialized,
  348. parent->second.Unexpanded.WarnUninitialized);
  349. InheritOptionalBool(preset.WarnUnusedCli,
  350. parent->second.Unexpanded.WarnUnusedCli);
  351. InheritOptionalBool(preset.WarnSystemVars,
  352. parent->second.Unexpanded.WarnSystemVars);
  353. for (auto const& v : parent->second.Unexpanded.CacheVariables) {
  354. preset.CacheVariables.insert(v);
  355. }
  356. for (auto const& v : parent->second.Unexpanded.Environment) {
  357. preset.Environment.insert(v);
  358. }
  359. }
  360. if (!preset.Hidden) {
  361. if (preset.Generator.empty()) {
  362. return ReadFileResult::INVALID_PRESET;
  363. }
  364. if (preset.BinaryDir.empty()) {
  365. return ReadFileResult::INVALID_PRESET;
  366. }
  367. if (preset.WarnDev == false && preset.ErrorDev == true) {
  368. return ReadFileResult::INVALID_PRESET;
  369. }
  370. if (preset.WarnDeprecated == false && preset.ErrorDeprecated == true) {
  371. return ReadFileResult::INVALID_PRESET;
  372. }
  373. }
  374. cycleStatus[preset.Name] = CycleStatus::Verified;
  375. return ReadFileResult::READ_OK;
  376. }
  377. ReadFileResult ComputePresetInheritance(
  378. std::map<std::string, cmCMakePresetsFile::PresetPair>& presets)
  379. {
  380. std::map<std::string, CycleStatus> cycleStatus;
  381. for (auto const& it : presets) {
  382. cycleStatus[it.first] = CycleStatus::Unvisited;
  383. }
  384. for (auto& it : presets) {
  385. auto result = VisitPreset(presets, it.second.Unexpanded, cycleStatus);
  386. if (result != ReadFileResult::READ_OK) {
  387. return result;
  388. }
  389. }
  390. return ReadFileResult::READ_OK;
  391. }
  392. constexpr const char* ValidPrefixes[] = {
  393. "",
  394. "env",
  395. "penv",
  396. "vendor",
  397. };
  398. bool PrefixesValidMacroNamespace(const std::string& str)
  399. {
  400. return std::any_of(
  401. std::begin(ValidPrefixes), std::end(ValidPrefixes),
  402. [&str](const char* prefix) -> bool { return cmHasPrefix(prefix, str); });
  403. }
  404. bool IsValidMacroNamespace(const std::string& str)
  405. {
  406. return std::any_of(
  407. std::begin(ValidPrefixes), std::end(ValidPrefixes),
  408. [&str](const char* prefix) -> bool { return str == prefix; });
  409. }
  410. enum class ExpandMacroResult
  411. {
  412. Ok,
  413. Ignore,
  414. Error,
  415. };
  416. ExpandMacroResult VisitEnv(const cmCMakePresetsFile& file,
  417. cmCMakePresetsFile::ExpandedPreset& preset,
  418. std::map<std::string, CycleStatus>& envCycles,
  419. std::string& value, CycleStatus& status);
  420. ExpandMacroResult ExpandMacros(const cmCMakePresetsFile& file,
  421. cmCMakePresetsFile::ExpandedPreset& preset,
  422. std::map<std::string, CycleStatus>& envCycles,
  423. std::string& out);
  424. ExpandMacroResult ExpandMacro(const cmCMakePresetsFile& file,
  425. cmCMakePresetsFile::ExpandedPreset& preset,
  426. std::map<std::string, CycleStatus>& envCycles,
  427. std::string& out,
  428. const std::string& macroNamespace,
  429. const std::string& macroName);
  430. bool ExpandMacros(const cmCMakePresetsFile& file,
  431. const UnexpandedPreset& preset,
  432. cm::optional<ExpandedPreset>& out)
  433. {
  434. out = preset;
  435. std::map<std::string, CycleStatus> envCycles;
  436. for (auto const& v : out->Environment) {
  437. envCycles[v.first] = CycleStatus::Unvisited;
  438. }
  439. for (auto& v : out->Environment) {
  440. if (v.second) {
  441. switch (VisitEnv(file, *out, envCycles, *v.second, envCycles[v.first])) {
  442. case ExpandMacroResult::Error:
  443. return false;
  444. case ExpandMacroResult::Ignore:
  445. out.reset();
  446. return true;
  447. case ExpandMacroResult::Ok:
  448. break;
  449. }
  450. }
  451. }
  452. std::string binaryDir = preset.BinaryDir;
  453. switch (ExpandMacros(file, *out, envCycles, binaryDir)) {
  454. case ExpandMacroResult::Error:
  455. return false;
  456. case ExpandMacroResult::Ignore:
  457. out.reset();
  458. return true;
  459. case ExpandMacroResult::Ok:
  460. break;
  461. }
  462. if (!cmSystemTools::FileIsFullPath(binaryDir)) {
  463. binaryDir = cmStrCat(file.SourceDir, '/', binaryDir);
  464. }
  465. out->BinaryDir = cmSystemTools::CollapseFullPath(binaryDir);
  466. cmSystemTools::ConvertToUnixSlashes(out->BinaryDir);
  467. for (auto& variable : out->CacheVariables) {
  468. if (variable.second) {
  469. switch (ExpandMacros(file, *out, envCycles, variable.second->Value)) {
  470. case ExpandMacroResult::Error:
  471. return false;
  472. case ExpandMacroResult::Ignore:
  473. out.reset();
  474. return true;
  475. case ExpandMacroResult::Ok:
  476. break;
  477. }
  478. }
  479. }
  480. return true;
  481. }
  482. ExpandMacroResult VisitEnv(const cmCMakePresetsFile& file,
  483. cmCMakePresetsFile::ExpandedPreset& preset,
  484. std::map<std::string, CycleStatus>& envCycles,
  485. std::string& value, CycleStatus& status)
  486. {
  487. if (status == CycleStatus::Verified) {
  488. return ExpandMacroResult::Ok;
  489. }
  490. if (status == CycleStatus::InProgress) {
  491. return ExpandMacroResult::Error;
  492. }
  493. status = CycleStatus::InProgress;
  494. auto e = ExpandMacros(file, preset, envCycles, value);
  495. if (e != ExpandMacroResult::Ok) {
  496. return e;
  497. }
  498. status = CycleStatus::Verified;
  499. return ExpandMacroResult::Ok;
  500. }
  501. ExpandMacroResult ExpandMacros(const cmCMakePresetsFile& file,
  502. cmCMakePresetsFile::ExpandedPreset& preset,
  503. std::map<std::string, CycleStatus>& envCycles,
  504. std::string& out)
  505. {
  506. std::string result;
  507. std::string macroNamespace;
  508. std::string macroName;
  509. enum class State
  510. {
  511. Default,
  512. MacroNamespace,
  513. MacroName,
  514. } state = State::Default;
  515. for (auto c : out) {
  516. switch (state) {
  517. case State::Default:
  518. if (c == '$') {
  519. state = State::MacroNamespace;
  520. } else {
  521. result += c;
  522. }
  523. break;
  524. case State::MacroNamespace:
  525. if (c == '{') {
  526. if (IsValidMacroNamespace(macroNamespace)) {
  527. state = State::MacroName;
  528. } else {
  529. result += '$';
  530. result += macroNamespace;
  531. result += '{';
  532. macroNamespace.clear();
  533. state = State::Default;
  534. }
  535. } else {
  536. macroNamespace += c;
  537. if (!PrefixesValidMacroNamespace(macroNamespace)) {
  538. result += '$';
  539. result += macroNamespace;
  540. macroNamespace.clear();
  541. state = State::Default;
  542. }
  543. }
  544. break;
  545. case State::MacroName:
  546. if (c == '}') {
  547. auto e = ExpandMacro(file, preset, envCycles, result, macroNamespace,
  548. macroName);
  549. if (e != ExpandMacroResult::Ok) {
  550. return e;
  551. }
  552. macroNamespace.clear();
  553. macroName.clear();
  554. state = State::Default;
  555. } else {
  556. macroName += c;
  557. }
  558. break;
  559. }
  560. }
  561. switch (state) {
  562. case State::Default:
  563. break;
  564. case State::MacroNamespace:
  565. result += '$';
  566. result += macroNamespace;
  567. break;
  568. case State::MacroName:
  569. return ExpandMacroResult::Error;
  570. }
  571. out = std::move(result);
  572. return ExpandMacroResult::Ok;
  573. }
  574. ExpandMacroResult ExpandMacro(const cmCMakePresetsFile& file,
  575. cmCMakePresetsFile::ExpandedPreset& preset,
  576. std::map<std::string, CycleStatus>& envCycles,
  577. std::string& out,
  578. const std::string& macroNamespace,
  579. const std::string& macroName)
  580. {
  581. if (macroNamespace.empty()) {
  582. if (macroName == "sourceDir") {
  583. out += file.SourceDir;
  584. return ExpandMacroResult::Ok;
  585. }
  586. if (macroName == "sourceParentDir") {
  587. out += cmSystemTools::GetParentDirectory(file.SourceDir);
  588. return ExpandMacroResult::Ok;
  589. }
  590. if (macroName == "sourceDirName") {
  591. out += cmSystemTools::GetFilenameName(file.SourceDir);
  592. return ExpandMacroResult::Ok;
  593. }
  594. if (macroName == "presetName") {
  595. out += preset.Name;
  596. return ExpandMacroResult::Ok;
  597. }
  598. if (macroName == "generator") {
  599. out += preset.Generator;
  600. return ExpandMacroResult::Ok;
  601. }
  602. if (macroName == "dollar") {
  603. out += '$';
  604. return ExpandMacroResult::Ok;
  605. }
  606. }
  607. if (macroNamespace == "env" && !macroName.empty()) {
  608. auto v = preset.Environment.find(macroName);
  609. if (v != preset.Environment.end() && v->second) {
  610. auto e =
  611. VisitEnv(file, preset, envCycles, *v->second, envCycles[macroName]);
  612. if (e != ExpandMacroResult::Ok) {
  613. return e;
  614. }
  615. out += *v->second;
  616. return ExpandMacroResult::Ok;
  617. }
  618. }
  619. if (macroNamespace == "env" || macroNamespace == "penv") {
  620. if (macroName.empty()) {
  621. return ExpandMacroResult::Error;
  622. }
  623. const char* value = std::getenv(macroName.c_str());
  624. if (value) {
  625. out += value;
  626. }
  627. return ExpandMacroResult::Ok;
  628. }
  629. if (macroNamespace == "vendor") {
  630. return ExpandMacroResult::Ignore;
  631. }
  632. return ExpandMacroResult::Error;
  633. }
  634. }
  635. std::string cmCMakePresetsFile::GetFilename(const std::string& sourceDir)
  636. {
  637. return cmStrCat(sourceDir, "/CMakePresets.json");
  638. }
  639. std::string cmCMakePresetsFile::GetUserFilename(const std::string& sourceDir)
  640. {
  641. return cmStrCat(sourceDir, "/CMakeUserPresets.json");
  642. }
  643. cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadProjectPresets(
  644. const std::string& sourceDir, bool allowNoFiles)
  645. {
  646. bool haveOneFile = false;
  647. this->SourceDir = sourceDir;
  648. this->Presets.clear();
  649. this->PresetOrder.clear();
  650. std::vector<std::string> presetOrder;
  651. std::map<std::string, PresetPair> presetMap;
  652. std::string filename = GetUserFilename(this->SourceDir);
  653. if (cmSystemTools::FileExists(filename)) {
  654. auto result = this->ReadJSONFile(filename, presetOrder, presetMap, true);
  655. if (result != ReadFileResult::READ_OK) {
  656. return result;
  657. }
  658. haveOneFile = true;
  659. }
  660. filename = GetFilename(this->SourceDir);
  661. if (cmSystemTools::FileExists(filename)) {
  662. auto result = this->ReadJSONFile(filename, presetOrder, presetMap, false);
  663. if (result != ReadFileResult::READ_OK) {
  664. return result;
  665. }
  666. haveOneFile = true;
  667. }
  668. if (!haveOneFile) {
  669. return allowNoFiles ? ReadFileResult::READ_OK
  670. : ReadFileResult::FILE_NOT_FOUND;
  671. }
  672. auto result = ComputePresetInheritance(presetMap);
  673. if (result != ReadFileResult::READ_OK) {
  674. return result;
  675. }
  676. for (auto& it : presetMap) {
  677. if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
  678. return ReadFileResult::INVALID_MACRO_EXPANSION;
  679. }
  680. }
  681. this->PresetOrder = std::move(presetOrder);
  682. this->Presets = std::move(presetMap);
  683. return ReadFileResult::READ_OK;
  684. }
  685. const char* cmCMakePresetsFile::ResultToString(ReadFileResult result)
  686. {
  687. switch (result) {
  688. case ReadFileResult::READ_OK:
  689. return "OK";
  690. case ReadFileResult::FILE_NOT_FOUND:
  691. return "File not found";
  692. case ReadFileResult::JSON_PARSE_ERROR:
  693. return "JSON parse error";
  694. case ReadFileResult::INVALID_ROOT:
  695. return "Invalid root object";
  696. case ReadFileResult::NO_VERSION:
  697. return "No \"version\" field";
  698. case ReadFileResult::INVALID_VERSION:
  699. return "Invalid \"version\" field";
  700. case ReadFileResult::UNRECOGNIZED_VERSION:
  701. return "Unrecognized \"version\" field";
  702. case ReadFileResult::INVALID_CMAKE_VERSION:
  703. return "Invalid \"cmakeMinimumRequired\" field";
  704. case ReadFileResult::UNRECOGNIZED_CMAKE_VERSION:
  705. return "\"cmakeMinimumRequired\" version too new";
  706. case ReadFileResult::INVALID_PRESETS:
  707. return "Invalid \"configurePresets\" field";
  708. case ReadFileResult::INVALID_PRESET:
  709. return "Invalid preset";
  710. case ReadFileResult::INVALID_VARIABLE:
  711. return "Invalid CMake variable definition";
  712. case ReadFileResult::DUPLICATE_PRESETS:
  713. return "Duplicate presets";
  714. case ReadFileResult::CYCLIC_PRESET_INHERITANCE:
  715. return "Cyclic preset inheritance";
  716. case ReadFileResult::USER_PRESET_INHERITANCE:
  717. return "Project preset inherits from user preset";
  718. case ReadFileResult::INVALID_MACRO_EXPANSION:
  719. return "Invalid macro expansion";
  720. }
  721. return "Unknown error";
  722. }
  723. cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile(
  724. const std::string& filename, std::vector<std::string>& presetOrder,
  725. std::map<std::string, PresetPair>& presetMap, bool user)
  726. {
  727. cmsys::ifstream fin(filename.c_str());
  728. if (!fin) {
  729. return ReadFileResult::FILE_NOT_FOUND;
  730. }
  731. // If there's a BOM, toss it.
  732. cmsys::FStream::ReadBOM(fin);
  733. Json::Value root;
  734. Json::CharReaderBuilder builder;
  735. if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
  736. return ReadFileResult::JSON_PARSE_ERROR;
  737. }
  738. int v = 0;
  739. auto result = RootVersionHelper(v, &root);
  740. if (result != ReadFileResult::READ_OK) {
  741. return result;
  742. }
  743. if (v < MIN_VERSION || v > MAX_VERSION) {
  744. return ReadFileResult::UNRECOGNIZED_VERSION;
  745. }
  746. RootPresets presets;
  747. if ((result = RootPresetsHelper(presets, &root)) !=
  748. ReadFileResult::READ_OK) {
  749. return result;
  750. }
  751. unsigned int currentMajor = cmVersion::GetMajorVersion();
  752. unsigned int currentMinor = cmVersion::GetMinorVersion();
  753. unsigned int currentPatch = cmVersion::GetPatchVersion();
  754. auto const& required = presets.CMakeMinimumRequired;
  755. if (required.Major > currentMajor ||
  756. (required.Major == currentMajor &&
  757. (required.Minor > currentMinor ||
  758. (required.Minor == currentMinor &&
  759. (required.Patch > currentPatch))))) {
  760. return ReadFileResult::UNRECOGNIZED_CMAKE_VERSION;
  761. }
  762. for (auto& preset : presets.Presets) {
  763. preset.User = user;
  764. if (preset.Name.empty()) {
  765. return ReadFileResult::INVALID_PRESET;
  766. }
  767. if (!presetMap.insert({ preset.Name, { preset, cm::nullopt } }).second) {
  768. return ReadFileResult::DUPLICATE_PRESETS;
  769. }
  770. presetOrder.push_back(preset.Name);
  771. }
  772. return ReadFileResult::READ_OK;
  773. }