cmGeneratorExpression.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst or https://cmake.org/licensing for details. */
  3. #include "cmGeneratorExpression.h"
  4. #include <algorithm>
  5. #include <cassert>
  6. #include <memory>
  7. #include <stack>
  8. #include <utility>
  9. #include <cm/string_view>
  10. #include "cmsys/RegularExpression.hxx"
  11. #include "cmGeneratorExpressionContext.h"
  12. #include "cmGeneratorExpressionDAGChecker.h"
  13. #include "cmGeneratorExpressionEvaluator.h"
  14. #include "cmGeneratorExpressionLexer.h"
  15. #include "cmGeneratorExpressionParser.h"
  16. #include "cmList.h"
  17. #include "cmLocalGenerator.h"
  18. #include "cmStringAlgorithms.h"
  19. #include "cmSystemTools.h"
  20. #include "cmake.h"
  21. cmGeneratorExpression::cmGeneratorExpression(cmake& cmakeInstance,
  22. cmListFileBacktrace backtrace)
  23. : CMakeInstance(cmakeInstance)
  24. , Backtrace(std::move(backtrace))
  25. {
  26. }
  27. cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression() = default;
  28. cmGeneratorExpression::~cmGeneratorExpression() = default;
  29. std::unique_ptr<cmCompiledGeneratorExpression> cmGeneratorExpression::Parse(
  30. std::string input) const
  31. {
  32. return std::unique_ptr<cmCompiledGeneratorExpression>(
  33. new cmCompiledGeneratorExpression(this->CMakeInstance, this->Backtrace,
  34. std::move(input)));
  35. }
  36. std::string cmGeneratorExpression::Evaluate(
  37. std::string input, cmLocalGenerator const* lg, std::string const& config,
  38. cmGeneratorTarget const* headTarget,
  39. cmGeneratorExpressionDAGChecker* dagChecker,
  40. cmGeneratorTarget const* currentTarget, std::string const& language)
  41. {
  42. if (Find(input) != std::string::npos) {
  43. #ifndef CMAKE_BOOTSTRAP
  44. auto profilingRAII = lg->GetCMakeInstance()->CreateProfilingEntry(
  45. "genex_compile_eval", input);
  46. #endif
  47. cmCompiledGeneratorExpression cge(*lg->GetCMakeInstance(),
  48. cmListFileBacktrace(), std::move(input));
  49. return cge.Evaluate(lg, config, headTarget, dagChecker, currentTarget,
  50. language);
  51. }
  52. return input;
  53. }
  54. std::string const& cmCompiledGeneratorExpression::Evaluate(
  55. cmLocalGenerator const* lg, std::string const& config,
  56. cmGeneratorTarget const* headTarget,
  57. cmGeneratorExpressionDAGChecker* dagChecker,
  58. cmGeneratorTarget const* currentTarget, std::string const& language) const
  59. {
  60. cmGeneratorExpressionContext context(
  61. lg, config, this->Quiet, headTarget,
  62. currentTarget ? currentTarget : headTarget, this->EvaluateForBuildsystem,
  63. this->Backtrace, language);
  64. if (!this->NeedsEvaluation) {
  65. return this->Input;
  66. }
  67. this->Output.clear();
  68. for (auto const& it : this->Evaluators) {
  69. this->Output += it->Evaluate(&context, dagChecker);
  70. this->SeenTargetProperties.insert(context.SeenTargetProperties.cbegin(),
  71. context.SeenTargetProperties.cend());
  72. if (context.HadError) {
  73. this->Output.clear();
  74. break;
  75. }
  76. }
  77. this->MaxLanguageStandard = context.MaxLanguageStandard;
  78. if (!context.HadError) {
  79. this->HadContextSensitiveCondition = context.HadContextSensitiveCondition;
  80. this->HadHeadSensitiveCondition = context.HadHeadSensitiveCondition;
  81. this->HadLinkLanguageSensitiveCondition =
  82. context.HadLinkLanguageSensitiveCondition;
  83. this->SourceSensitiveTargets = context.SourceSensitiveTargets;
  84. }
  85. this->DependTargets = context.DependTargets;
  86. this->AllTargetsSeen = context.AllTargets;
  87. return this->Output;
  88. }
  89. cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
  90. cmake& cmakeInstance, cmListFileBacktrace backtrace, std::string input)
  91. : Backtrace(std::move(backtrace))
  92. , Input(std::move(input))
  93. {
  94. #ifndef CMAKE_BOOTSTRAP
  95. auto profilingRAII =
  96. cmakeInstance.CreateProfilingEntry("genex_compile", this->Input);
  97. #endif
  98. cmGeneratorExpressionLexer l;
  99. std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(this->Input);
  100. this->NeedsEvaluation = l.GetSawGeneratorExpression();
  101. if (this->NeedsEvaluation) {
  102. cmGeneratorExpressionParser p(tokens);
  103. p.Parse(this->Evaluators);
  104. }
  105. }
  106. std::string cmGeneratorExpression::StripEmptyListElements(
  107. std::string const& input)
  108. {
  109. if (input.find(';') == std::string::npos) {
  110. return input;
  111. }
  112. std::string result;
  113. result.reserve(input.size());
  114. char const* c = input.c_str();
  115. char const* last = c;
  116. bool skipSemiColons = true;
  117. for (; *c; ++c) {
  118. if (*c == ';') {
  119. if (skipSemiColons) {
  120. result.append(last, c - last);
  121. last = c + 1;
  122. }
  123. skipSemiColons = true;
  124. } else {
  125. skipSemiColons = false;
  126. }
  127. }
  128. result.append(last);
  129. if (!result.empty() && *(result.end() - 1) == ';') {
  130. result.resize(result.size() - 1);
  131. }
  132. return result;
  133. }
  134. static std::string extractAllGeneratorExpressions(
  135. std::string const& input,
  136. std::map<std::string, std::vector<std::string>>* collected)
  137. {
  138. std::string result;
  139. std::string::size_type pos = 0;
  140. std::string::size_type lastPos = pos;
  141. // stack of { Generator Expression Name, Start Position of Value }
  142. std::stack<std::pair<std::string, std::string::size_type>> genexps;
  143. while ((pos = input.find("$<", lastPos)) != std::string::npos) {
  144. result += input.substr(lastPos, pos - lastPos);
  145. pos += 2;
  146. char const* c = input.c_str() + pos;
  147. char const* cName = c;
  148. char const* const cStart = c;
  149. for (; *c; ++c) {
  150. if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
  151. ++c;
  152. cName = c + 1;
  153. continue;
  154. }
  155. if (c[0] == ':' && cName) {
  156. genexps.push({ input.substr(pos + (cName - cStart), c - cName),
  157. pos + (c + 1 - cStart) });
  158. cName = nullptr;
  159. } else if (c[0] == '>') {
  160. if (!cName && !genexps.empty()) {
  161. if (collected) {
  162. (*collected)[genexps.top().first].push_back(input.substr(
  163. genexps.top().second, pos + c - cStart - genexps.top().second));
  164. }
  165. genexps.pop();
  166. }
  167. if (genexps.empty()) {
  168. break;
  169. }
  170. }
  171. }
  172. std::string::size_type const traversed = (c - cStart) + 1;
  173. if (!*c) {
  174. result += "$<" + input.substr(pos, traversed);
  175. }
  176. pos += traversed;
  177. lastPos = pos;
  178. }
  179. if (genexps.empty()) {
  180. result += input.substr(lastPos);
  181. }
  182. return cmGeneratorExpression::StripEmptyListElements(result);
  183. }
  184. static std::string stripAllGeneratorExpressions(std::string const& input)
  185. {
  186. return extractAllGeneratorExpressions(input, nullptr);
  187. }
  188. static void prefixItems(std::string const& content, std::string& result,
  189. cm::string_view prefix)
  190. {
  191. std::vector<std::string> entries;
  192. cmGeneratorExpression::Split(content, entries);
  193. char const* sep = "";
  194. for (std::string const& e : entries) {
  195. result += sep;
  196. sep = ";";
  197. if (!cmSystemTools::FileIsFullPath(e) &&
  198. cmGeneratorExpression::Find(e) != 0) {
  199. result += prefix;
  200. }
  201. result += e;
  202. }
  203. }
  204. static std::string stripExportInterface(
  205. std::string const& input, cmGeneratorExpression::PreprocessContext context,
  206. cm::string_view importPrefix)
  207. {
  208. std::string result;
  209. int nestingLevel = 0;
  210. std::string::size_type pos = 0;
  211. std::string::size_type lastPos = pos;
  212. while (true) {
  213. std::string::size_type bPos = input.find("$<BUILD_INTERFACE:", lastPos);
  214. std::string::size_type iPos = input.find("$<INSTALL_INTERFACE:", lastPos);
  215. std::string::size_type lPos =
  216. input.find("$<BUILD_LOCAL_INTERFACE:", lastPos);
  217. pos = std::min({ bPos, iPos, lPos });
  218. if (pos == std::string::npos) {
  219. break;
  220. }
  221. result += input.substr(lastPos, pos - lastPos);
  222. enum class FoundGenex
  223. {
  224. BuildInterface,
  225. InstallInterface,
  226. BuildLocalInterface,
  227. } foundGenex = FoundGenex::BuildInterface;
  228. if (pos == bPos) {
  229. foundGenex = FoundGenex::BuildInterface;
  230. pos += cmStrLen("$<BUILD_INTERFACE:");
  231. } else if (pos == iPos) {
  232. foundGenex = FoundGenex::InstallInterface;
  233. pos += cmStrLen("$<INSTALL_INTERFACE:");
  234. } else if (pos == lPos) {
  235. foundGenex = FoundGenex::BuildLocalInterface;
  236. pos += cmStrLen("$<BUILD_LOCAL_INTERFACE:");
  237. } else {
  238. assert(false && "Invalid position found");
  239. }
  240. nestingLevel = 1;
  241. char const* c = input.c_str() + pos;
  242. char const* const cStart = c;
  243. for (; *c; ++c) {
  244. if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
  245. ++nestingLevel;
  246. ++c;
  247. continue;
  248. }
  249. if (c[0] == '>') {
  250. --nestingLevel;
  251. if (nestingLevel != 0) {
  252. continue;
  253. }
  254. if (context == cmGeneratorExpression::BuildInterface &&
  255. foundGenex == FoundGenex::BuildInterface) {
  256. result += input.substr(pos, c - cStart);
  257. } else if (context == cmGeneratorExpression::InstallInterface &&
  258. foundGenex == FoundGenex::InstallInterface) {
  259. std::string const content = input.substr(pos, c - cStart);
  260. if (!importPrefix.empty()) {
  261. prefixItems(content, result, importPrefix);
  262. } else {
  263. result += content;
  264. }
  265. }
  266. break;
  267. }
  268. }
  269. std::string::size_type const traversed = (c - cStart) + 1;
  270. if (!*c) {
  271. auto remaining = input.substr(pos, traversed);
  272. switch (foundGenex) {
  273. case FoundGenex::BuildInterface:
  274. result = cmStrCat(result, "$<BUILD_INTERFACE:", remaining);
  275. break;
  276. case FoundGenex::InstallInterface:
  277. result = cmStrCat(result, "$<INSTALL_INTERFACE:", remaining);
  278. break;
  279. case FoundGenex::BuildLocalInterface:
  280. result = cmStrCat(result, "$<BUILD_LOCAL_INTERFACE:", remaining);
  281. break;
  282. }
  283. }
  284. pos += traversed;
  285. lastPos = pos;
  286. }
  287. if (nestingLevel == 0) {
  288. result += input.substr(lastPos);
  289. }
  290. return cmGeneratorExpression::StripEmptyListElements(result);
  291. }
  292. void cmGeneratorExpression::Split(std::string const& input,
  293. std::vector<std::string>& output)
  294. {
  295. std::string::size_type pos = 0;
  296. std::string::size_type lastPos = pos;
  297. while ((pos = input.find("$<", lastPos)) != std::string::npos) {
  298. std::string part = input.substr(lastPos, pos - lastPos);
  299. std::string preGenex;
  300. if (!part.empty()) {
  301. std::string::size_type startPos = input.rfind(';', pos);
  302. if (startPos == std::string::npos) {
  303. preGenex = part;
  304. part.clear();
  305. } else if (startPos != pos - 1 && startPos >= lastPos) {
  306. part = input.substr(lastPos, startPos - lastPos);
  307. preGenex = input.substr(startPos + 1, pos - startPos - 1);
  308. }
  309. if (!part.empty()) {
  310. cmExpandList(part, output);
  311. }
  312. }
  313. pos += 2;
  314. int nestingLevel = 1;
  315. char const* c = input.c_str() + pos;
  316. char const* const cStart = c;
  317. for (; *c; ++c) {
  318. if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
  319. ++nestingLevel;
  320. ++c;
  321. continue;
  322. }
  323. if (c[0] == '>') {
  324. --nestingLevel;
  325. if (nestingLevel == 0) {
  326. break;
  327. }
  328. }
  329. }
  330. for (; *c; ++c) {
  331. // Capture the part after the genex and before the next ';'
  332. if (c[0] == ';') {
  333. --c;
  334. break;
  335. }
  336. }
  337. std::string::size_type const traversed = (c - cStart) + 1;
  338. output.push_back(preGenex + "$<" + input.substr(pos, traversed));
  339. pos += traversed;
  340. lastPos = pos;
  341. }
  342. if (lastPos < input.size()) {
  343. cmExpandList(input.substr(lastPos), output);
  344. }
  345. }
  346. std::string cmGeneratorExpression::Preprocess(std::string const& input,
  347. PreprocessContext context,
  348. cm::string_view importPrefix)
  349. {
  350. if (context == StripAllGeneratorExpressions) {
  351. return stripAllGeneratorExpressions(input);
  352. }
  353. if (context == BuildInterface || context == InstallInterface) {
  354. return stripExportInterface(input, context, importPrefix);
  355. }
  356. assert(false &&
  357. "cmGeneratorExpression::Preprocess called with invalid args");
  358. return std::string();
  359. }
  360. std::string cmGeneratorExpression::Collect(
  361. std::string const& input,
  362. std::map<std::string, std::vector<std::string>>& collected)
  363. {
  364. return extractAllGeneratorExpressions(input, &collected);
  365. }
  366. cm::string_view::size_type cmGeneratorExpression::Find(cm::string_view input)
  367. {
  368. cm::string_view::size_type const openpos = input.find("$<");
  369. if (openpos != cm::string_view::npos &&
  370. input.find('>', openpos) != cm::string_view::npos) {
  371. return openpos;
  372. }
  373. return cm::string_view::npos;
  374. }
  375. bool cmGeneratorExpression::IsValidTargetName(std::string const& input)
  376. {
  377. // The ':' is supported to allow use with IMPORTED targets. At least
  378. // Qt 4 and 5 IMPORTED targets use ':' as the namespace delimiter.
  379. static cmsys::RegularExpression targetNameValidator("^[A-Za-z0-9_.:+-]+$");
  380. return targetNameValidator.find(input);
  381. }
  382. void cmGeneratorExpression::ReplaceInstallPrefix(
  383. std::string& input, std::string const& replacement)
  384. {
  385. std::string::size_type pos = 0;
  386. std::string::size_type lastPos = pos;
  387. while ((pos = input.find("$<INSTALL_PREFIX>", lastPos)) !=
  388. std::string::npos) {
  389. std::string::size_type endPos = pos + cmStrLen("$<INSTALL_PREFIX>");
  390. input.replace(pos, endPos - pos, replacement);
  391. lastPos = endPos;
  392. }
  393. }
  394. void cmCompiledGeneratorExpression::GetMaxLanguageStandard(
  395. cmGeneratorTarget const* tgt, std::map<std::string, std::string>& mapping)
  396. {
  397. auto it = this->MaxLanguageStandard.find(tgt);
  398. if (it != this->MaxLanguageStandard.end()) {
  399. mapping = it->second;
  400. }
  401. }
  402. std::string const& cmGeneratorExpressionInterpreter::Evaluate(
  403. std::string expression, std::string const& property)
  404. {
  405. this->CompiledGeneratorExpression =
  406. this->GeneratorExpression.Parse(std::move(expression));
  407. // Specify COMPILE_OPTIONS to DAGchecker, same semantic as COMPILE_FLAGS
  408. cmGeneratorExpressionDAGChecker dagChecker{
  409. this->HeadTarget,
  410. property == "COMPILE_FLAGS" ? "COMPILE_OPTIONS" : property,
  411. nullptr,
  412. nullptr,
  413. this->LocalGenerator,
  414. this->Config,
  415. };
  416. return this->CompiledGeneratorExpression->Evaluate(
  417. this->LocalGenerator, this->Config, this->HeadTarget, &dagChecker, nullptr,
  418. this->Language);
  419. }