cmCMakePresetsGraphReadJSON.cxx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  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 <functional>
  4. #include <map>
  5. #include <string>
  6. #include <utility>
  7. #include <vector>
  8. #include <cm/memory>
  9. #include <cm/optional>
  10. #include <cmext/string_view>
  11. #include <cm3p/json/reader.h>
  12. #include <cm3p/json/value.h>
  13. #include "cmsys/FStream.hxx"
  14. #include "cmCMakePresetsGraph.h"
  15. #include "cmCMakePresetsGraphInternal.h"
  16. #include "cmJSONHelpers.h"
  17. #include "cmVersion.h"
  18. namespace {
  19. using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
  20. using CacheVariable = cmCMakePresetsGraph::CacheVariable;
  21. using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
  22. using BuildPreset = cmCMakePresetsGraph::BuildPreset;
  23. using TestPreset = cmCMakePresetsGraph::TestPreset;
  24. using ArchToolsetStrategy = cmCMakePresetsGraph::ArchToolsetStrategy;
  25. constexpr int MIN_VERSION = 1;
  26. constexpr int MAX_VERSION = 3;
  27. struct CMakeVersion
  28. {
  29. unsigned int Major = 0;
  30. unsigned int Minor = 0;
  31. unsigned int Patch = 0;
  32. };
  33. struct RootPresets
  34. {
  35. CMakeVersion CMakeMinimumRequired;
  36. std::vector<cmCMakePresetsGraph::ConfigurePreset> ConfigurePresets;
  37. std::vector<cmCMakePresetsGraph::BuildPreset> BuildPresets;
  38. std::vector<cmCMakePresetsGraph::TestPreset> TestPresets;
  39. };
  40. std::unique_ptr<cmCMakePresetsGraphInternal::NotCondition> InvertCondition(
  41. std::unique_ptr<cmCMakePresetsGraph::Condition> condition)
  42. {
  43. auto retval = cm::make_unique<cmCMakePresetsGraphInternal::NotCondition>();
  44. retval->SubCondition = std::move(condition);
  45. return retval;
  46. }
  47. auto const ConditionStringHelper = cmJSONStringHelper<ReadFileResult>(
  48. ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION);
  49. auto const ConditionBoolHelper = cmJSONBoolHelper<ReadFileResult>(
  50. ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION);
  51. auto const ConditionStringListHelper =
  52. cmJSONVectorHelper<std::string, ReadFileResult>(
  53. ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION,
  54. ConditionStringHelper);
  55. auto const ConstConditionHelper =
  56. cmJSONObjectHelper<cmCMakePresetsGraphInternal::ConstCondition,
  57. ReadFileResult>(ReadFileResult::READ_OK,
  58. ReadFileResult::INVALID_CONDITION, false)
  59. .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
  60. .Bind("value"_s, &cmCMakePresetsGraphInternal::ConstCondition::Value,
  61. ConditionBoolHelper, true);
  62. auto const EqualsConditionHelper =
  63. cmJSONObjectHelper<cmCMakePresetsGraphInternal::EqualsCondition,
  64. ReadFileResult>(ReadFileResult::READ_OK,
  65. ReadFileResult::INVALID_CONDITION, false)
  66. .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
  67. .Bind("lhs"_s, &cmCMakePresetsGraphInternal::EqualsCondition::Lhs,
  68. ConditionStringHelper, true)
  69. .Bind("rhs"_s, &cmCMakePresetsGraphInternal::EqualsCondition::Rhs,
  70. ConditionStringHelper, true);
  71. auto const InListConditionHelper =
  72. cmJSONObjectHelper<cmCMakePresetsGraphInternal::InListCondition,
  73. ReadFileResult>(ReadFileResult::READ_OK,
  74. ReadFileResult::INVALID_CONDITION, false)
  75. .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
  76. .Bind("string"_s, &cmCMakePresetsGraphInternal::InListCondition::String,
  77. ConditionStringHelper, true)
  78. .Bind("list"_s, &cmCMakePresetsGraphInternal::InListCondition::List,
  79. ConditionStringListHelper, true);
  80. auto const MatchesConditionHelper =
  81. cmJSONObjectHelper<cmCMakePresetsGraphInternal::MatchesCondition,
  82. ReadFileResult>(ReadFileResult::READ_OK,
  83. ReadFileResult::INVALID_CONDITION, false)
  84. .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
  85. .Bind("string"_s, &cmCMakePresetsGraphInternal::MatchesCondition::String,
  86. ConditionStringHelper, true)
  87. .Bind("regex"_s, &cmCMakePresetsGraphInternal::MatchesCondition::Regex,
  88. ConditionStringHelper, true);
  89. ReadFileResult SubConditionHelper(
  90. std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
  91. const Json::Value* value);
  92. auto const ListConditionVectorHelper =
  93. cmJSONVectorHelper<std::unique_ptr<cmCMakePresetsGraph::Condition>,
  94. ReadFileResult>(ReadFileResult::READ_OK,
  95. ReadFileResult::INVALID_CONDITION,
  96. SubConditionHelper);
  97. auto const AnyAllOfConditionHelper =
  98. cmJSONObjectHelper<cmCMakePresetsGraphInternal::AnyAllOfCondition,
  99. ReadFileResult>(ReadFileResult::READ_OK,
  100. ReadFileResult::INVALID_CONDITION, false)
  101. .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
  102. .Bind("conditions"_s,
  103. &cmCMakePresetsGraphInternal::AnyAllOfCondition::Conditions,
  104. ListConditionVectorHelper);
  105. auto const NotConditionHelper =
  106. cmJSONObjectHelper<cmCMakePresetsGraphInternal::NotCondition,
  107. ReadFileResult>(ReadFileResult::READ_OK,
  108. ReadFileResult::INVALID_CONDITION, false)
  109. .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
  110. .Bind("condition"_s,
  111. &cmCMakePresetsGraphInternal::NotCondition::SubCondition,
  112. SubConditionHelper);
  113. ReadFileResult ConditionHelper(
  114. std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
  115. const Json::Value* value)
  116. {
  117. if (!value) {
  118. out.reset();
  119. return ReadFileResult::READ_OK;
  120. }
  121. if (value->isBool()) {
  122. auto c = cm::make_unique<cmCMakePresetsGraphInternal::ConstCondition>();
  123. c->Value = value->asBool();
  124. out = std::move(c);
  125. return ReadFileResult::READ_OK;
  126. }
  127. if (value->isNull()) {
  128. out = cm::make_unique<cmCMakePresetsGraphInternal::NullCondition>();
  129. return ReadFileResult::READ_OK;
  130. }
  131. if (value->isObject()) {
  132. if (!value->isMember("type")) {
  133. return ReadFileResult::INVALID_CONDITION;
  134. }
  135. if (!(*value)["type"].isString()) {
  136. return ReadFileResult::INVALID_CONDITION;
  137. }
  138. auto type = (*value)["type"].asString();
  139. if (type == "const") {
  140. auto c = cm::make_unique<cmCMakePresetsGraphInternal::ConstCondition>();
  141. CHECK_OK(ConstConditionHelper(*c, value));
  142. out = std::move(c);
  143. return ReadFileResult::READ_OK;
  144. }
  145. if (type == "equals" || type == "notEquals") {
  146. auto c = cm::make_unique<cmCMakePresetsGraphInternal::EqualsCondition>();
  147. CHECK_OK(EqualsConditionHelper(*c, value));
  148. out = std::move(c);
  149. if (type == "notEquals") {
  150. out = InvertCondition(std::move(out));
  151. }
  152. return ReadFileResult::READ_OK;
  153. }
  154. if (type == "inList" || type == "notInList") {
  155. auto c = cm::make_unique<cmCMakePresetsGraphInternal::InListCondition>();
  156. CHECK_OK(InListConditionHelper(*c, value));
  157. out = std::move(c);
  158. if (type == "notInList") {
  159. out = InvertCondition(std::move(out));
  160. }
  161. return ReadFileResult::READ_OK;
  162. }
  163. if (type == "matches" || type == "notMatches") {
  164. auto c =
  165. cm::make_unique<cmCMakePresetsGraphInternal::MatchesCondition>();
  166. CHECK_OK(MatchesConditionHelper(*c, value));
  167. out = std::move(c);
  168. if (type == "notMatches") {
  169. out = InvertCondition(std::move(out));
  170. }
  171. return ReadFileResult::READ_OK;
  172. }
  173. if (type == "anyOf" || type == "allOf") {
  174. auto c =
  175. cm::make_unique<cmCMakePresetsGraphInternal::AnyAllOfCondition>();
  176. c->StopValue = (type == "anyOf");
  177. CHECK_OK(AnyAllOfConditionHelper(*c, value));
  178. out = std::move(c);
  179. return ReadFileResult::READ_OK;
  180. }
  181. if (type == "not") {
  182. auto c = cm::make_unique<cmCMakePresetsGraphInternal::NotCondition>();
  183. CHECK_OK(NotConditionHelper(*c, value));
  184. out = std::move(c);
  185. return ReadFileResult::READ_OK;
  186. }
  187. }
  188. return ReadFileResult::INVALID_CONDITION;
  189. }
  190. ReadFileResult SubConditionHelper(
  191. std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
  192. const Json::Value* value)
  193. {
  194. std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
  195. auto result = ConditionHelper(ptr, value);
  196. if (ptr && ptr->IsNull()) {
  197. return ReadFileResult::INVALID_CONDITION;
  198. }
  199. out = std::move(ptr);
  200. return result;
  201. }
  202. ReadFileResult EnvironmentHelper(cm::optional<std::string>& out,
  203. const Json::Value* value)
  204. {
  205. if (!value || value->isNull()) {
  206. out = cm::nullopt;
  207. return ReadFileResult::READ_OK;
  208. }
  209. if (value->isString()) {
  210. out = value->asString();
  211. return ReadFileResult::READ_OK;
  212. }
  213. return ReadFileResult::INVALID_PRESET;
  214. }
  215. auto const VersionIntHelper = cmJSONIntHelper<ReadFileResult>(
  216. ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
  217. auto const VersionHelper = cmJSONRequiredHelper<int, ReadFileResult>(
  218. ReadFileResult::NO_VERSION, VersionIntHelper);
  219. auto const RootVersionHelper =
  220. cmJSONObjectHelper<int, ReadFileResult>(ReadFileResult::READ_OK,
  221. ReadFileResult::INVALID_ROOT)
  222. .Bind("version"_s, VersionHelper, false);
  223. auto const CMakeVersionUIntHelper = cmJSONUIntHelper<ReadFileResult>(
  224. ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
  225. auto const CMakeVersionHelper =
  226. cmJSONObjectHelper<CMakeVersion, ReadFileResult>(
  227. ReadFileResult::READ_OK, ReadFileResult::INVALID_CMAKE_VERSION, false)
  228. .Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false)
  229. .Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false)
  230. .Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
  231. auto const RootPresetsHelper =
  232. cmJSONObjectHelper<RootPresets, ReadFileResult>(
  233. ReadFileResult::READ_OK, ReadFileResult::INVALID_ROOT, false)
  234. .Bind<int>("version"_s, nullptr, VersionHelper)
  235. .Bind("configurePresets"_s, &RootPresets::ConfigurePresets,
  236. cmCMakePresetsGraphInternal::ConfigurePresetsHelper, false)
  237. .Bind("buildPresets"_s, &RootPresets::BuildPresets,
  238. cmCMakePresetsGraphInternal::BuildPresetsHelper, false)
  239. .Bind("testPresets"_s, &RootPresets::TestPresets,
  240. cmCMakePresetsGraphInternal::TestPresetsHelper, false)
  241. .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
  242. CMakeVersionHelper, false)
  243. .Bind<std::nullptr_t>(
  244. "vendor"_s, nullptr,
  245. cmCMakePresetsGraphInternal::VendorHelper(ReadFileResult::INVALID_ROOT),
  246. false);
  247. }
  248. namespace cmCMakePresetsGraphInternal {
  249. cmCMakePresetsGraph::ReadFileResult PresetStringHelper(
  250. std::string& out, const Json::Value* value)
  251. {
  252. static auto const helper = cmJSONStringHelper<ReadFileResult>(
  253. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
  254. return helper(out, value);
  255. }
  256. cmCMakePresetsGraph::ReadFileResult PresetVectorStringHelper(
  257. std::vector<std::string>& out, const Json::Value* value)
  258. {
  259. static auto const helper = cmJSONVectorHelper<std::string, ReadFileResult>(
  260. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
  261. cmCMakePresetsGraphInternal::PresetStringHelper);
  262. return helper(out, value);
  263. }
  264. cmCMakePresetsGraph::ReadFileResult PresetBoolHelper(bool& out,
  265. const Json::Value* value)
  266. {
  267. static auto const helper = cmJSONBoolHelper<ReadFileResult>(
  268. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
  269. return helper(out, value);
  270. }
  271. cmCMakePresetsGraph::ReadFileResult PresetOptionalBoolHelper(
  272. cm::optional<bool>& out, const Json::Value* value)
  273. {
  274. static auto const helper = cmJSONOptionalHelper<bool, ReadFileResult>(
  275. ReadFileResult::READ_OK, PresetBoolHelper);
  276. return helper(out, value);
  277. }
  278. cmCMakePresetsGraph::ReadFileResult PresetIntHelper(int& out,
  279. const Json::Value* value)
  280. {
  281. static auto const helper = cmJSONIntHelper<ReadFileResult>(
  282. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
  283. return helper(out, value);
  284. }
  285. cmCMakePresetsGraph::ReadFileResult PresetOptionalIntHelper(
  286. cm::optional<int>& out, const Json::Value* value)
  287. {
  288. static auto const helper = cmJSONOptionalHelper<int, ReadFileResult>(
  289. ReadFileResult::READ_OK, PresetIntHelper);
  290. return helper(out, value);
  291. }
  292. cmCMakePresetsGraph::ReadFileResult PresetVectorIntHelper(
  293. std::vector<int>& out, const Json::Value* value)
  294. {
  295. static auto const helper = cmJSONVectorHelper<int, ReadFileResult>(
  296. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, PresetIntHelper);
  297. return helper(out, value);
  298. }
  299. cmJSONHelper<std::nullptr_t, ReadFileResult> VendorHelper(ReadFileResult error)
  300. {
  301. return [error](std::nullptr_t& /*out*/,
  302. const Json::Value* value) -> ReadFileResult {
  303. if (!value) {
  304. return ReadFileResult::READ_OK;
  305. }
  306. if (!value->isObject()) {
  307. return error;
  308. }
  309. return ReadFileResult::READ_OK;
  310. };
  311. }
  312. ReadFileResult PresetConditionHelper(
  313. std::shared_ptr<cmCMakePresetsGraph::Condition>& out,
  314. const Json::Value* value)
  315. {
  316. std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
  317. auto result = ConditionHelper(ptr, value);
  318. out = std::move(ptr);
  319. return result;
  320. }
  321. ReadFileResult PresetVectorOneOrMoreStringHelper(std::vector<std::string>& out,
  322. const Json::Value* value)
  323. {
  324. out.clear();
  325. if (!value) {
  326. return ReadFileResult::READ_OK;
  327. }
  328. if (value->isString()) {
  329. out.push_back(value->asString());
  330. return ReadFileResult::READ_OK;
  331. }
  332. return PresetVectorStringHelper(out, value);
  333. }
  334. cmCMakePresetsGraph::ReadFileResult EnvironmentMapHelper(
  335. std::map<std::string, cm::optional<std::string>>& out,
  336. const Json::Value* value)
  337. {
  338. static auto const helper =
  339. cmJSONMapHelper<cm::optional<std::string>, ReadFileResult>(
  340. ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
  341. EnvironmentHelper);
  342. return helper(out, value);
  343. }
  344. }
  345. cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
  346. const std::string& filename, bool user)
  347. {
  348. cmsys::ifstream fin(filename.c_str());
  349. if (!fin) {
  350. return ReadFileResult::FILE_NOT_FOUND;
  351. }
  352. // If there's a BOM, toss it.
  353. cmsys::FStream::ReadBOM(fin);
  354. Json::Value root;
  355. Json::CharReaderBuilder builder;
  356. Json::CharReaderBuilder::strictMode(&builder.settings_);
  357. if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
  358. return ReadFileResult::JSON_PARSE_ERROR;
  359. }
  360. int v = 0;
  361. auto result = RootVersionHelper(v, &root);
  362. if (result != ReadFileResult::READ_OK) {
  363. return result;
  364. }
  365. if (v < MIN_VERSION || v > MAX_VERSION) {
  366. return ReadFileResult::UNRECOGNIZED_VERSION;
  367. }
  368. if (user) {
  369. this->UserVersion = v;
  370. } else {
  371. this->Version = v;
  372. }
  373. // Support for build and test presets added in version 2.
  374. if (v < 2 &&
  375. (root.isMember("buildPresets") || root.isMember("testPresets"))) {
  376. return ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED;
  377. }
  378. RootPresets presets;
  379. if ((result = RootPresetsHelper(presets, &root)) !=
  380. ReadFileResult::READ_OK) {
  381. return result;
  382. }
  383. unsigned int currentMajor = cmVersion::GetMajorVersion();
  384. unsigned int currentMinor = cmVersion::GetMinorVersion();
  385. unsigned int currentPatch = cmVersion::GetPatchVersion();
  386. auto const& required = presets.CMakeMinimumRequired;
  387. if (required.Major > currentMajor ||
  388. (required.Major == currentMajor &&
  389. (required.Minor > currentMinor ||
  390. (required.Minor == currentMinor &&
  391. (required.Patch > currentPatch))))) {
  392. return ReadFileResult::UNRECOGNIZED_CMAKE_VERSION;
  393. }
  394. for (auto& preset : presets.ConfigurePresets) {
  395. preset.User = user;
  396. if (preset.Name.empty()) {
  397. return ReadFileResult::INVALID_PRESET;
  398. }
  399. PresetPair<ConfigurePreset> presetPair;
  400. presetPair.Unexpanded = preset;
  401. presetPair.Expanded = cm::nullopt;
  402. if (!this->ConfigurePresets
  403. .emplace(std::make_pair(preset.Name, presetPair))
  404. .second) {
  405. return ReadFileResult::DUPLICATE_PRESETS;
  406. }
  407. // Support for installDir presets added in version 3.
  408. if (v < 3 && !preset.InstallDir.empty()) {
  409. return ReadFileResult::INSTALL_PREFIX_UNSUPPORTED;
  410. }
  411. // Support for conditions added in version 3.
  412. if (v < 3 && preset.ConditionEvaluator) {
  413. return ReadFileResult::CONDITION_UNSUPPORTED;
  414. }
  415. // Support for toolchainFile presets added in version 3.
  416. if (v < 3 && !preset.ToolchainFile.empty()) {
  417. return ReadFileResult::TOOLCHAIN_FILE_UNSUPPORTED;
  418. }
  419. this->ConfigurePresetOrder.push_back(preset.Name);
  420. }
  421. for (auto& preset : presets.BuildPresets) {
  422. preset.User = user;
  423. if (preset.Name.empty()) {
  424. return ReadFileResult::INVALID_PRESET;
  425. }
  426. PresetPair<BuildPreset> presetPair;
  427. presetPair.Unexpanded = preset;
  428. presetPair.Expanded = cm::nullopt;
  429. if (!this->BuildPresets.emplace(preset.Name, presetPair).second) {
  430. return ReadFileResult::DUPLICATE_PRESETS;
  431. }
  432. // Support for conditions added in version 3.
  433. if (v < 3 && preset.ConditionEvaluator) {
  434. return ReadFileResult::CONDITION_UNSUPPORTED;
  435. }
  436. this->BuildPresetOrder.push_back(preset.Name);
  437. }
  438. for (auto& preset : presets.TestPresets) {
  439. preset.User = user;
  440. if (preset.Name.empty()) {
  441. return ReadFileResult::INVALID_PRESET;
  442. }
  443. PresetPair<TestPreset> presetPair;
  444. presetPair.Unexpanded = preset;
  445. presetPair.Expanded = cm::nullopt;
  446. if (!this->TestPresets.emplace(preset.Name, presetPair).second) {
  447. return ReadFileResult::DUPLICATE_PRESETS;
  448. }
  449. // Support for conditions added in version 3.
  450. if (v < 3 && preset.ConditionEvaluator) {
  451. return ReadFileResult::CONDITION_UNSUPPORTED;
  452. }
  453. this->TestPresetOrder.push_back(preset.Name);
  454. }
  455. return ReadFileResult::READ_OK;
  456. }