Coverage Report

Created: 2026-03-12 06:35

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