Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmStandardLevelResolver.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
4
#include "cmStandardLevelResolver.h"
5
6
#include <algorithm>
7
#include <cassert>
8
#include <cstddef>
9
#include <set>
10
#include <stdexcept>
11
#include <unordered_map>
12
#include <utility>
13
#include <vector>
14
15
#include <cm/iterator>
16
#include <cm/optional>
17
#include <cm/string_view>
18
#include <cmext/algorithm>
19
#include <cmext/string_view>
20
21
#include "cmFileSet.h"
22
#include "cmFileSetMetadata.h"
23
#include "cmGeneratorExpression.h"
24
#include "cmGeneratorTarget.h"
25
#include "cmGlobalGenerator.h"
26
#include "cmList.h"
27
#include "cmListFileCache.h"
28
#include "cmMakefile.h"
29
#include "cmMessageType.h"
30
#include "cmPolicies.h"
31
#include "cmSourceFile.h"
32
#include "cmStandardLevel.h"
33
#include "cmStringAlgorithms.h"
34
#include "cmTarget.h"
35
#include "cmValue.h"
36
#include "cmake.h"
37
38
namespace {
39
40
#define FEATURE_STRING(F) , #F
41
char const* const C_FEATURES[] = { nullptr FOR_EACH_C_FEATURE(
42
  FEATURE_STRING) };
43
44
char const* const CXX_FEATURES[] = { nullptr FOR_EACH_CXX_FEATURE(
45
  FEATURE_STRING) };
46
47
char const* const CUDA_FEATURES[] = { nullptr FOR_EACH_CUDA_FEATURE(
48
  FEATURE_STRING) };
49
50
char const* const HIP_FEATURES[] = { nullptr FOR_EACH_HIP_FEATURE(
51
  FEATURE_STRING) };
52
53
char const* const SYCL_FEATURES[] = { nullptr FOR_EACH_SYCL_FEATURE(
54
  FEATURE_STRING) };
55
#undef FEATURE_STRING
56
57
bool TargetUsesSYCLCompileFeatures(cmMakefile* makefile,
58
                                   std::string const& targetName)
59
0
{
60
0
  auto* globalGenerator = makefile->GetGlobalGenerator();
61
0
  if (!globalGenerator->GetLanguageEnabled("SYCL")) {
62
0
    return false;
63
0
  }
64
0
  if (!globalGenerator->GetLanguageEnabled("CXX")) {
65
0
    return true;
66
0
  }
67
68
0
  cmTarget* target = makefile->FindTargetToUse(targetName);
69
0
  if (!target) {
70
0
    return false;
71
0
  }
72
73
0
  if (cmGeneratorTarget* generatorTarget =
74
0
        globalGenerator->FindGeneratorTarget(targetName)) {
75
0
    std::set<std::string> const languages =
76
0
      generatorTarget->GetAllConfigCompileLanguages();
77
0
    return languages.find("SYCL") != languages.end() &&
78
0
      languages.find("CXX") == languages.end();
79
0
  }
80
81
0
  bool hasCxxSource = false;
82
0
  bool hasSyclSource = false;
83
0
  bool hasSyclHeaders = false;
84
0
  auto recordLanguage = [&hasCxxSource, &hasSyclSource](cm::string_view lang) {
85
0
    hasCxxSource = hasCxxSource || lang == "CXX"_s;
86
0
    hasSyclSource = hasSyclSource || lang == "SYCL"_s;
87
0
  };
88
0
  for (auto const& name : target->GetAllPrivateFileSets()) {
89
0
    cmFileSet const* fileSet = target->GetFileSet(name);
90
0
    if (fileSet->GetType() == cm::FileSetMetadata::HEADERS) {
91
0
      hasSyclHeaders =
92
0
        hasSyclHeaders || fileSet->GetProperty("LANGUAGE") == "SYCL"_s;
93
0
      continue;
94
0
    }
95
0
    if (fileSet->GetType() != cm::FileSetMetadata::SOURCES ||
96
0
        fileSet->GetFileEntries().empty()) {
97
0
      continue;
98
0
    }
99
0
    cmValue const language = fileSet->GetProperty("LANGUAGE");
100
0
    if (!language.IsEmpty()) {
101
0
      recordLanguage(*language);
102
0
      continue;
103
0
    }
104
0
    for (auto const& entry : fileSet->GetFileEntries()) {
105
0
      for (auto const& source : cmList{ entry.Value }) {
106
0
        if (cmGeneratorExpression::Find(source) == std::string::npos) {
107
0
          recordLanguage(target->GetMakefile()
108
0
                           ->GetOrCreateSource(source)
109
0
                           ->GetOrDetermineLanguage());
110
0
        }
111
0
      }
112
0
    }
113
0
  }
114
0
  for (auto const& entry : target->GetSourceEntries()) {
115
0
    cmList sources{ entry.Value };
116
0
    for (auto const& source : sources) {
117
0
      if (cmGeneratorExpression::Find(source) != std::string::npos) {
118
0
        continue;
119
0
      }
120
0
      std::string const& language = target->GetMakefile()
121
0
                                      ->GetOrCreateSource(source)
122
0
                                      ->GetOrDetermineLanguage();
123
0
      recordLanguage(language);
124
0
    }
125
0
  }
126
0
  if (hasSyclHeaders && hasCxxSource) {
127
0
    hasCxxSource = false;
128
0
    hasSyclSource = true;
129
0
  }
130
0
  return hasSyclSource && !hasCxxSource;
131
0
}
132
133
int ParseStd(std::string const& level)
134
24
{
135
24
  try {
136
24
    return std::stoi(level);
137
24
  } catch (std::invalid_argument&) {
138
    // Fall through to use an invalid value.
139
0
  }
140
0
  return -1;
141
24
}
142
143
struct StandardLevelComputer
144
{
145
  explicit StandardLevelComputer(std::string lang, std::vector<int> levels,
146
                                 std::vector<std::string> levelsStr)
147
70
    : Language(std::move(lang))
148
70
    , Levels(std::move(levels))
149
70
    , LevelsAsStrings(std::move(levelsStr))
150
70
  {
151
70
    assert(this->Levels.size() == this->LevelsAsStrings.size());
152
70
  }
153
154
  // Note that the logic here is shadowed in `GetEffectiveStandard`; if one is
155
  // changed, the other needs changed as well.
156
  std::string GetCompileOptionDef(cmMakefile* makefile,
157
                                  cmGeneratorTarget const* target,
158
                                  std::string const& config) const
159
0
  {
160
161
0
    auto const& stds = this->Levels;
162
0
    auto const& stdsStrings = this->LevelsAsStrings;
163
164
0
    cmValue defaultStd = makefile->GetDefinition(
165
0
      cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
166
0
    if (!cmNonempty(defaultStd)) {
167
      // this compiler has no notion of language standard levels
168
0
      return std::string{};
169
0
    }
170
171
0
    cmPolicies::PolicyStatus const cmp0128{ makefile->GetPolicyStatus(
172
0
      cmPolicies::CMP0128) };
173
0
    bool const defaultExt{ makefile
174
0
                             ->GetDefinition(cmStrCat("CMAKE_", this->Language,
175
0
                                                      "_EXTENSIONS_DEFAULT"))
176
0
                             .IsOn() };
177
0
    bool ext = true;
178
179
0
    if (cmp0128 == cmPolicies::NEW) {
180
0
      ext = defaultExt;
181
0
    }
182
183
0
    if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) {
184
0
      ext = extPropValue.IsOn();
185
0
    }
186
187
0
    std::string const type{ ext ? "EXTENSION" : "STANDARD" };
188
189
0
    cmValue standardProp = target->GetLanguageStandard(this->Language, config);
190
0
    if (!standardProp) {
191
0
      if (cmp0128 == cmPolicies::NEW) {
192
        // Add extension flag if compiler's default doesn't match.
193
0
        if (ext != defaultExt) {
194
0
          return cmStrCat("CMAKE_", this->Language, *defaultStd, '_', type,
195
0
                          "_COMPILE_OPTION");
196
0
        }
197
0
      } else {
198
0
        if (cmp0128 == cmPolicies::WARN &&
199
0
            makefile->PolicyOptionalWarningEnabled(
200
0
              "CMAKE_POLICY_WARNING_CMP0128") &&
201
0
            ext != defaultExt) {
202
0
          char const* state{};
203
0
          if (ext) {
204
0
            if (!makefile->GetDefinition(cmStrCat(
205
0
                  "CMAKE_", this->Language, "_EXTENSION_COMPILE_OPTION"))) {
206
0
              state = "enabled";
207
0
            }
208
0
          } else {
209
0
            state = "disabled";
210
0
          }
211
0
          if (state) {
212
0
            makefile->IssuePolicyWarning(
213
0
              cmPolicies::CMP0128, {},
214
0
              cmStrCat("For compatibility with older versions of CMake, "
215
0
                       "compiler extensions won't be "_s,
216
0
                       state, '.'));
217
0
          }
218
0
        }
219
220
0
        if (ext) {
221
0
          return cmStrCat("CMAKE_", this->Language,
222
0
                          "_EXTENSION_COMPILE_OPTION");
223
0
        }
224
0
      }
225
0
      return std::string{};
226
0
    }
227
228
0
    if (target->GetLanguageStandardRequired(this->Language)) {
229
0
      std::string option_flag = cmStrCat(
230
0
        "CMAKE_", this->Language, *standardProp, '_', type, "_COMPILE_OPTION");
231
232
0
      cmValue opt = target->Target->GetMakefile()->GetDefinition(option_flag);
233
0
      if (!opt) {
234
0
        makefile->IssueMessage(
235
0
          MessageType::FATAL_ERROR,
236
0
          cmStrCat("Target \"", target->GetName(),
237
0
                   "\" requires the language dialect \"", this->Language,
238
0
                   *standardProp, "\" ",
239
0
                   ext ? "(with compiler extensions)" : "",
240
0
                   ". But the current compiler \"",
241
0
                   makefile->GetSafeDefinition(
242
0
                     cmStrCat("CMAKE_", this->Language, "_COMPILER_ID")),
243
0
                   "\" does not support this, or "
244
0
                   "CMake does not know the flags to enable it."));
245
0
      }
246
0
      return option_flag;
247
0
    }
248
249
    // If the request matches the compiler's defaults we don't need to add
250
    // anything.
251
0
    if (*standardProp == *defaultStd && ext == defaultExt) {
252
0
      if (cmp0128 == cmPolicies::NEW) {
253
0
        return std::string{};
254
0
      }
255
256
0
      if (cmp0128 == cmPolicies::WARN &&
257
0
          makefile->PolicyOptionalWarningEnabled(
258
0
            "CMAKE_POLICY_WARNING_CMP0128")) {
259
0
        makefile->IssuePolicyWarning(
260
0
          cmPolicies::CMP0128, {},
261
0
          "For compatibility with older versions of CMake, unnecessary flags "
262
0
          "for language standard or compiler extensions may be added."_s);
263
0
      }
264
0
    }
265
266
0
    std::string standardStr(*standardProp);
267
0
    if (this->Language == "CUDA"_s && standardStr == "98"_s) {
268
0
      standardStr = "03";
269
0
    }
270
271
0
    auto stdIt =
272
0
      std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr));
273
0
    if (stdIt == cm::cend(stds)) {
274
0
      std::string e = cmStrCat(
275
0
        cmStandardLevelResolver::GetStandardPropertyName(this->Language),
276
0
        " is set to invalid value '", standardStr, '\'');
277
0
      makefile->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
278
0
                                                 target->GetBacktrace());
279
0
      return std::string{};
280
0
    }
