Coverage Report

Created: 2026-06-15 07:03

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