Coverage Report

Created: 2026-04-29 07:01

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