281
282
0
    auto defaultStdIt =
283
0
      std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(*defaultStd));
284
0
    if (defaultStdIt == cm::cend(stds)) {
285
0
      std::string e = cmStrCat("CMAKE_", this->Language,
286
0
                               "_STANDARD_DEFAULT is set to invalid value '",
287
0
                               *defaultStd, '\'');
288
0
      makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
289
0
      return std::string{};
290
0
    }
291
292
    // If the standard requested is older than the compiler's default or the
293
    // extension mode doesn't match then we need to use a flag.
294
0
    if ((cmp0128 != cmPolicies::NEW && stdIt <= defaultStdIt) ||
295
0
        (cmp0128 == cmPolicies::NEW &&
296
0
         (stdIt < defaultStdIt || ext != defaultExt))) {
297
0
      auto offset = std::distance(cm::cbegin(stds), stdIt);
298
0
      return cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type,
299
0
                      "_COMPILE_OPTION");
300
0
    }
301
302
    // The compiler's default is at least as new as the requested standard,
303
    // and the requested standard is not required.  Decay to the newest
304
    // standard for which a flag is defined.
305
0
    for (; defaultStdIt < stdIt; --stdIt) {
306
0
      auto offset = std::distance(cm::cbegin(stds), stdIt);
307
0
      std::string option_flag =
308
0
        cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type,
309
0
                 "_COMPILE_OPTION");
