cmGeneratorExpression.cxx 14 KB

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