cmStandardLevelResolver.cxx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmStandardLevelResolver.h"
  4. #include <algorithm>
  5. #include <cassert>
  6. #include <cstddef>
  7. #include <sstream>
  8. #include <stdexcept>
  9. #include <unordered_map>
  10. #include <utility>
  11. #include <vector>
  12. #include <cm/iterator>
  13. #include <cmext/algorithm>
  14. #include "cmGeneratorExpression.h"
  15. #include "cmGeneratorTarget.h"
  16. #include "cmGlobalGenerator.h"
  17. #include "cmMakefile.h"
  18. #include "cmMessageType.h"
  19. #include "cmPolicies.h"
  20. #include "cmStringAlgorithms.h"
  21. #include "cmTarget.h"
  22. #include "cmValue.h"
  23. #include "cmake.h"
  24. namespace {
  25. #define FEATURE_STRING(F) , #F
  26. const char* const C_FEATURES[] = { nullptr FOR_EACH_C_FEATURE(
  27. FEATURE_STRING) };
  28. const char* const CXX_FEATURES[] = { nullptr FOR_EACH_CXX_FEATURE(
  29. FEATURE_STRING) };
  30. const char* const CUDA_FEATURES[] = { nullptr FOR_EACH_CUDA_FEATURE(
  31. FEATURE_STRING) };
  32. const char* const HIP_FEATURES[] = { nullptr FOR_EACH_HIP_FEATURE(
  33. FEATURE_STRING) };
  34. #undef FEATURE_STRING
  35. struct StandardNeeded
  36. {
  37. int index;
  38. int value;
  39. };
  40. int ParseStd(std::string const& level)
  41. {
  42. try {
  43. return std::stoi(level);
  44. } catch (std::invalid_argument&) {
  45. // Fall through to use an invalid value.
  46. }
  47. return -1;
  48. }
  49. struct StandardLevelComputer
  50. {
  51. explicit StandardLevelComputer(std::string lang, std::vector<int> levels,
  52. std::vector<std::string> levelsStr)
  53. : Language(std::move(lang))
  54. , Levels(std::move(levels))
  55. , LevelsAsStrings(std::move(levelsStr))
  56. {
  57. assert(this->Levels.size() == this->LevelsAsStrings.size());
  58. }
  59. std::string GetCompileOptionDef(cmMakefile* makefile,
  60. cmGeneratorTarget const* target,
  61. std::string const& config) const
  62. {
  63. const auto& stds = this->Levels;
  64. const auto& stdsStrings = this->LevelsAsStrings;
  65. cmValue defaultStd = makefile->GetDefinition(
  66. cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
  67. if (!cmNonempty(defaultStd)) {
  68. // this compiler has no notion of language standard levels
  69. return std::string{};
  70. }
  71. cmPolicies::PolicyStatus const cmp0128{ makefile->GetPolicyStatus(
  72. cmPolicies::CMP0128) };
  73. bool const defaultExt{ cmIsOn(*makefile->GetDefinition(
  74. cmStrCat("CMAKE_", this->Language, "_EXTENSIONS_DEFAULT"))) };
  75. bool ext = true;
  76. if (cmp0128 == cmPolicies::NEW) {
  77. ext = defaultExt;
  78. }
  79. if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) {
  80. ext = cmIsOn(*extPropValue);
  81. }
  82. std::string const type{ ext ? "EXTENSION" : "STANDARD" };
  83. cmValue standardProp = target->GetLanguageStandard(this->Language, config);
  84. if (!standardProp) {
  85. if (cmp0128 == cmPolicies::NEW) {
  86. // Add extension flag if compiler's default doesn't match.
  87. if (ext != defaultExt) {
  88. return cmStrCat("CMAKE_", this->Language, *defaultStd, "_", type,
  89. "_COMPILE_OPTION");
  90. }
  91. } else {
  92. if (cmp0128 == cmPolicies::WARN &&
  93. makefile->PolicyOptionalWarningEnabled(
  94. "CMAKE_POLICY_WARNING_CMP0128") &&
  95. ext != defaultExt) {
  96. const char* state{};
  97. if (ext) {
  98. if (!makefile->GetDefinition(cmStrCat(
  99. "CMAKE_", this->Language, "_EXTENSION_COMPILE_OPTION"))) {
  100. state = "enabled";
  101. }
  102. } else {
  103. state = "disabled";
  104. }
  105. if (state) {
  106. makefile->IssueMessage(
  107. MessageType::AUTHOR_WARNING,
  108. cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0128),
  109. "\nFor compatibility with older versions of CMake, "
  110. "compiler extensions won't be ",
  111. state, "."));
  112. }
  113. }
  114. if (ext) {
  115. return cmStrCat("CMAKE_", this->Language,
  116. "_EXTENSION_COMPILE_OPTION");
  117. }
  118. }
  119. return std::string{};
  120. }
  121. if (target->GetLanguageStandardRequired(this->Language)) {
  122. std::string option_flag = cmStrCat(
  123. "CMAKE_", this->Language, *standardProp, "_", type, "_COMPILE_OPTION");
  124. cmValue opt = target->Target->GetMakefile()->GetDefinition(option_flag);
  125. if (!opt) {
  126. std::ostringstream e;
  127. e << "Target \"" << target->GetName()
  128. << "\" requires the language "
  129. "dialect \""
  130. << this->Language << *standardProp << "\" "
  131. << (ext ? "(with compiler extensions)" : "")
  132. << ". But the current compiler \""
  133. << makefile->GetSafeDefinition("CMAKE_" + this->Language +
  134. "_COMPILER_ID")
  135. << "\" does not support this, or "
  136. "CMake does not know the flags to enable it.";
  137. makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
  138. }
  139. return option_flag;
  140. }
  141. // If the request matches the compiler's defaults we don't need to add
  142. // anything.
  143. if (*standardProp == *defaultStd && ext == defaultExt) {
  144. if (cmp0128 == cmPolicies::NEW) {
  145. return std::string{};
  146. }
  147. if (cmp0128 == cmPolicies::WARN &&
  148. makefile->PolicyOptionalWarningEnabled(
  149. "CMAKE_POLICY_WARNING_CMP0128")) {
  150. makefile->IssueMessage(
  151. MessageType::AUTHOR_WARNING,
  152. cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0128),
  153. "\nFor compatibility with older versions of CMake, "
  154. "unnecessary flags for language standard or compiler "
  155. "extensions may be added."));
  156. }
  157. }
  158. std::string standardStr(*standardProp);
  159. if (this->Language == "CUDA" && standardStr == "98") {
  160. standardStr = "03";
  161. }
  162. auto stdIt =
  163. std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr));
  164. if (stdIt == cm::cend(stds)) {
  165. std::string e =
  166. cmStrCat(this->Language, "_STANDARD is set to invalid value '",
  167. standardStr, "'");
  168. makefile->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
  169. target->GetBacktrace());
  170. return std::string{};
  171. }
  172. auto defaultStdIt =
  173. std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(*defaultStd));
  174. if (defaultStdIt == cm::cend(stds)) {
  175. std::string e = cmStrCat("CMAKE_", this->Language,
  176. "_STANDARD_DEFAULT is set to invalid value '",
  177. *defaultStd, "'");
  178. makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
  179. return std::string{};
  180. }
  181. // If the standard requested is older than the compiler's default or the
  182. // extension mode doesn't match then we need to use a flag.
  183. if (stdIt < defaultStdIt ||
  184. (cmp0128 == cmPolicies::NEW && ext != defaultExt)) {
  185. auto offset = std::distance(cm::cbegin(stds), stdIt);
  186. return cmStrCat("CMAKE_", this->Language, stdsStrings[offset], "_", type,
  187. "_COMPILE_OPTION");
  188. }
  189. // The compiler's default is at least as new as the requested standard,
  190. // and the requested standard is not required. Decay to the newest
  191. // standard for which a flag is defined.
  192. for (; defaultStdIt < stdIt; --stdIt) {
  193. auto offset = std::distance(cm::cbegin(stds), stdIt);
  194. std::string option_flag =
  195. cmStrCat("CMAKE_", this->Language, stdsStrings[offset], "_", type,
  196. "_COMPILE_OPTION");
  197. if (target->Target->GetMakefile()->GetDefinition(option_flag)) {
  198. return option_flag;
  199. }
  200. }
  201. return std::string{};
  202. }
  203. bool GetNewRequiredStandard(cmMakefile* makefile,
  204. std::string const& targetName,
  205. const std::string& feature,
  206. cmValue currentLangStandardValue,
  207. std::string& newRequiredStandard,
  208. std::string* error) const
  209. {
  210. if (currentLangStandardValue) {
  211. newRequiredStandard = *currentLangStandardValue;
  212. } else {
  213. newRequiredStandard.clear();
  214. }
  215. auto needed = this->HighestStandardNeeded(makefile, feature);
  216. cmValue existingStandard = currentLangStandardValue;
  217. if (!existingStandard) {
  218. cmValue defaultStandard = makefile->GetDefinition(
  219. cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
  220. if (cmNonempty(defaultStandard)) {
  221. existingStandard = defaultStandard;
  222. }
  223. }
  224. auto existingLevelIter = cm::cend(this->Levels);
  225. if (existingStandard) {
  226. existingLevelIter =
  227. std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
  228. ParseStd(*existingStandard));
  229. if (existingLevelIter == cm::cend(this->Levels)) {
  230. const std::string e =
  231. cmStrCat("The ", this->Language, "_STANDARD property on target \"",
  232. targetName, "\" contained an invalid value: \"",
  233. *existingStandard, "\".");
  234. if (error) {
  235. *error = e;
  236. } else {
  237. makefile->IssueMessage(MessageType::FATAL_ERROR, e);
  238. }
  239. return false;
  240. }
  241. }
  242. if (needed.index != -1) {
  243. // Ensure the C++ language level is high enough to support
  244. // the needed C++ features.
  245. if (existingLevelIter == cm::cend(this->Levels) ||
  246. existingLevelIter < this->Levels.begin() + needed.index) {
  247. newRequiredStandard = this->LevelsAsStrings[needed.index];
  248. }
  249. }
  250. return true;
  251. }
  252. bool HaveStandardAvailable(cmMakefile* makefile,
  253. cmGeneratorTarget const* target,
  254. std::string const& config,
  255. std::string const& feature) const
  256. {
  257. cmValue defaultStandard = makefile->GetDefinition(
  258. cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
  259. if (!defaultStandard) {
  260. makefile->IssueMessage(
  261. MessageType::INTERNAL_ERROR,
  262. cmStrCat("CMAKE_", this->Language,
  263. "_STANDARD_DEFAULT is not set. COMPILE_FEATURES support "
  264. "not fully configured for this compiler."));
  265. // Return true so the caller does not try to lookup the default standard.
  266. return true;
  267. }
  268. // convert defaultStandard to an integer
  269. if (std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
  270. ParseStd(*defaultStandard)) == cm::cend(this->Levels)) {
  271. const std::string e = cmStrCat("The CMAKE_", this->Language,
  272. "_STANDARD_DEFAULT variable contains an "
  273. "invalid value: \"",
  274. *defaultStandard, "\".");
  275. makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
  276. return false;
  277. }
  278. cmValue existingStandard =
  279. target->GetLanguageStandard(this->Language, config);
  280. if (!existingStandard) {
  281. existingStandard = defaultStandard;
  282. }
  283. auto existingLevelIter =
  284. std::find(cm::cbegin(this->Levels), cm::cend(this->Levels),
  285. ParseStd(*existingStandard));
  286. if (existingLevelIter == cm::cend(this->Levels)) {
  287. const std::string e =
  288. cmStrCat("The ", this->Language, "_STANDARD property on target \"",
  289. target->GetName(), "\" contained an invalid value: \"",
  290. *existingStandard, "\".");
  291. makefile->IssueMessage(MessageType::FATAL_ERROR, e);
  292. return false;
  293. }
  294. auto needed = this->HighestStandardNeeded(makefile, feature);
  295. return (needed.index == -1) ||
  296. (this->Levels.begin() + needed.index) <= existingLevelIter;
  297. }
  298. StandardNeeded HighestStandardNeeded(cmMakefile* makefile,
  299. std::string const& feature) const
  300. {
  301. std::string prefix = cmStrCat("CMAKE_", this->Language);
  302. StandardNeeded maxLevel = { -1, -1 };
  303. for (size_t i = 0; i < this->Levels.size(); ++i) {
  304. if (cmValue prop = makefile->GetDefinition(
  305. cmStrCat(prefix, this->LevelsAsStrings[i], "_COMPILE_FEATURES"))) {
  306. std::vector<std::string> props = cmExpandedList(*prop);
  307. if (cm::contains(props, feature)) {
  308. maxLevel = { static_cast<int>(i), this->Levels[i] };
  309. }
  310. }
  311. }
  312. return maxLevel;
  313. }
  314. bool IsLaterStandard(int lhs, int rhs) const
  315. {
  316. auto rhsIt =
  317. std::find(cm::cbegin(this->Levels), cm::cend(this->Levels), rhs);
  318. return std::find(rhsIt, cm::cend(this->Levels), lhs) !=
  319. cm::cend(this->Levels);
  320. }
  321. std::string Language;
  322. std::vector<int> Levels;
  323. std::vector<std::string> LevelsAsStrings;
  324. };
  325. std::unordered_map<std::string, StandardLevelComputer>
  326. StandardComputerMapping = {
  327. { "C",
  328. StandardLevelComputer{
  329. "C", std::vector<int>{ 90, 99, 11, 17, 23 },
  330. std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
  331. { "CXX",
  332. StandardLevelComputer{
  333. "CXX", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
  334. std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } },
  335. { "CUDA",
  336. StandardLevelComputer{
  337. "CUDA", std::vector<int>{ 03, 11, 14, 17, 20, 23 },
  338. std::vector<std::string>{ "03", "11", "14", "17", "20", "23" } } },
  339. { "OBJC",
  340. StandardLevelComputer{
  341. "OBJC", std::vector<int>{ 90, 99, 11, 17, 23 },
  342. std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
  343. { "OBJCXX",
  344. StandardLevelComputer{
  345. "OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
  346. std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } },
  347. { "HIP",
  348. StandardLevelComputer{
  349. "HIP", std::vector<int>{ 98, 11, 14, 17, 20, 23 },
  350. std::vector<std::string>{ "98", "11", "14", "17", "20", "23" } } }
  351. };
  352. }
  353. std::string cmStandardLevelResolver::GetCompileOptionDef(
  354. cmGeneratorTarget const* target, std::string const& lang,
  355. std::string const& config) const
  356. {
  357. const auto& mapping = StandardComputerMapping.find(lang);
  358. if (mapping == cm::cend(StandardComputerMapping)) {
  359. return std::string{};
  360. }
  361. return mapping->second.GetCompileOptionDef(this->Makefile, target, config);
  362. }
  363. bool cmStandardLevelResolver::AddRequiredTargetFeature(
  364. cmTarget* target, const std::string& feature, std::string* error) const
  365. {
  366. if (cmGeneratorExpression::Find(feature) != std::string::npos) {
  367. target->AppendProperty("COMPILE_FEATURES", feature);
  368. return true;
  369. }
  370. std::string lang;
  371. if (!this->CheckCompileFeaturesAvailable(target->GetName(), feature, lang,
  372. error)) {
  373. return false;
  374. }
  375. target->AppendProperty("COMPILE_FEATURES", feature);
  376. // FIXME: Add a policy to avoid updating the <LANG>_STANDARD target
  377. // property due to COMPILE_FEATURES. The language standard selection
  378. // should be done purely at generate time based on whatever the project
  379. // code put in these properties explicitly. That is mostly true now,
  380. // but for compatibility we need to continue updating the property here.
  381. std::string newRequiredStandard;
  382. bool newRequired = this->GetNewRequiredStandard(
  383. target->GetName(), feature,
  384. target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
  385. error);
  386. if (!newRequiredStandard.empty()) {
  387. target->SetProperty(cmStrCat(lang, "_STANDARD"), newRequiredStandard);
  388. }
  389. return newRequired;
  390. }
  391. bool cmStandardLevelResolver::CheckCompileFeaturesAvailable(
  392. const std::string& targetName, const std::string& feature, std::string& lang,
  393. std::string* error) const
  394. {
  395. if (!this->CompileFeatureKnown(targetName, feature, lang, error)) {
  396. return false;
  397. }
  398. if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) {
  399. return true;
  400. }
  401. cmValue features = this->CompileFeaturesAvailable(lang, error);
  402. if (!features) {
  403. return false;
  404. }
  405. std::vector<std::string> availableFeatures = cmExpandedList(features);
  406. if (!cm::contains(availableFeatures, feature)) {
  407. std::ostringstream e;
  408. e << "The compiler feature \"" << feature << "\" is not known to " << lang
  409. << " compiler\n\""
  410. << this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID")
  411. << "\"\nversion "
  412. << this->Makefile->GetSafeDefinition("CMAKE_" + lang +
  413. "_COMPILER_VERSION")
  414. << ".";
  415. if (error) {
  416. *error = e.str();
  417. } else {
  418. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
  419. }
  420. return false;
  421. }
  422. return true;
  423. }
  424. bool cmStandardLevelResolver::CompileFeatureKnown(
  425. const std::string& targetName, const std::string& feature, std::string& lang,
  426. std::string* error) const
  427. {
  428. assert(cmGeneratorExpression::Find(feature) == std::string::npos);
  429. bool isCFeature =
  430. std::find_if(cm::cbegin(C_FEATURES) + 1, cm::cend(C_FEATURES),
  431. cmStrCmp(feature)) != cm::cend(C_FEATURES);
  432. if (isCFeature) {
  433. lang = "C";
  434. return true;
  435. }
  436. bool isCxxFeature =
  437. std::find_if(cm::cbegin(CXX_FEATURES) + 1, cm::cend(CXX_FEATURES),
  438. cmStrCmp(feature)) != cm::cend(CXX_FEATURES);
  439. if (isCxxFeature) {
  440. lang = "CXX";
  441. return true;
  442. }
  443. bool isCudaFeature =
  444. std::find_if(cm::cbegin(CUDA_FEATURES) + 1, cm::cend(CUDA_FEATURES),
  445. cmStrCmp(feature)) != cm::cend(CUDA_FEATURES);
  446. if (isCudaFeature) {
  447. lang = "CUDA";
  448. return true;
  449. }
  450. bool isHIPFeature =
  451. std::find_if(cm::cbegin(HIP_FEATURES) + 1, cm::cend(HIP_FEATURES),
  452. cmStrCmp(feature)) != cm::cend(HIP_FEATURES);
  453. if (isHIPFeature) {
  454. lang = "HIP";
  455. return true;
  456. }
  457. std::ostringstream e;
  458. if (error) {
  459. e << "specified";
  460. } else {
  461. e << "Specified";
  462. }
  463. e << " unknown feature \"" << feature
  464. << "\" for "
  465. "target \""
  466. << targetName << "\".";
  467. if (error) {
  468. *error = e.str();
  469. } else {
  470. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
  471. }
  472. return false;
  473. }
  474. cmValue cmStandardLevelResolver::CompileFeaturesAvailable(
  475. const std::string& lang, std::string* error) const
  476. {
  477. if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) {
  478. std::ostringstream e;
  479. if (error) {
  480. e << "cannot";
  481. } else {
  482. e << "Cannot";
  483. }
  484. e << " use features from non-enabled language " << lang;
  485. if (error) {
  486. *error = e.str();
  487. } else {
  488. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
  489. }
  490. return nullptr;
  491. }
  492. cmValue featuresKnown =
  493. this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
  494. if (!cmNonempty(featuresKnown)) {
  495. std::ostringstream e;
  496. if (error) {
  497. e << "no";
  498. } else {
  499. e << "No";
  500. }
  501. e << " known features for " << lang << " compiler\n\""
  502. << this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID")
  503. << "\"\nversion "
  504. << this->Makefile->GetSafeDefinition("CMAKE_" + lang +
  505. "_COMPILER_VERSION")
  506. << ".";
  507. if (error) {
  508. *error = e.str();
  509. } else {
  510. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
  511. }
  512. return nullptr;
  513. }
  514. return featuresKnown;
  515. }
  516. bool cmStandardLevelResolver::GetNewRequiredStandard(
  517. const std::string& targetName, const std::string& feature,
  518. cmValue currentLangStandardValue, std::string& newRequiredStandard,
  519. std::string* error) const
  520. {
  521. std::string lang;
  522. if (!this->CheckCompileFeaturesAvailable(targetName, feature, lang, error)) {
  523. return false;
  524. }
  525. auto mapping = StandardComputerMapping.find(lang);
  526. if (mapping != cm::cend(StandardComputerMapping)) {
  527. return mapping->second.GetNewRequiredStandard(
  528. this->Makefile, targetName, feature, currentLangStandardValue,
  529. newRequiredStandard, error);
  530. }
  531. return false;
  532. }
  533. bool cmStandardLevelResolver::HaveStandardAvailable(
  534. cmGeneratorTarget const* target, std::string const& lang,
  535. std::string const& config, const std::string& feature) const
  536. {
  537. auto mapping = StandardComputerMapping.find(lang);
  538. if (mapping != cm::cend(StandardComputerMapping)) {
  539. return mapping->second.HaveStandardAvailable(this->Makefile, target,
  540. config, feature);
  541. }
  542. return false;
  543. }
  544. bool cmStandardLevelResolver::IsLaterStandard(std::string const& lang,
  545. std::string const& lhs,
  546. std::string const& rhs) const
  547. {
  548. auto mapping = StandardComputerMapping.find(lang);
  549. if (mapping != cm::cend(StandardComputerMapping)) {
  550. return mapping->second.IsLaterStandard(std::stoi(lhs), std::stoi(rhs));
  551. }
  552. return false;
  553. }