310
0
      if (target->Target->GetMakefile()->GetDefinition(option_flag)) {
311
0
        return option_flag;
312
0
      }
313
0
    }
314
315
0
    return std::string{};
316
0
  }
317
318
  std::string GetEffectiveStandard(cmMakefile* makefile,
319
                                   cmGeneratorTarget const* target,
320
                                   std::string const& config) const
321
0
  {
322
0
    auto const& stds = this->Levels;
323
0
    auto const& stdsStrings = this->LevelsAsStrings;
324
325
0
    cmValue defaultStd = makefile->GetDefinition(
326
0
      cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
327
0
    if (!cmNonempty(defaultStd)) {
328
      // this compiler has no notion of language standard levels
329
0
      return std::string{};
330
0
    }
331
332
0
    cmPolicies::PolicyStatus const cmp0128{ makefile->GetPolicyStatus(
333
0
      cmPolicies::CMP0128) };
334
0
    bool const defaultExt{ makefile
335
0
                             ->GetDefinition(cmStrCat("CMAKE_", this->Language,
336
0
                                                      "_EXTENSIONS_DEFAULT"))
337
0
                             .IsOn() };
338
0
    bool ext = true;
339
340
0
    if (cmp0128 == cmPolicies::NEW) {
341
0
      ext = defaultExt;
342
0
    }
343
344
0
    if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) {
345
0
      ext = extPropValue.IsOn();
346
0
    }
347
348
0
    std::string const type{ ext ? "EXTENSION" : "STANDARD" };
349
350
0
    cmValue standardProp = target->GetLanguageStandard(this->Language, config);
351
0
    if (!standardProp) {
352
0
      if (cmp0128 == cmPolicies::NEW) {
353
        // Add extension flag if compiler's default doesn't match.
354
0
        if (ext != defaultExt) {
355
0
          return *defaultStd;
356
0
        }
357
0
      } else {
358
0
        if (ext) {
359
0
          return *defaultStd;
360
0
        }
361
0
      }
362
0
      return std::string{};
363
0
    }
364
365
0
    if (target->GetLanguageStandardRequired(this->Language)) {
366
0
      return *standardProp;
367
0
    }
368
369
    // If the request matches the compiler's defaults we don't need to add
370
    // anything.
371
0
    if (*standardProp == *defaultStd && ext == defaultExt) {
372
0
      if (cmp0128 == cmPolicies::NEW) {
373
0
        return std::string{};
374
0
      }
375
0
    }
376
377
0
    std::string standardStr(*standardProp);
378
0
    if (this->Language == "CUDA"_s && standardStr == "98"_s) {
379
0
      standardStr = "03";
380
0
    }
381
382
0
    auto stdIt =
383
0
      std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr));
