cmStandardLevelResolver.cxx 26 KB

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