cmCMakeHostSystemInformationCommand.cxx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  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 "cmCMakeHostSystemInformationCommand.h"
  4. #include <algorithm>
  5. #include <cassert>
  6. #include <cctype>
  7. #include <cstddef>
  8. #include <initializer_list>
  9. #include <map>
  10. #include <string>
  11. #include <utility>
  12. #include <cm/optional>
  13. #include <cm/string_view>
  14. #include <cmext/string_view>
  15. #include "cmsys/FStream.hxx"
  16. #include "cmsys/Glob.hxx"
  17. #include "cmsys/SystemInformation.hxx"
  18. #include "cmArgumentParser.h"
  19. #include "cmExecutionStatus.h"
  20. #include "cmList.h"
  21. #include "cmMakefile.h"
  22. #include "cmRange.h"
  23. #include "cmStringAlgorithms.h"
  24. #include "cmSystemTools.h"
  25. #include "cmValue.h"
  26. #include "cmWindowsRegistry.h"
  27. #ifdef _WIN32
  28. # include "cmAlgorithms.h"
  29. # include "cmGlobalGenerator.h"
  30. # include "cmGlobalVisualStudio10Generator.h"
  31. # include "cmGlobalVisualStudioVersionedGenerator.h"
  32. # include "cmVSSetupHelper.h"
  33. #endif
  34. namespace {
  35. std::string const DELIM[2] = { {}, ";" };
  36. // BEGIN Private functions
  37. std::string ValueToString(std::size_t const value)
  38. {
  39. return std::to_string(value);
  40. }
  41. std::string ValueToString(const char* const value)
  42. {
  43. return value ? value : std::string{};
  44. }
  45. std::string ValueToString(std::string const& value)
  46. {
  47. return value;
  48. }
  49. cm::optional<std::string> GetValue(cmsys::SystemInformation& info,
  50. std::string const& key)
  51. {
  52. if (key == "NUMBER_OF_LOGICAL_CORES"_s) {
  53. return ValueToString(info.GetNumberOfLogicalCPU());
  54. }
  55. if (key == "NUMBER_OF_PHYSICAL_CORES"_s) {
  56. return ValueToString(info.GetNumberOfPhysicalCPU());
  57. }
  58. if (key == "HOSTNAME"_s) {
  59. return ValueToString(info.GetHostname());
  60. }
  61. if (key == "FQDN"_s) {
  62. return ValueToString(info.GetFullyQualifiedDomainName());
  63. }
  64. if (key == "TOTAL_VIRTUAL_MEMORY"_s) {
  65. return ValueToString(info.GetTotalVirtualMemory());
  66. }
  67. if (key == "AVAILABLE_VIRTUAL_MEMORY"_s) {
  68. return ValueToString(info.GetAvailableVirtualMemory());
  69. }
  70. if (key == "TOTAL_PHYSICAL_MEMORY"_s) {
  71. return ValueToString(info.GetTotalPhysicalMemory());
  72. }
  73. if (key == "AVAILABLE_PHYSICAL_MEMORY"_s) {
  74. return ValueToString(info.GetAvailablePhysicalMemory());
  75. }
  76. if (key == "IS_64BIT"_s) {
  77. return ValueToString(info.Is64Bits());
  78. }
  79. if (key == "HAS_FPU"_s) {
  80. return ValueToString(
  81. info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_FPU));
  82. }
  83. if (key == "HAS_MMX"_s) {
  84. return ValueToString(
  85. info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_MMX));
  86. }
  87. if (key == "HAS_MMX_PLUS"_s) {
  88. return ValueToString(info.DoesCPUSupportFeature(
  89. cmsys::SystemInformation::CPU_FEATURE_MMX_PLUS));
  90. }
  91. if (key == "HAS_SSE"_s) {
  92. return ValueToString(
  93. info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE));
  94. }
  95. if (key == "HAS_SSE2"_s) {
  96. return ValueToString(
  97. info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_SSE2));
  98. }
  99. if (key == "HAS_SSE_FP"_s) {
  100. return ValueToString(info.DoesCPUSupportFeature(
  101. cmsys::SystemInformation::CPU_FEATURE_SSE_FP));
  102. }
  103. if (key == "HAS_SSE_MMX"_s) {
  104. return ValueToString(info.DoesCPUSupportFeature(
  105. cmsys::SystemInformation::CPU_FEATURE_SSE_MMX));
  106. }
  107. if (key == "HAS_AMD_3DNOW"_s) {
  108. return ValueToString(info.DoesCPUSupportFeature(
  109. cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW));
  110. }
  111. if (key == "HAS_AMD_3DNOW_PLUS"_s) {
  112. return ValueToString(info.DoesCPUSupportFeature(
  113. cmsys::SystemInformation::CPU_FEATURE_AMD_3DNOW_PLUS));
  114. }
  115. if (key == "HAS_IA64"_s) {
  116. return ValueToString(
  117. info.DoesCPUSupportFeature(cmsys::SystemInformation::CPU_FEATURE_IA64));
  118. }
  119. if (key == "HAS_SERIAL_NUMBER"_s) {
  120. return ValueToString(info.DoesCPUSupportFeature(
  121. cmsys::SystemInformation::CPU_FEATURE_SERIALNUMBER));
  122. }
  123. if (key == "PROCESSOR_NAME"_s) {
  124. return ValueToString(info.GetExtendedProcessorName());
  125. }
  126. if (key == "PROCESSOR_DESCRIPTION"_s) {
  127. return info.GetCPUDescription();
  128. }
  129. if (key == "PROCESSOR_SERIAL_NUMBER"_s) {
  130. return ValueToString(info.GetProcessorSerialNumber());
  131. }
  132. if (key == "OS_NAME"_s) {
  133. return ValueToString(info.GetOSName());
  134. }
  135. if (key == "OS_RELEASE"_s) {
  136. return ValueToString(info.GetOSRelease());
  137. }
  138. if (key == "OS_VERSION"_s) {
  139. return ValueToString(info.GetOSVersion());
  140. }
  141. if (key == "OS_PLATFORM"_s) {
  142. return ValueToString(info.GetOSPlatform());
  143. }
  144. return {};
  145. }
  146. cm::optional<std::pair<std::string, std::string>> ParseOSReleaseLine(
  147. std::string const& line)
  148. {
  149. std::string key;
  150. std::string value;
  151. char prev = 0;
  152. enum ParserState
  153. {
  154. PARSE_KEY_1ST,
  155. PARSE_KEY,
  156. FOUND_EQ,
  157. PARSE_SINGLE_QUOTE_VALUE,
  158. PARSE_DBL_QUOTE_VALUE,
  159. PARSE_VALUE,
  160. IGNORE_REST
  161. } state = PARSE_KEY_1ST;
  162. for (auto ch : line) {
  163. switch (state) {
  164. case PARSE_KEY_1ST:
  165. if (std::isalpha(ch) || ch == '_') {
  166. key += ch;
  167. state = PARSE_KEY;
  168. } else if (!cmIsSpace(ch)) {
  169. state = IGNORE_REST;
  170. }
  171. break;
  172. case PARSE_KEY:
  173. if (ch == '=') {
  174. state = FOUND_EQ;
  175. } else if (std::isalnum(ch) || ch == '_') {
  176. key += ch;
  177. } else {
  178. state = IGNORE_REST;
  179. }
  180. break;
  181. case FOUND_EQ:
  182. switch (ch) {
  183. case '\'':
  184. state = PARSE_SINGLE_QUOTE_VALUE;
  185. break;
  186. case '"':
  187. state = PARSE_DBL_QUOTE_VALUE;
  188. break;
  189. case '#':
  190. case '\\':
  191. state = IGNORE_REST;
  192. break;
  193. default:
  194. value += ch;
  195. state = PARSE_VALUE;
  196. }
  197. break;
  198. case PARSE_SINGLE_QUOTE_VALUE:
  199. if (ch == '\'') {
  200. if (prev != '\\') {
  201. state = IGNORE_REST;
  202. } else {
  203. assert(!value.empty());
  204. value[value.size() - 1] = ch;
  205. }
  206. } else {
  207. value += ch;
  208. }
  209. break;
  210. case PARSE_DBL_QUOTE_VALUE:
  211. if (ch == '"') {
  212. if (prev != '\\') {
  213. state = IGNORE_REST;
  214. } else {
  215. assert(!value.empty());
  216. value[value.size() - 1] = ch;
  217. }
  218. } else {
  219. value += ch;
  220. }
  221. break;
  222. case PARSE_VALUE:
  223. if (ch == '#' || cmIsSpace(ch)) {
  224. state = IGNORE_REST;
  225. } else {
  226. value += ch;
  227. }
  228. break;
  229. default:
  230. // Unexpected os-release parser state!
  231. state = IGNORE_REST;
  232. break;
  233. }
  234. if (state == IGNORE_REST) {
  235. break;
  236. }
  237. prev = ch;
  238. }
  239. if (!(key.empty() || value.empty())) {
  240. return std::make_pair(key, value);
  241. }
  242. return {};
  243. }
  244. std::map<std::string, std::string> GetOSReleaseVariables(
  245. cmExecutionStatus& status)
  246. {
  247. auto& makefile = status.GetMakefile();
  248. const auto& sysroot = makefile.GetSafeDefinition("CMAKE_SYSROOT");
  249. std::map<std::string, std::string> data;
  250. // Based on
  251. // https://www.freedesktop.org/software/systemd/man/latest/os-release.html
  252. for (auto name : { "/etc/os-release"_s, "/usr/lib/os-release"_s }) {
  253. const auto& filename = cmStrCat(sysroot, name);
  254. if (cmSystemTools::FileExists(filename)) {
  255. cmsys::ifstream fin(filename.c_str());
  256. for (std::string line; !std::getline(fin, line).fail();) {
  257. auto kv = ParseOSReleaseLine(line);
  258. if (kv.has_value()) {
  259. data.emplace(kv.value());
  260. }
  261. }
  262. break;
  263. }
  264. }
  265. // Got smth?
  266. if (!data.empty()) {
  267. return data;
  268. }
  269. // Ugh, it could be some pre-os-release distro.
  270. // Lets try some fallback getters.
  271. // See also:
  272. // - http://linuxmafia.com/faq/Admin/release-files.html
  273. // 1. CMake provided
  274. cmsys::Glob gl;
  275. std::vector<std::string> scripts;
  276. auto const findExpr = cmStrCat(cmSystemTools::GetCMakeRoot(),
  277. "/Modules/Internal/OSRelease/*.cmake");
  278. if (gl.FindFiles(findExpr)) {
  279. scripts = gl.GetFiles();
  280. }
  281. // 2. User provided (append to the CMake prvided)
  282. cmList::append(
  283. scripts, makefile.GetDefinition("CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS"));
  284. // Filter out files that are not in format `NNN-name.cmake`
  285. auto checkName = [](std::string const& filepath) -> bool {
  286. auto const& filename = cmSystemTools::GetFilenameName(filepath);
  287. // NOTE Minimum filename length expected:
  288. // NNN-<at-least-one-char-name>.cmake --> 11
  289. return (filename.size() < 11) || !std::isdigit(filename[0]) ||
  290. !std::isdigit(filename[1]) || !std::isdigit(filename[2]) ||
  291. filename[3] != '-';
  292. };
  293. scripts.erase(std::remove_if(scripts.begin(), scripts.end(), checkName),
  294. scripts.end());
  295. // Make sure scripts are running in desired order
  296. std::sort(scripts.begin(), scripts.end(),
  297. [](std::string const& lhs, std::string const& rhs) -> bool {
  298. long lhs_order;
  299. cmStrToLong(cmSystemTools::GetFilenameName(lhs).substr(0u, 3u),
  300. &lhs_order);
  301. long rhs_order;
  302. cmStrToLong(cmSystemTools::GetFilenameName(rhs).substr(0u, 3u),
  303. &rhs_order);
  304. return lhs_order < rhs_order;
  305. });
  306. // Name of the variable to put the results
  307. std::string const result_variable{ "CMAKE_GET_OS_RELEASE_FALLBACK_RESULT" };
  308. for (auto const& script : scripts) {
  309. // Unset the result variable
  310. makefile.RemoveDefinition(result_variable);
  311. // include FATAL_ERROR and ERROR in the return status
  312. if (!makefile.ReadListFile(script) ||
  313. cmSystemTools::GetErrorOccurredFlag()) {
  314. // Ok, no worries... go try the next script.
  315. continue;
  316. }
  317. cmList variables{ makefile.GetDefinition(result_variable) };
  318. if (variables.empty()) {
  319. // Heh, this script didn't found anything... go try the next one.
  320. continue;
  321. }
  322. for (auto const& variable : variables) {
  323. auto value = makefile.GetSafeDefinition(variable);
  324. makefile.RemoveDefinition(variable);
  325. if (!cmHasPrefix(variable, cmStrCat(result_variable, '_'))) {
  326. // Ignore unknown variable set by the script
  327. continue;
  328. }
  329. auto key = variable.substr(result_variable.size() + 1,
  330. variable.size() - result_variable.size() - 1);
  331. data.emplace(std::move(key), std::move(value));
  332. }
  333. // Try 'till some script can get anything
  334. if (!data.empty()) {
  335. data.emplace("USED_FALLBACK_SCRIPT", script);
  336. break;
  337. }
  338. }
  339. makefile.RemoveDefinition(result_variable);
  340. return data;
  341. }
  342. cm::optional<std::string> GetDistribValue(cmExecutionStatus& status,
  343. std::string const& key,
  344. std::string const& variable)
  345. {
  346. const auto prefix = "DISTRIB_"_s;
  347. if (!cmHasPrefix(key, prefix)) {
  348. return {};
  349. }
  350. static const std::map<std::string, std::string> s_os_release =
  351. GetOSReleaseVariables(status);
  352. auto& makefile = status.GetMakefile();
  353. const std::string subkey =
  354. key.substr(prefix.size(), key.size() - prefix.size());
  355. if (subkey == "INFO"_s) {
  356. std::string vars;
  357. for (const auto& kv : s_os_release) {
  358. auto cmake_var_name = cmStrCat(variable, '_', kv.first);
  359. vars += DELIM[!vars.empty()] + cmake_var_name;
  360. makefile.AddDefinition(cmake_var_name, kv.second);
  361. }
  362. return cm::optional<std::string>(std::move(vars));
  363. }
  364. // Query individual variable
  365. const auto it = s_os_release.find(subkey);
  366. if (it != s_os_release.cend()) {
  367. return it->second;
  368. }
  369. // NOTE Empty string means requested variable not set
  370. return std::string{};
  371. }
  372. #ifdef _WIN32
  373. std::string FindMSYSTEM_PREFIX(std::vector<std::string> prefixes)
  374. {
  375. for (std::string const& prefix : prefixes) {
  376. std::string out;
  377. std::string err;
  378. int ret;
  379. // In a modern MSYSTEM environment we expect cygpath to be in PATH.
  380. std::vector<std::string> cygpath_cmd{ "cygpath", "-w", prefix };
  381. if (cmSystemTools::RunSingleCommand(cygpath_cmd, &out, &err, &ret, nullptr,
  382. cmSystemTools::OUTPUT_NONE)) {
  383. if (ret == 0) {
  384. out = cmTrimWhitespace(out);
  385. cmSystemTools::ConvertToUnixSlashes(out);
  386. if (cmSystemTools::FileIsDirectory(out)) {
  387. return out;
  388. }
  389. }
  390. } else {
  391. // In a legacy MSYSTEM environment (MinGW/MSYS 1.0) there is no
  392. // cygpath but we expect 'sh' to be in PATH.
  393. std::vector<std::string> sh_cmd{
  394. "sh", "-c", cmStrCat("cd \"", prefix, "\" && cmd //c cd")
  395. };
  396. if (cmSystemTools::RunSingleCommand(sh_cmd, &out, &err, &ret, nullptr,
  397. cmSystemTools::OUTPUT_NONE)) {
  398. if (ret == 0) {
  399. out = cmTrimWhitespace(out);
  400. cmSystemTools::ConvertToUnixSlashes(out);
  401. if (cmSystemTools::FileIsDirectory(out)) {
  402. return out;
  403. }
  404. }
  405. }
  406. }
  407. }
  408. return {};
  409. }
  410. std::string FallbackMSYSTEM_PREFIX(cm::string_view msystem)
  411. {
  412. // These layouts are used by distributions such as
  413. // * MSYS2: https://www.msys2.org/docs/environments/
  414. // * MinGW/MSYS 1.0: http://mingw.osdn.io/
  415. if (msystem == "MSYS"_s) {
  416. static std::string const msystem_msys = FindMSYSTEM_PREFIX({ "/usr" });
  417. return msystem_msys;
  418. }
  419. if (msystem == "MINGW32"_s) {
  420. static std::string const msystem_mingw32 =
  421. FindMSYSTEM_PREFIX({ "/mingw32", "/mingw" });
  422. return msystem_mingw32;
  423. }
  424. if (msystem == "MINGW64"_s) {
  425. static std::string const msystem_mingw64 =
  426. FindMSYSTEM_PREFIX({ "/mingw64" });
  427. return msystem_mingw64;
  428. }
  429. if (msystem == "UCRT64"_s) {
  430. static std::string const msystem_ucrt64 =
  431. FindMSYSTEM_PREFIX({ "/ucrt64" });
  432. return msystem_ucrt64;
  433. }
  434. if (msystem == "CLANG32"_s) {
  435. static std::string const msystem_clang32 =
  436. FindMSYSTEM_PREFIX({ "/clang32" });
  437. return msystem_clang32;
  438. }
  439. if (msystem == "CLANG64"_s) {
  440. static std::string const msystem_clang64 =
  441. FindMSYSTEM_PREFIX({ "/clang64" });
  442. return msystem_clang64;
  443. }
  444. if (msystem == "CLANGARM64"_s) {
  445. static std::string const msystem_clangarm64 =
  446. FindMSYSTEM_PREFIX({ "/clangarm64" });
  447. return msystem_clangarm64;
  448. }
  449. return {};
  450. }
  451. cm::optional<std::string> GetWindowsValue(cmExecutionStatus& status,
  452. std::string const& key)
  453. {
  454. auto* const gg = status.GetMakefile().GetGlobalGenerator();
  455. for (auto vs : { 15, 16, 17 }) {
  456. if (key == cmStrCat("VS_"_s, vs, "_DIR"_s)) {
  457. std::string value;
  458. // If generating for the VS nn IDE, use the same instance.
  459. if (cmHasPrefix(gg->GetName(), cmStrCat("Visual Studio "_s, vs, ' '))) {
  460. cmGlobalVisualStudioVersionedGenerator* vsNNgen =
  461. static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
  462. if (vsNNgen->GetVSInstance(value)) {
  463. return value;
  464. }
  465. }
  466. // Otherwise, find a VS nn instance ourselves.
  467. cmVSSetupAPIHelper vsSetupAPIHelper(vs);
  468. if (vsSetupAPIHelper.GetVSInstanceInfo(value)) {
  469. cmSystemTools::ConvertToUnixSlashes(value);
  470. }
  471. return value;
  472. }
  473. }
  474. if (key == "VS_MSBUILD_COMMAND"_s && gg->IsVisualStudioAtLeast10()) {
  475. cmGlobalVisualStudio10Generator* vs10gen =
  476. static_cast<cmGlobalVisualStudio10Generator*>(gg);
  477. return vs10gen->FindMSBuildCommandEarly(&status.GetMakefile());
  478. }
  479. if (key == "MSYSTEM_PREFIX") {
  480. // MSYSTEM_PREFIX is meaningful only under a MSYSTEM environment.
  481. cm::optional<std::string> ms = cmSystemTools::GetEnvVar("MSYSTEM");
  482. if (!ms || ms->empty()) {
  483. return std::string();
  484. }
  485. // Prefer the MSYSTEM_PREFIX environment variable.
  486. if (cm::optional<std::string> msp =
  487. cmSystemTools::GetEnvVar("MSYSTEM_PREFIX")) {
  488. cmSystemTools::ConvertToUnixSlashes(*msp);
  489. if (cmSystemTools::FileIsDirectory(*msp)) {
  490. return msp;
  491. }
  492. }
  493. // Fall back to known distribution layouts.
  494. return FallbackMSYSTEM_PREFIX(*ms);
  495. }
  496. return {};
  497. }
  498. #endif
  499. cm::optional<std::string> GetValueChained()
  500. {
  501. return {};
  502. }
  503. template <typename GetterFn, typename... Next>
  504. cm::optional<std::string> GetValueChained(GetterFn current, Next... chain)
  505. {
  506. auto value = current();
  507. if (value.has_value()) {
  508. return value;
  509. }
  510. return GetValueChained(chain...);
  511. }
  512. template <typename Range>
  513. bool QueryWindowsRegistry(Range args, cmExecutionStatus& status,
  514. std::string const& variable)
  515. {
  516. using View = cmWindowsRegistry::View;
  517. if (args.empty()) {
  518. status.SetError("missing <key> specification.");
  519. return false;
  520. }
  521. std::string const& key = *args.begin();
  522. struct Arguments : public ArgumentParser::ParseResult
  523. {
  524. std::string ValueName;
  525. bool ValueNames = false;
  526. bool SubKeys = false;
  527. std::string View;
  528. std::string Separator;
  529. std::string ErrorVariable;
  530. };
  531. cmArgumentParser<Arguments> parser;
  532. parser.Bind("VALUE"_s, &Arguments::ValueName)
  533. .Bind("VALUE_NAMES"_s, &Arguments::ValueNames)
  534. .Bind("SUBKEYS"_s, &Arguments::SubKeys)
  535. .Bind("VIEW"_s, &Arguments::View)
  536. .Bind("SEPARATOR"_s, &Arguments::Separator)
  537. .Bind("ERROR_VARIABLE"_s, &Arguments::ErrorVariable);
  538. std::vector<std::string> invalidArgs;
  539. Arguments const arguments = parser.Parse(args.advance(1), &invalidArgs);
  540. if (!invalidArgs.empty()) {
  541. status.SetError(cmStrCat("given invalid argument(s) \"",
  542. cmJoin(invalidArgs, ", "_s), "\"."));
  543. return false;
  544. }
  545. if (arguments.MaybeReportError(status.GetMakefile())) {
  546. return true;
  547. }
  548. if ((!arguments.ValueName.empty() &&
  549. (arguments.ValueNames || arguments.SubKeys)) ||
  550. (arguments.ValueName.empty() && arguments.ValueNames &&
  551. arguments.SubKeys)) {
  552. status.SetError("given mutually exclusive sub-options VALUE, "
  553. "VALUE_NAMES or SUBKEYS.");
  554. return false;
  555. }
  556. if (!arguments.View.empty() && !cmWindowsRegistry::ToView(arguments.View)) {
  557. status.SetError(
  558. cmStrCat("given invalid value for VIEW: ", arguments.View, '.'));
  559. return false;
  560. }
  561. auto& makefile = status.GetMakefile();
  562. makefile.AddDefinition(variable, ""_s);
  563. auto view = arguments.View.empty()
  564. ? View::Both
  565. : *cmWindowsRegistry::ToView(arguments.View);
  566. cmWindowsRegistry registry(makefile);
  567. if (arguments.ValueNames) {
  568. auto result = registry.GetValueNames(key, view);
  569. if (result) {
  570. makefile.AddDefinition(variable, cmList::to_string(*result));
  571. }
  572. } else if (arguments.SubKeys) {
  573. auto result = registry.GetSubKeys(key, view);
  574. if (result) {
  575. makefile.AddDefinition(variable, cmList::to_string(*result));
  576. }
  577. } else {
  578. auto result =
  579. registry.ReadValue(key, arguments.ValueName, view, arguments.Separator);
  580. if (result) {
  581. makefile.AddDefinition(variable, *result);
  582. }
  583. }
  584. // return error message if requested
  585. if (!arguments.ErrorVariable.empty()) {
  586. makefile.AddDefinition(arguments.ErrorVariable, registry.GetLastError());
  587. }
  588. return true;
  589. }
  590. // END Private functions
  591. } // anonymous namespace
  592. // cmCMakeHostSystemInformation
  593. bool cmCMakeHostSystemInformationCommand(std::vector<std::string> const& args,
  594. cmExecutionStatus& status)
  595. {
  596. std::size_t current_index = 0;
  597. if (args.size() < (current_index + 2) || args[current_index] != "RESULT"_s) {
  598. status.SetError("missing RESULT specification.");
  599. return false;
  600. }
  601. auto const& variable = args[current_index + 1];
  602. current_index += 2;
  603. if (args.size() < (current_index + 2) || args[current_index] != "QUERY"_s) {
  604. status.SetError("missing QUERY specification");
  605. return false;
  606. }
  607. if (args[current_index + 1] == "WINDOWS_REGISTRY"_s) {
  608. return QueryWindowsRegistry(cmMakeRange(args).advance(current_index + 2),
  609. status, variable);
  610. }
  611. static cmsys::SystemInformation info;
  612. static auto initialized = false;
  613. if (!initialized) {
  614. info.RunCPUCheck();
  615. info.RunOSCheck();
  616. info.RunMemoryCheck();
  617. initialized = true;
  618. }
  619. std::string result_list;
  620. for (auto i = current_index + 1; i < args.size(); ++i) {
  621. result_list += DELIM[!result_list.empty()];
  622. auto const& key = args[i];
  623. // clang-format off
  624. auto value =
  625. GetValueChained(
  626. [&]() { return GetValue(info, key); }
  627. , [&]() { return GetDistribValue(status, key, variable); }
  628. #ifdef _WIN32
  629. , [&]() { return GetWindowsValue(status, key); }
  630. #endif
  631. );
  632. // clang-format on
  633. if (!value) {
  634. status.SetError("does not recognize <key> " + key);
  635. return false;
  636. }
  637. result_list += value.value();
  638. }
  639. status.GetMakefile().AddDefinition(variable, result_list);
  640. return true;
  641. }