Coverage Report

Created: 2026-02-09 06:05

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