cmGeneratorExpression.cxx 14 KB

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