Coverage Report

Created: 2026-09-14 06:43

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