384
0
    if (stdIt == cm::cend(stds)) {
385
0
      return std::string{};
386
0
    }
387
388
0
    auto defaultStdIt =
389
0
      std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(*defaultStd));
390
0
    if (defaultStdIt == cm::cend(stds)) {
391
0
      return std::string{};
392
0
    }
393
394
    // If the standard requested is older than the compiler's default or the
395
    // extension mode doesn't match then we need to use a flag.
396
0
    if ((cmp0128 != cmPolicies::NEW && stdIt <= defaultStdIt) ||
397
0
        (cmp0128 == cmPolicies::NEW &&
398
0
         (stdIt < defaultStdIt || ext != defaultExt))) {
399
0
      auto offset = std::distance(cm::cbegin(stds), stdIt);
400
0
      return stdsStrings[offset];
401
0
    }
402
403
    // The compiler's default is at least as new as the requested standard,
404
    // and the requested standard is not required.  Decay to the newest
405
    // standard for which a flag is defined.
406
0
    for (; defaultStdIt < stdIt; --stdIt) {
407
0
      auto offset = std::distance(cm::cbegin(stds), stdIt);
408
0
      std::string option_flag =
409
0
        cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type,
410
0
                 "_COMPILE_OPTION");
411
0
      if (target->Target->GetMakefile()->GetDefinition(option_flag)) {
412
0
        return stdsStrings[offset];
413
0
      }
414
0
    }
415
416
0
    return std::string{};
417
0
  }
418
419
  bool GetNewRequiredStandard(cmMakefile* makefile,
420
                              std::string const& targetName,
421
                              cm::optional<cmStandardLevel> featureLevel,
422
                              cmValue currentLangStandardValue,
423
                              std::string& newRequiredStandard,
424
                              std::string* error) const
