cmStandardLevelResolver.cxx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  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 <sstream>
  7. #include <vector>
  8. #include <cm/iterator>
  9. #include <cmext/algorithm>
  10. #include "cmGeneratorExpression.h"
  11. #include "cmGeneratorTarget.h"
  12. #include "cmGlobalGenerator.h"
  13. #include "cmMakefile.h"
  14. #include "cmMessageType.h"
  15. #include "cmProperty.h"
  16. #include "cmStringAlgorithms.h"
  17. #include "cmTarget.h"
  18. #include "cmake.h"
  19. #define FEATURE_STRING(F) , #F
  20. static const char* const C_FEATURES[] = { nullptr FOR_EACH_C_FEATURE(
  21. FEATURE_STRING) };
  22. static const char* const CXX_FEATURES[] = { nullptr FOR_EACH_CXX_FEATURE(
  23. FEATURE_STRING) };
  24. static const char* const CUDA_FEATURES[] = { nullptr FOR_EACH_CUDA_FEATURE(
  25. FEATURE_STRING) };
  26. #undef FEATURE_STRING
  27. static const char* const C_STANDARDS[] = { "90", "99", "11" };
  28. static const char* const CXX_STANDARDS[] = { "98", "11", "14", "17", "20" };
  29. static const char* const CUDA_STANDARDS[] = { "03", "11", "14", "17", "20" };
  30. bool cmStandardLevelResolver::AddRequiredTargetFeature(
  31. cmTarget* target, const std::string& feature, std::string* error) const
  32. {
  33. if (cmGeneratorExpression::Find(feature) != std::string::npos) {
  34. target->AppendProperty("COMPILE_FEATURES", feature);
  35. return true;
  36. }
  37. std::string lang;
  38. if (!this->CheckCompileFeaturesAvailable(target->GetName(), feature, lang,
  39. error)) {
  40. return false;
  41. }
  42. target->AppendProperty("COMPILE_FEATURES", feature);
  43. // FIXME: Add a policy to avoid updating the <LANG>_STANDARD target
  44. // property due to COMPILE_FEATURES. The language standard selection
  45. // should be done purely at generate time based on whatever the project
  46. // code put in these properties explicitly. That is mostly true now,
  47. // but for compatibility we need to continue updating the property here.
  48. if (lang == "C" || lang == "OBJC") {
  49. return this->AddRequiredTargetCFeature(target, feature, lang, error);
  50. }
  51. if (lang == "CUDA") {
  52. return this->AddRequiredTargetCudaFeature(target, feature, lang, error);
  53. }
  54. return this->AddRequiredTargetCxxFeature(target, feature, lang, error);
  55. }
  56. bool cmStandardLevelResolver::CheckCompileFeaturesAvailable(
  57. const std::string& targetName, const std::string& feature, std::string& lang,
  58. std::string* error) const
  59. {
  60. if (!this->CompileFeatureKnown(targetName, feature, lang, error)) {
  61. return false;
  62. }
  63. const char* features = this->CompileFeaturesAvailable(lang, error);
  64. if (!features) {
  65. return false;
  66. }
  67. std::vector<std::string> availableFeatures = cmExpandedList(features);
  68. if (!cm::contains(availableFeatures, feature)) {
  69. std::ostringstream e;
  70. e << "The compiler feature \"" << feature << "\" is not known to " << lang
  71. << " compiler\n\""
  72. << this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILER_ID")
  73. << "\"\nversion "
  74. << this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILER_VERSION")
  75. << ".";
  76. if (error) {
  77. *error = e.str();
  78. } else {
  79. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
  80. }
  81. return false;
  82. }
  83. return true;
  84. }
  85. bool cmStandardLevelResolver::CompileFeatureKnown(
  86. const std::string& targetName, const std::string& feature, std::string& lang,
  87. std::string* error) const
  88. {
  89. assert(cmGeneratorExpression::Find(feature) == std::string::npos);
  90. bool isCFeature =
  91. std::find_if(cm::cbegin(C_FEATURES) + 1, cm::cend(C_FEATURES),
  92. cmStrCmp(feature)) != cm::cend(C_FEATURES);
  93. if (isCFeature) {
  94. lang = "C";
  95. return true;
  96. }
  97. bool isCxxFeature =
  98. std::find_if(cm::cbegin(CXX_FEATURES) + 1, cm::cend(CXX_FEATURES),
  99. cmStrCmp(feature)) != cm::cend(CXX_FEATURES);
  100. if (isCxxFeature) {
  101. lang = "CXX";
  102. return true;
  103. }
  104. bool isCudaFeature =
  105. std::find_if(cm::cbegin(CUDA_FEATURES) + 1, cm::cend(CUDA_FEATURES),
  106. cmStrCmp(feature)) != cm::cend(CUDA_FEATURES);
  107. if (isCudaFeature) {
  108. lang = "CUDA";
  109. return true;
  110. }
  111. std::ostringstream e;
  112. if (error) {
  113. e << "specified";
  114. } else {
  115. e << "Specified";
  116. }
  117. e << " unknown feature \"" << feature
  118. << "\" for "
  119. "target \""
  120. << targetName << "\".";
  121. if (error) {
  122. *error = e.str();
  123. } else {
  124. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
  125. }
  126. return false;
  127. }
  128. const char* cmStandardLevelResolver::CompileFeaturesAvailable(
  129. const std::string& lang, std::string* error) const
  130. {
  131. if (!this->Makefile->GetGlobalGenerator()->GetLanguageEnabled(lang)) {
  132. std::ostringstream e;
  133. if (error) {
  134. e << "cannot";
  135. } else {
  136. e << "Cannot";
  137. }
  138. e << " use features from non-enabled language " << lang;
  139. if (error) {
  140. *error = e.str();
  141. } else {
  142. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
  143. }
  144. return nullptr;
  145. }
  146. const char* featuresKnown =
  147. this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
  148. if (!featuresKnown || !*featuresKnown) {
  149. std::ostringstream e;
  150. if (error) {
  151. e << "no";
  152. } else {
  153. e << "No";
  154. }
  155. e << " known features for " << lang << " compiler\n\""
  156. << this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID")
  157. << "\"\nversion "
  158. << this->Makefile->GetSafeDefinition("CMAKE_" + lang +
  159. "_COMPILER_VERSION")
  160. << ".";
  161. if (error) {
  162. *error = e.str();
  163. } else {
  164. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
  165. }
  166. return nullptr;
  167. }
  168. return featuresKnown;
  169. }
  170. bool cmStandardLevelResolver::GetNewRequiredStandard(
  171. const std::string& targetName, const std::string& feature,
  172. cmProp currentLangStandardValue, std::string& newRequiredStandard,
  173. std::string* error) const
  174. {
  175. std::string lang;
  176. if (!this->CheckCompileFeaturesAvailable(targetName, feature, lang, error)) {
  177. return false;
  178. }
  179. if (lang == "C" || lang == "OBJC") {
  180. return this->GetNewRequiredCStandard(targetName, feature, lang,
  181. currentLangStandardValue,
  182. newRequiredStandard, error);
  183. }
  184. if (lang == "CUDA") {
  185. return this->GetNewRequiredCudaStandard(targetName, feature, lang,
  186. currentLangStandardValue,
  187. newRequiredStandard, error);
  188. }
  189. return this->GetNewRequiredCxxStandard(targetName, feature, lang,
  190. currentLangStandardValue,
  191. newRequiredStandard, error);
  192. }
  193. bool cmStandardLevelResolver::HaveStandardAvailable(
  194. cmGeneratorTarget const* target, std::string const& lang,
  195. std::string const& config, const std::string& feature) const
  196. {
  197. if (lang == "C" || lang == "OBJC") {
  198. return this->HaveCStandardAvailable(target, lang, config, feature);
  199. }
  200. if (lang == "CUDA") {
  201. return this->HaveCudaStandardAvailable(target, lang, config, feature);
  202. }
  203. return this->HaveCxxStandardAvailable(target, lang, config, feature);
  204. }
  205. bool cmStandardLevelResolver::HaveCStandardAvailable(
  206. cmGeneratorTarget const* target, std::string const& lang,
  207. std::string const& config, const std::string& feature) const
  208. {
  209. cmProp defaultCStandard =
  210. this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
  211. if (!defaultCStandard) {
  212. this->Makefile->IssueMessage(
  213. MessageType::INTERNAL_ERROR,
  214. cmStrCat("CMAKE_", lang,
  215. "_STANDARD_DEFAULT is not set. COMPILE_FEATURES support "
  216. "not fully configured for this compiler."));
  217. // Return true so the caller does not try to lookup the default standard.
  218. return true;
  219. }
  220. if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
  221. cmStrCmp(*defaultCStandard)) == cm::cend(C_STANDARDS)) {
  222. const std::string e = cmStrCat("The CMAKE_", lang,
  223. "_STANDARD_DEFAULT variable contains an "
  224. "invalid value: \"",
  225. *defaultCStandard, "\".");
  226. this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
  227. return false;
  228. }
  229. bool needC90 = false;
  230. bool needC99 = false;
  231. bool needC11 = false;
  232. this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11);
  233. cmProp existingCStandard = target->GetLanguageStandard(lang, config);
  234. if (!existingCStandard) {
  235. existingCStandard = defaultCStandard;
  236. }
  237. if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
  238. cmStrCmp(*existingCStandard)) == cm::cend(C_STANDARDS)) {
  239. const std::string e = cmStrCat(
  240. "The ", lang, "_STANDARD property on target \"", target->GetName(),
  241. "\" contained an invalid value: \"", *existingCStandard, "\".");
  242. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
  243. return false;
  244. }
  245. const char* const* existingCIt = existingCStandard
  246. ? std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
  247. cmStrCmp(*existingCStandard))
  248. : cm::cend(C_STANDARDS);
  249. if (needC11 && existingCStandard &&
  250. existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
  251. cm::cend(C_STANDARDS), cmStrCmp("11"))) {
  252. return false;
  253. }
  254. if (needC99 && existingCStandard &&
  255. existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
  256. cm::cend(C_STANDARDS), cmStrCmp("99"))) {
  257. return false;
  258. }
  259. if (needC90 && existingCStandard &&
  260. existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
  261. cm::cend(C_STANDARDS), cmStrCmp("90"))) {
  262. return false;
  263. }
  264. return true;
  265. }
  266. bool cmStandardLevelResolver::IsLaterStandard(std::string const& lang,
  267. std::string const& lhs,
  268. std::string const& rhs) const
  269. {
  270. if (lang == "C" || lang == "OBJC") {
  271. const char* const* rhsIt = std::find_if(
  272. cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS), cmStrCmp(rhs));
  273. return std::find_if(rhsIt, cm::cend(C_STANDARDS), cmStrCmp(lhs)) !=
  274. cm::cend(C_STANDARDS);
  275. }
  276. if (lang == "CUDA") {
  277. const char* const* rhsIt = std::find_if(
  278. cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS), cmStrCmp(rhs));
  279. return std::find_if(rhsIt, cm::cend(CUDA_STANDARDS), cmStrCmp(lhs)) !=
  280. cm::cend(CUDA_STANDARDS);
  281. }
  282. const char* const* rhsIt = std::find_if(
  283. cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS), cmStrCmp(rhs));
  284. return std::find_if(rhsIt, cm::cend(CXX_STANDARDS), cmStrCmp(lhs)) !=
  285. cm::cend(CXX_STANDARDS);
  286. }
  287. bool cmStandardLevelResolver::HaveCxxStandardAvailable(
  288. cmGeneratorTarget const* target, std::string const& lang,
  289. std::string const& config, const std::string& feature) const
  290. {
  291. cmProp defaultCxxStandard =
  292. this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
  293. if (!defaultCxxStandard) {
  294. this->Makefile->IssueMessage(
  295. MessageType::INTERNAL_ERROR,
  296. cmStrCat("CMAKE_", lang,
  297. "_STANDARD_DEFAULT is not set. COMPILE_FEATURES support "
  298. "not fully configured for this compiler."));
  299. // Return true so the caller does not try to lookup the default standard.
  300. return true;
  301. }
  302. if (std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
  303. cmStrCmp(*defaultCxxStandard)) == cm::cend(CXX_STANDARDS)) {
  304. const std::string e =
  305. cmStrCat("The CMAKE_", lang, "_STANDARD_DEFAULT variable contains an ",
  306. "invalid value: \"", *defaultCxxStandard, "\".");
  307. this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
  308. return false;
  309. }
  310. bool needCxx98 = false;
  311. bool needCxx11 = false;
  312. bool needCxx14 = false;
  313. bool needCxx17 = false;
  314. bool needCxx20 = false;
  315. this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14,
  316. needCxx17, needCxx20);
  317. cmProp existingCxxStandard = target->GetLanguageStandard(lang, config);
  318. if (!existingCxxStandard) {
  319. existingCxxStandard = defaultCxxStandard;
  320. }
  321. const char* const* existingCxxLevel =
  322. std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
  323. cmStrCmp(*existingCxxStandard));
  324. if (existingCxxLevel == cm::cend(CXX_STANDARDS)) {
  325. const std::string e = cmStrCat(
  326. "The ", lang, "_STANDARD property on target \"", target->GetName(),
  327. "\" contained an invalid value: \"", *existingCxxStandard, "\".");
  328. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
  329. return false;
  330. }
  331. /* clang-format off */
  332. const char* const* needCxxLevel =
  333. needCxx20 ? &CXX_STANDARDS[4]
  334. : needCxx17 ? &CXX_STANDARDS[3]
  335. : needCxx14 ? &CXX_STANDARDS[2]
  336. : needCxx11 ? &CXX_STANDARDS[1]
  337. : needCxx98 ? &CXX_STANDARDS[0]
  338. : nullptr;
  339. /* clang-format on */
  340. return !needCxxLevel || needCxxLevel <= existingCxxLevel;
  341. }
  342. void cmStandardLevelResolver::CheckNeededCxxLanguage(
  343. const std::string& feature, std::string const& lang, bool& needCxx98,
  344. bool& needCxx11, bool& needCxx14, bool& needCxx17, bool& needCxx20) const
  345. {
  346. if (const char* propCxx98 = this->Makefile->GetDefinition(
  347. cmStrCat("CMAKE_", lang, "98_COMPILE_FEATURES"))) {
  348. std::vector<std::string> props = cmExpandedList(propCxx98);
  349. needCxx98 = cm::contains(props, feature);
  350. }
  351. if (const char* propCxx11 = this->Makefile->GetDefinition(
  352. cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
  353. std::vector<std::string> props = cmExpandedList(propCxx11);
  354. needCxx11 = cm::contains(props, feature);
  355. }
  356. if (const char* propCxx14 = this->Makefile->GetDefinition(
  357. cmStrCat("CMAKE_", lang, "14_COMPILE_FEATURES"))) {
  358. std::vector<std::string> props = cmExpandedList(propCxx14);
  359. needCxx14 = cm::contains(props, feature);
  360. }
  361. if (const char* propCxx17 = this->Makefile->GetDefinition(
  362. cmStrCat("CMAKE_", lang, "17_COMPILE_FEATURES"))) {
  363. std::vector<std::string> props = cmExpandedList(propCxx17);
  364. needCxx17 = cm::contains(props, feature);
  365. }
  366. if (const char* propCxx20 = this->Makefile->GetDefinition(
  367. cmStrCat("CMAKE_", lang, "20_COMPILE_FEATURES"))) {
  368. std::vector<std::string> props = cmExpandedList(propCxx20);
  369. needCxx20 = cm::contains(props, feature);
  370. }
  371. }
  372. bool cmStandardLevelResolver::AddRequiredTargetCxxFeature(
  373. cmTarget* target, const std::string& feature, std::string const& lang,
  374. std::string* error) const
  375. {
  376. std::string newRequiredStandard;
  377. if (this->GetNewRequiredCxxStandard(
  378. target->GetName(), feature, lang,
  379. target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
  380. error)) {
  381. if (!newRequiredStandard.empty()) {
  382. target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
  383. }
  384. return true;
  385. }
  386. return false;
  387. }
  388. bool cmStandardLevelResolver::GetNewRequiredCxxStandard(
  389. const std::string& targetName, const std::string& feature,
  390. std::string const& lang, cmProp currentLangStandardValue,
  391. std::string& newRequiredStandard, std::string* error) const
  392. {
  393. newRequiredStandard.clear();
  394. bool needCxx98 = false;
  395. bool needCxx11 = false;
  396. bool needCxx14 = false;
  397. bool needCxx17 = false;
  398. bool needCxx20 = false;
  399. this->CheckNeededCxxLanguage(feature, lang, needCxx98, needCxx11, needCxx14,
  400. needCxx17, needCxx20);
  401. cmProp existingCxxStandard = currentLangStandardValue;
  402. if (existingCxxStandard == nullptr) {
  403. cmProp defaultCxxStandard =
  404. this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
  405. if (defaultCxxStandard && !defaultCxxStandard->empty()) {
  406. existingCxxStandard = defaultCxxStandard;
  407. }
  408. }
  409. const char* const* existingCxxLevel = nullptr;
  410. if (existingCxxStandard) {
  411. existingCxxLevel =
  412. std::find_if(cm::cbegin(CXX_STANDARDS), cm::cend(CXX_STANDARDS),
  413. cmStrCmp(*existingCxxStandard));
  414. if (existingCxxLevel == cm::cend(CXX_STANDARDS)) {
  415. const std::string e = cmStrCat(
  416. "The ", lang, "_STANDARD property on target \"", targetName,
  417. "\" contained an invalid value: \"", *existingCxxStandard, "\".");
  418. if (error) {
  419. *error = e;
  420. } else {
  421. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
  422. }
  423. return false;
  424. }
  425. }
  426. /* clang-format off */
  427. const char* const* needCxxLevel =
  428. needCxx20 ? &CXX_STANDARDS[4]
  429. : needCxx17 ? &CXX_STANDARDS[3]
  430. : needCxx14 ? &CXX_STANDARDS[2]
  431. : needCxx11 ? &CXX_STANDARDS[1]
  432. : needCxx98 ? &CXX_STANDARDS[0]
  433. : nullptr;
  434. /* clang-format on */
  435. if (needCxxLevel) {
  436. // Ensure the C++ language level is high enough to support
  437. // the needed C++ features.
  438. if (!existingCxxLevel || existingCxxLevel < needCxxLevel) {
  439. newRequiredStandard = *needCxxLevel;
  440. }
  441. }
  442. return true;
  443. }
  444. bool cmStandardLevelResolver::HaveCudaStandardAvailable(
  445. cmGeneratorTarget const* target, std::string const& lang,
  446. std::string const& config, const std::string& feature) const
  447. {
  448. cmProp defaultCudaStandard =
  449. this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
  450. if (!defaultCudaStandard) {
  451. this->Makefile->IssueMessage(
  452. MessageType::INTERNAL_ERROR,
  453. cmStrCat("CMAKE_", lang,
  454. "_STANDARD_DEFAULT is not set. COMPILE_FEATURES support "
  455. "not fully configured for this compiler."));
  456. // Return true so the caller does not try to lookup the default standard.
  457. return true;
  458. }
  459. if (std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS),
  460. cmStrCmp(*defaultCudaStandard)) ==
  461. cm::cend(CUDA_STANDARDS)) {
  462. const std::string e =
  463. cmStrCat("The CMAKE_", lang, "_STANDARD_DEFAULT variable contains an ",
  464. "invalid value: \"", *defaultCudaStandard, "\".");
  465. this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
  466. return false;
  467. }
  468. bool needCuda03 = false;
  469. bool needCuda11 = false;
  470. bool needCuda14 = false;
  471. bool needCuda17 = false;
  472. bool needCuda20 = false;
  473. this->CheckNeededCudaLanguage(feature, lang, needCuda03, needCuda11,
  474. needCuda14, needCuda17, needCuda20);
  475. cmProp existingCudaStandard = target->GetLanguageStandard(lang, config);
  476. if (!existingCudaStandard) {
  477. existingCudaStandard = defaultCudaStandard;
  478. }
  479. const char* const* existingCudaLevel =
  480. std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS),
  481. cmStrCmp(*existingCudaStandard));
  482. if (existingCudaLevel == cm::cend(CUDA_STANDARDS)) {
  483. const std::string e = cmStrCat(
  484. "The ", lang, "_STANDARD property on target \"", target->GetName(),
  485. "\" contained an invalid value: \"", *existingCudaStandard, "\".");
  486. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
  487. return false;
  488. }
  489. /* clang-format off */
  490. const char* const* needCudaLevel =
  491. needCuda20 ? &CUDA_STANDARDS[4]
  492. : needCuda17 ? &CUDA_STANDARDS[3]
  493. : needCuda14 ? &CUDA_STANDARDS[2]
  494. : needCuda11 ? &CUDA_STANDARDS[1]
  495. : needCuda03 ? &CUDA_STANDARDS[0]
  496. : nullptr;
  497. /* clang-format on */
  498. return !needCudaLevel || needCudaLevel <= existingCudaLevel;
  499. }
  500. void cmStandardLevelResolver::CheckNeededCudaLanguage(
  501. const std::string& feature, std::string const& lang, bool& needCuda03,
  502. bool& needCuda11, bool& needCuda14, bool& needCuda17, bool& needCuda20) const
  503. {
  504. if (const char* propCuda03 = this->Makefile->GetDefinition(
  505. cmStrCat("CMAKE_", lang, "03_COMPILE_FEATURES"))) {
  506. std::vector<std::string> props = cmExpandedList(propCuda03);
  507. needCuda03 = cm::contains(props, feature);
  508. }
  509. if (const char* propCuda11 = this->Makefile->GetDefinition(
  510. cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
  511. std::vector<std::string> props = cmExpandedList(propCuda11);
  512. needCuda11 = cm::contains(props, feature);
  513. }
  514. if (const char* propCuda14 = this->Makefile->GetDefinition(
  515. cmStrCat("CMAKE_", lang, "14_COMPILE_FEATURES"))) {
  516. std::vector<std::string> props = cmExpandedList(propCuda14);
  517. needCuda14 = cm::contains(props, feature);
  518. }
  519. if (const char* propCuda17 = this->Makefile->GetDefinition(
  520. cmStrCat("CMAKE_", lang, "17_COMPILE_FEATURES"))) {
  521. std::vector<std::string> props = cmExpandedList(propCuda17);
  522. needCuda17 = cm::contains(props, feature);
  523. }
  524. if (const char* propCuda20 = this->Makefile->GetDefinition(
  525. cmStrCat("CMAKE_", lang, "20_COMPILE_FEATURES"))) {
  526. std::vector<std::string> props = cmExpandedList(propCuda20);
  527. needCuda20 = cm::contains(props, feature);
  528. }
  529. }
  530. bool cmStandardLevelResolver::AddRequiredTargetCudaFeature(
  531. cmTarget* target, const std::string& feature, std::string const& lang,
  532. std::string* error) const
  533. {
  534. std::string newRequiredStandard;
  535. if (this->GetNewRequiredCudaStandard(
  536. target->GetName(), feature, lang,
  537. target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
  538. error)) {
  539. if (!newRequiredStandard.empty()) {
  540. target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
  541. }
  542. return true;
  543. }
  544. return false;
  545. }
  546. bool cmStandardLevelResolver::GetNewRequiredCudaStandard(
  547. const std::string& targetName, const std::string& feature,
  548. std::string const& lang, cmProp currentLangStandardValue,
  549. std::string& newRequiredStandard, std::string* error) const
  550. {
  551. newRequiredStandard.clear();
  552. bool needCuda03 = false;
  553. bool needCuda11 = false;
  554. bool needCuda14 = false;
  555. bool needCuda17 = false;
  556. bool needCuda20 = false;
  557. this->CheckNeededCudaLanguage(feature, lang, needCuda03, needCuda11,
  558. needCuda14, needCuda17, needCuda20);
  559. cmProp existingCudaStandard = currentLangStandardValue;
  560. if (existingCudaStandard == nullptr) {
  561. cmProp defaultCudaStandard =
  562. this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
  563. if (defaultCudaStandard && !defaultCudaStandard->empty()) {
  564. existingCudaStandard = defaultCudaStandard;
  565. }
  566. }
  567. const char* const* existingCudaLevel = nullptr;
  568. if (existingCudaStandard) {
  569. existingCudaLevel =
  570. std::find_if(cm::cbegin(CUDA_STANDARDS), cm::cend(CUDA_STANDARDS),
  571. cmStrCmp(*existingCudaStandard));
  572. if (existingCudaLevel == cm::cend(CUDA_STANDARDS)) {
  573. const std::string e = cmStrCat(
  574. "The ", lang, "_STANDARD property on target \"", targetName,
  575. "\" contained an invalid value: \"", *existingCudaStandard, "\".");
  576. if (error) {
  577. *error = e;
  578. } else {
  579. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
  580. }
  581. return false;
  582. }
  583. }
  584. /* clang-format off */
  585. const char* const* needCudaLevel =
  586. needCuda20 ? &CUDA_STANDARDS[4]
  587. : needCuda17 ? &CUDA_STANDARDS[3]
  588. : needCuda14 ? &CUDA_STANDARDS[2]
  589. : needCuda11 ? &CUDA_STANDARDS[1]
  590. : needCuda03 ? &CUDA_STANDARDS[0]
  591. : nullptr;
  592. /* clang-format on */
  593. if (needCudaLevel) {
  594. // Ensure the CUDA language level is high enough to support
  595. // the needed CUDA features.
  596. if (!existingCudaLevel || existingCudaLevel < needCudaLevel) {
  597. newRequiredStandard = *needCudaLevel;
  598. }
  599. }
  600. return true;
  601. }
  602. void cmStandardLevelResolver::CheckNeededCLanguage(const std::string& feature,
  603. std::string const& lang,
  604. bool& needC90,
  605. bool& needC99,
  606. bool& needC11) const
  607. {
  608. if (const char* propC90 = this->Makefile->GetDefinition(
  609. cmStrCat("CMAKE_", lang, "90_COMPILE_FEATURES"))) {
  610. std::vector<std::string> props = cmExpandedList(propC90);
  611. needC90 = cm::contains(props, feature);
  612. }
  613. if (const char* propC99 = this->Makefile->GetDefinition(
  614. cmStrCat("CMAKE_", lang, "99_COMPILE_FEATURES"))) {
  615. std::vector<std::string> props = cmExpandedList(propC99);
  616. needC99 = cm::contains(props, feature);
  617. }
  618. if (const char* propC11 = this->Makefile->GetDefinition(
  619. cmStrCat("CMAKE_", lang, "11_COMPILE_FEATURES"))) {
  620. std::vector<std::string> props = cmExpandedList(propC11);
  621. needC11 = cm::contains(props, feature);
  622. }
  623. }
  624. bool cmStandardLevelResolver::AddRequiredTargetCFeature(
  625. cmTarget* target, const std::string& feature, std::string const& lang,
  626. std::string* error) const
  627. {
  628. std::string newRequiredStandard;
  629. if (this->GetNewRequiredCStandard(
  630. target->GetName(), feature, lang,
  631. target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
  632. error)) {
  633. if (!newRequiredStandard.empty()) {
  634. target->SetLanguageStandardProperty(lang, newRequiredStandard, feature);
  635. }
  636. return true;
  637. }
  638. return false;
  639. }
  640. bool cmStandardLevelResolver::GetNewRequiredCStandard(
  641. const std::string& targetName, const std::string& feature,
  642. std::string const& lang, cmProp currentLangStandardValue,
  643. std::string& newRequiredStandard, std::string* error) const
  644. {
  645. newRequiredStandard.clear();
  646. bool needC90 = false;
  647. bool needC99 = false;
  648. bool needC11 = false;
  649. this->CheckNeededCLanguage(feature, lang, needC90, needC99, needC11);
  650. cmProp existingCStandard = currentLangStandardValue;
  651. if (existingCStandard == nullptr) {
  652. cmProp defaultCStandard =
  653. this->Makefile->GetDef(cmStrCat("CMAKE_", lang, "_STANDARD_DEFAULT"));
  654. if (defaultCStandard && !defaultCStandard->empty()) {
  655. existingCStandard = defaultCStandard;
  656. }
  657. }
  658. if (existingCStandard) {
  659. if (std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
  660. cmStrCmp(*existingCStandard)) == cm::cend(C_STANDARDS)) {
  661. const std::string e = cmStrCat(
  662. "The ", lang, "_STANDARD property on target \"", targetName,
  663. "\" contained an invalid value: \"", *existingCStandard, "\".");
  664. if (error) {
  665. *error = e;
  666. } else {
  667. this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e);
  668. }
  669. return false;
  670. }
  671. }
  672. const char* const* existingCIt = existingCStandard
  673. ? std::find_if(cm::cbegin(C_STANDARDS), cm::cend(C_STANDARDS),
  674. cmStrCmp(*existingCStandard))
  675. : cm::cend(C_STANDARDS);
  676. bool setC90 = needC90 && !existingCStandard;
  677. bool setC99 = needC99 && !existingCStandard;
  678. bool setC11 = needC11 && !existingCStandard;
  679. if (needC11 && existingCStandard &&
  680. existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
  681. cm::cend(C_STANDARDS), cmStrCmp("11"))) {
  682. setC11 = true;
  683. } else if (needC99 && existingCStandard &&
  684. existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
  685. cm::cend(C_STANDARDS),
  686. cmStrCmp("99"))) {
  687. setC99 = true;
  688. } else if (needC90 && existingCStandard &&
  689. existingCIt < std::find_if(cm::cbegin(C_STANDARDS),
  690. cm::cend(C_STANDARDS),
  691. cmStrCmp("90"))) {
  692. setC90 = true;
  693. }
  694. if (setC11) {
  695. newRequiredStandard = "11";
  696. } else if (setC99) {
  697. newRequiredStandard = "99";
  698. } else if (setC90) {
  699. newRequiredStandard = "90";
  700. }
  701. return true;
  702. }