425
0
  {
426
0
    if (currentLangStandardValue) {
427
0
      newRequiredStandard = *currentLangStandardValue;
428
0
    } else {
429
0
      newRequiredStandard.clear();
430
0
    }
431
432
0
    cmValue existingStandard = currentLangStandardValue;
433
0
    if (!existingStandard) {
434
0
      cmValue defaultStandard = makefile->GetDefinition(
435
0
        cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
436
0
      if (cmNonempty(defaultStandard)) {
437
0
        existingStandard = defaultStandard;
438
0
      }
439
0
    }
440
441
0
    auto existingLevelIter = cm::cend(this->Levels);
442
0
    if (existingStandard) {
443
0
      existingLevelIter =
444
0
        std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
445
0
                  ParseStd(*existingStandard));
446
0
      if (existingLevelIter == cm::cend(this->Levels)) {
447
0
        std::string const e = cmStrCat(
448
0
          "The ",
449
0
          cmStandardLevelResolver::GetStandardPropertyName(this->Language),
450
0
          " property on target \"", targetName,
451
0
          "\" contained an invalid value: \"", *existingStandard, "\".");
452
0
        if (error) {
453
0
          *error = e;
454
0
        } else {
455
0
          makefile->IssueMessage(MessageType::FATAL_ERROR, e);
456
0
        }
457
0
        return false;
458
0
      }
459
0
    }
460
461
0
    if (featureLevel) {
462
      // Ensure the C++ language level is high enough to support
463
      // the needed C++ features.
464
0
      if (existingLevelIter == cm::cend(this->Levels) ||
465
0
          existingLevelIter < this->Levels.begin() + featureLevel->Index()) {
466
0
        newRequiredStandard = this->LevelsAsStrings[featureLevel->Index()];
467
0
      }
468
0
    }
469
470
0
    return true;
471
0
  }
472
473
  bool HaveStandardAvailable(cmMakefile* makefile,
474
                             cmGeneratorTarget const* target,
475
                             std::string const& config,
476
                             std::string const& feature) const
477
12
  {
478
12
    cmValue defaultStandard = makefile->GetDefinition(
479
12
      cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
480
12
    if (!defaultStandard) {
481
0
      makefile->IssueMessage(
482
0
        MessageType::INTERNAL_ERROR,
483
0
        cmStrCat("CMAKE_", this->Language,
484
0
                 "_STANDARD_DEFAULT is not set.  COMPILE_FEATURES support "
485
0
                 "not fully configured for this compiler."));
486
      // Return true so the caller does not try to lookup the default standard.
487
0
      return true;
488
0
    }
489
    // convert defaultStandard to an integer
490
12
    if (std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
491
12
                  ParseStd(*defaultStandard)) == cm::cend(this->Levels)) {
492
0
      std::string const e = cmStrCat("The CMAKE_", this->Language,
493
0
                                     "_STANDARD_DEFAULT variable contains an "
494
0
                                     "invalid value: \"",
495
0
                                     *defaultStandard, "\".");
496
0
      makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
497
0
      return false;
498
0
    }
499
500
12
    cmValue existingStandard =
501
12
      target->GetLanguageStandard(this->Language, config);
502
12
    if (!existingStandard) {
503
12
      existingStandard = defaultStandard;
504
12
    }
505
506
12
    auto existingLevelIter =
507
12
      std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
508
12
                ParseStd(*existingStandard));
509
12
    if (existingLevelIter == cm::cend(this->Levels)) {
510
0
      std::string const e = cmStrCat(
511
0
        "The ",
512
0
        cmStandardLevelResolver::GetStandardPropertyName(this->Language),
513
0
        " property on target \"", target->GetName(),
514
0
        "\" contained an invalid value: \"", *existingStandard, "\".");
515
0
      makefile->IssueMessage(MessageType::FATAL_ERROR, e);
516
0
      return false;
517
0
    }
518
519
12
    cm::optional<cmStandardLevel> needed =
520
12
      this->CompileFeatureStandardLevel(makefile, feature);
521
522
12
    return !needed ||
523
0
      (this->Levels.begin() + needed->Index()) <= existingLevelIter;
524
12
  }
525
526
  cm::optional<cmStandardLevel> CompileFeatureStandardLevel(
527
    cmMakefile* makefile, std::string const& feature) const
528
12
  {
529
12
    std::string prefix = cmStrCat("CMAKE_", this->Language);
530
12
    cm::optional<cmStandardLevel> maxLevel;
531
96
    for (size_t i = 0; i < this->Levels.size(); ++i) {
532
84
      if (cmValue prop = makefile->GetDefinition(
533
84
            cmStrCat(prefix, this->LevelsAsStrings[i], "_COMPILE_FEATURES"))) {
534
0
        cmList props{ *prop };
535
0
        if (cm::contains(props, feature)) {
536
0
          maxLevel = cmStandardLevel(i);
537
0
        }
538
0
      }
539
84
    }
540
12
    return maxLevel;
541
12
  }
542
543
  cm::optional<cmStandardLevel> LanguageStandardLevel(
544
    std::string const& standardStr) const
545
0
  {
546
0
    cm::optional<cmStandardLevel> langLevel;
547
0
    auto const& stds = this->Levels;
548
0
    auto stdIt =
549
0
      std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr));
550
0
    if (stdIt != cm::cend(stds)) {
551
0
      langLevel = cmStandardLevel(std::distance(cm::cbegin(stds), stdIt));
552
0
    }
553
0
    return langLevel;
554
0
  }
555
556
  bool IsLaterStandard(int lhs, int rhs) const
557
0
  {
558
0
    auto rhsIt =
559
0
      std::find(cm::cbegin(this->Levels), cm::cend(this->Levels), rhs);
560
561
0
    return std::find(rhsIt, cm::cend(this->Levels), lhs) !=
562
0
      cm::cend(this->Levels);
563
0
  }
564
565
  std::string Language;
566
  std::vector<int> Levels;
567
  std::vector<std::string> LevelsAsStrings;
568
};
569
570
std::unordered_map<std::string,
571
                   StandardLevelComputer> const StandardComputerMapping = {
572
  { "C",
573
    StandardLevelComputer{
574
      "C", std::vector<int>{ 90, 99, 11, 17, 23 },
575
      std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
576
  { "CXX",
577
    StandardLevelComputer{
578
      "CXX", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
579
      std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } },
580
  { "CUDA",
581
    StandardLevelComputer{
582
      "CUDA", std::vector<int>{ 03, 11, 14, 17, 20, 23, 26 },
583
      std::vector<std::string>{ "03", "11", "14", "17", "20", "23", "26" } } },
584
  { "OBJC",
585
    StandardLevelComputer{
586
      "OBJC", std::vector<int>{ 90, 99, 11, 17, 23 },
587
      std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
588
  { "OBJCXX",
589
    StandardLevelComputer{
590
      "OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
591
      std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } },
592
  { "HIP",
593
    StandardLevelComputer{
594
      "HIP", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
595
      std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } },
596
  { "SYCL",
597
    StandardLevelComputer{
598
      "SYCL", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
599
      std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } }
600
};
601
}
602
603
std::string cmStandardLevelResolver::GetStandardPropertyName(
604
  std::string const& lang)
605
12
{
606
12
  return lang == "SYCL" ? "SYCL_CXX_STANDARD" : cmStrCat(lang, "_STANDARD");
607
12
}
608
609
std::string cmStandardLevelResolver::GetCompileOptionDef(
610
  cmGeneratorTarget const* target, std::string const& lang,
611
  std::string const& config) const
612
0
{
613
0
  auto const& mapping = StandardComputerMapping.find(lang);
614
0
  if (mapping == cm::cend(StandardComputerMapping)) {
615
0
    return std::string{};
616
0
  }
617
618
0
  return mapping->second.GetCompileOptionDef(this->Makefile, target, config);
619
0
}
620
621
std::string cmStandardLevelResolver::GetEffectiveStandard(
622
  cmGeneratorTarget const* target, std::string const& lang,
623
  std::string const& config) const
624
0
{
625
0
  auto const& mapping = StandardComputerMapping.find(lang);
626
0
  if (mapping == cm::cend(StandardComputerMapping)) {
627
0
    return std::string{};
628
0
  }
629
630
0
  return mapping->second.GetEffectiveStandard(this->Makefile, target, config);
631
0
}
632
633
std::string cmStandardLevelResolver::GetLevelString(
634
  std::string const& lang, cmStandardLevel level) const
635
0
{
636
0
  auto mapping = StandardComputerMapping.find(lang);
637
0
  if (mapping == StandardComputerMapping.end()) {
638
0
    return {};
639
0
  }
640
641
0
  if (mapping->second.LevelsAsStrings.size() <= level.Index()) {
642
0
    return {};
643
0
  }
644
645
0
  return mapping->second.LevelsAsStrings[level.Index()];
646
0
}
647
648
bool cmStandardLevelResolver::AddRequiredTargetFeature(
649
  cmTarget* target, std::string const& feature, std::string* error) const
650
0
{
651
0
  if (cmGeneratorExpression::Find(feature) != std::string::npos) {
652
0
    target->AppendProperty("COMPILE_FEATURES", feature,
653
0
                           this->Makefile->GetBacktrace());
654
0
    return true;
655
0
  }
656
657
0
  std::string lang;
658
0
  if (!this->CheckCompileFeaturesAvailable(target->GetName(), feature, lang,
659
0
                                           error)) {
660
0
    return false;
661
0
  }
662
663
0
  target->AppendProperty("COMPILE_FEATURES", feature,
664
0
                         this->Makefile->GetBacktrace());
665
666
  // FIXME: Add a policy to avoid updating the <LANG>_STANDARD target
667
  // property due to COMPILE_FEATURES.  The language standard selection
668
  // should be done purely at generate time based on whatever the project
669
  // code put in these properties explicitly.  That is mostly true now,
670
  // but for compatibility we need to continue updating the property here.
671
0
  cm::optional<cmStandardLevel> featureLevel;
672
0
  std::string newRequiredStandard;
673
0
  bool succeeded = this->GetNewRequiredStandard(
674
0
    target->GetName(), feature,
675
0
    target->GetProperty(GetStandardPropertyName(lang)), featureLevel,
676
0
    newRequiredStandard, error);
677
0
  if (!newRequiredStandard.empty()) {
678
0
    target->SetProperty(GetStandardPropertyName(lang), newRequiredStandard);
679
0
  }
680
0
  return succeeded;
681
0
}
682
683
bool cmStandardLevelResolver::CheckCompileFeaturesAvailable(
684
  std::string const& targetName, std::string const& feature, std::string& lang,
685
  std::string* error) const
686
0
{
687
0
  if (!this->CompileFeatureKnown(targetName, feature, lang, error)) {
688
0
    return false;
689
0
  }
690
691
0
  if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) {
692
0
    return true;
693
0
  }
694
695
0
  cmValue features = this->CompileFeaturesAvailable(lang, error);
696
0
  if (!features) {
697
0
    return false;
698
0
  }
699
700
0
  cmList availableFeatures{ features };
701
0
  if (!cm::contains(availableFeatures, feature)) {
702
0
    std::string e = cmStrCat("The compiler feature \"", feature,
703
0
                             "\" is not known to ", lang, " compiler\n\"",
704
0
                             this->Makefile->GetSafeDefinition(
705
0
                               cmStrCat("CMAKE_", lang, "_COMPILER_ID")),
706
0
                             "\"\nversion ",
707
0
                             this->Makefile->GetSafeDefinition(
708
0
                               cmStrCat("CMAKE_", lang, "_COMPILER_VERSION")),
709
0
                             '.');
710
0
    if (error) {
711
0
      *error = std::move(e);
712
0
    } else {
713
0
      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
714
0
    }
715
0
    return false;
716
0
  }
717
718
0
  return true;
719
0
}
720
721
bool cmStandardLevelResolver::CompileFeatureKnown(
722
  std::string const& targetName, std::string const& feature, std::string& lang,
723
  std::string* error) const
724
99
{
725
99
  assert(cmGeneratorExpression::Find(feature) == std::string::npos);
726
727
99
  bool isCFeature =
728
99
    std::find_if(cm::cbegin(C_FEATURES) + 1, cm::cend(C_FEATURES),
729
99
                 cmStrCmp(feature)) != cm::cend(C_FEATURES);
730
99
  if (isCFeature) {
731
0
    lang = "C";
732
0
    return true;
733
0
  }
734
99
  bool isCxxFeature =
735
99
    std::find_if(cm::cbegin(CXX_FEATURES) + 1, cm::cend(CXX_FEATURES),
736
99
                 cmStrCmp(feature)) != cm::cend(CXX_FEATURES);
737
99
  bool isSYCLFeature =
738
99
    std::find_if(cm::cbegin(SYCL_FEATURES) + 1, cm::cend(SYCL_FEATURES),
739
99
                 cmStrCmp(feature)) != cm::cend(SYCL_FEATURES);
740
99
  if (isSYCLFeature &&
741
0
      (!isCxxFeature ||
742
0
       TargetUsesSYCLCompileFeatures(this->Makefile, targetName))) {
743
0
    lang = "SYCL";
744
0
    return true;
745
0
  }
746
99
  if (isCxxFeature) {
747
12
    lang = "CXX";
748
12
    return true;
749
12
  }
750
87
  bool isCudaFeature =
751
87
    std::find_if(cm::cbegin(CUDA_FEATURES) + 1, cm::cend(CUDA_FEATURES),
752
87
                 cmStrCmp(feature)) != cm::cend(CUDA_FEATURES);
753
87
  if (isCudaFeature) {
754
0
    lang = "CUDA";
755
0
    return true;
756
0
  }
757
87
  bool isHIPFeature =
758
87
    std::find_if(cm::cbegin(HIP_FEATURES) + 1, cm::cend(HIP_FEATURES),
759
87
                 cmStrCmp(feature)) != cm::cend(HIP_FEATURES);
760
87
  if (isHIPFeature) {
761
0
    lang = "HIP";
762
0
    return true;
763
0
  }
764
87
  std::string e =
765
87
    cmStrCat(error ? "specified" : "Specified", " unknown feature \"", feature,
766
87
             "\" for target \"", targetName, "\".");
767
87
  if (error) {
768
87
    *error = std::move(e);
769
87
  } else {
770
0
    this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
771
0
  }
772
87
  return false;
773
87
}
774
775
cm::optional<cmStandardLevel>
776
cmStandardLevelResolver::CompileFeatureStandardLevel(
777
  std::string const& lang, std::string const& feature) const
778
0
{
779
0
  auto mapping = StandardComputerMapping.find(lang);
780
0
  if (mapping == cm::cend(StandardComputerMapping)) {
781
0
    return cm::nullopt;
782
0
  }
783
0
  return mapping->second.CompileFeatureStandardLevel(this->Makefile, feature);
784
0
}
785
786
cm::optional<cmStandardLevel> cmStandardLevelResolver::LanguageStandardLevel(
787
  std::string const& lang, std::string const& standardStr) const
788
0
{
789
0
  auto mapping = StandardComputerMapping.find(lang);
790
0
  if (mapping == cm::cend(StandardComputerMapping)) {
791
0
    return cm::nullopt;
792
0
  }
793
0
  return mapping->second.LanguageStandardLevel(standardStr);
794
0
}
795
796
cmValue cmStandardLevelResolver::CompileFeaturesAvailable(
797
  std::string const& lang, std::string* error) const
798
1
{
799
1
  if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) {
800
0
    std::string e = cmStrCat(error ? "cannot"_s : "Cannot"_s,
801
0
                             " use features from non-enabled language ", lang);
802
0
    if (error) {
803
0
      *error = std::move(e);
804
0
    } else {
805
0
      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
806
0
    }
807
0
    return nullptr;
808
0
  }
809
810
1
  cmValue featuresKnown = this->Makefile->GetDefinition(
811
1
    cmStrCat("CMAKE_", lang, "_COMPILE_FEATURES"));
812
813
1
  if (!cmNonempty(featuresKnown)) {
814
0
    std::string e = cmStrCat(error ? "no"_s : "No"_s, " known features for ",
815
0
                             lang, " compiler\n\"",
816
0
                             this->Makefile->GetSafeDefinition(
817
0
                               cmStrCat("CMAKE_", lang, "_COMPILER_ID")),
818
0
                             "\"\nversion ",
819
0
                             this->Makefile->GetSafeDefinition(
820
0
                               cmStrCat("CMAKE_", lang, "_COMPILER_VERSION")),
821
0
                             '.');
822
0
    if (error) {
823
0
      *error = std::move(e);
824
0
    } else {
825
0
      this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
826
0
    }
827
0
    return nullptr;
828
0
  }
829
1
  return featuresKnown;
830
1
}
831
832
bool cmStandardLevelResolver::GetNewRequiredStandard(
833
  std::string const& targetName, std::string const& feature,
834
  cmValue currentLangStandardValue,
835
  cm::optional<cmStandardLevel>& featureLevel,
836
  std::string& newRequiredStandard, std::string* error) const
837
0
{
838
0
  std::string lang;
839
0
  if (!this->CheckCompileFeaturesAvailable(targetName, feature, lang, error)) {
840
0
    return false;
841
0
  }
842
843
0
  featureLevel = this->CompileFeatureStandardLevel(lang, feature);
844
845
0
  auto mapping = StandardComputerMapping.find(lang);
846
0
  if (mapping != cm::cend(StandardComputerMapping)) {
847
0
    return mapping->second.GetNewRequiredStandard(
848
0
      this->Makefile, targetName, featureLevel, currentLangStandardValue,
849
0
      newRequiredStandard, error);
850
0
  }
851
0
  return false;
852
0
}
853
854
bool cmStandardLevelResolver::HaveStandardAvailable(
855
  cmGeneratorTarget const* target, std::string const& lang,
856
  std::string const& config, std::string const& feature) const
857
12
{
858
12
  auto mapping = StandardComputerMapping.find(lang);
859
12
  if (mapping != cm::cend(StandardComputerMapping)) {
860
12
    return mapping->second.HaveStandardAvailable(this->Makefile, target,
861
12
                                                 config, feature);
862
12
  }
863
0
  return false;
864
12
}
865
866
bool cmStandardLevelResolver::IsLaterStandard(std::string const& lang,
867
                                              std::string const& lhs,
868
                                              std::string const& rhs) const
869
0
{
870
0
  auto mapping = StandardComputerMapping.find(lang);
871
0
  if (mapping != cm::cend(StandardComputerMapping)) {
872
0
    return mapping->second.IsLaterStandard(std::stoi(lhs), std::stoi(rhs));
873
0
  }
874
0
  return false;
875
0
}