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 "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. std::stack<char const*> starts; // indices of "$<"
  142. std::stack<char const*> colons; // indices of ":"
  143. while ((pos = input.find("$<", lastPos)) != std::string::npos) {
  144. result += input.substr(lastPos, pos - lastPos);
  145. starts.push(input.c_str() + pos);
  146. pos += 2;
  147. char const* c = input.c_str() + pos;
  148. char const* const cStart = c;
  149. for (; *c; ++c) {
  150. if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
  151. starts.push(c);
  152. ++c;
  153. continue;
  154. }
  155. if (c[0] == ':') {
  156. if (colons.size() < starts.size()) {
  157. colons.push(c);
  158. }
  159. } else if (c[0] == '>') {
  160. if (collected && !starts.empty() && !colons.empty()) {
  161. (*collected)[std::string(starts.top() + 2, colons.top())].push_back(
  162. std::string(colons.top() + 1, c));
  163. }
  164. if (!starts.empty()) {
  165. starts.pop();
  166. }
  167. if (!colons.empty()) {
  168. colons.pop();
  169. }
  170. if (starts.empty()) {
  171. break;
  172. }
  173. }
  174. }
  175. std::string::size_type const traversed = (c - cStart) + 1;
  176. if (!*c) {
  177. result += "$<" + input.substr(pos, traversed);
  178. }
  179. pos += traversed;
  180. lastPos = pos;
  181. }
  182. if (starts.empty()) {
  183. result += input.substr(lastPos);
  184. }
  185. return cmGeneratorExpression::StripEmptyListElements(result);
  186. }
  187. static std::string stripAllGeneratorExpressions(std::string const& input)
  188. {
  189. return extractAllGeneratorExpressions(input, nullptr);
  190. }
  191. static void prefixItems(std::string const& content, std::string& result,
  192. cm::string_view const& prefix)
  193. {
  194. std::vector<std::string> entries;
  195. cmGeneratorExpression::Split(content, entries);
  196. char const* sep = "";
  197. for (std::string const& e : entries) {
  198. result += sep;
  199. sep = ";";
  200. if (!cmSystemTools::FileIsFullPath(e) &&
  201. cmGeneratorExpression::Find(e) != 0) {
  202. result += prefix;
  203. }
  204. result += e;
  205. }
  206. }
  207. static std::string stripExportInterface(
  208. std::string const& input, cmGeneratorExpression::PreprocessContext context,
  209. cm::string_view importPrefix)
  210. {
  211. std::string result;
  212. int nestingLevel = 0;
  213. std::string::size_type pos = 0;
  214. std::string::size_type lastPos = pos;
  215. while (true) {
  216. std::string::size_type bPos = input.find("$<BUILD_INTERFACE:", lastPos);
  217. std::string::size_type iPos = input.find("$<INSTALL_INTERFACE:", lastPos);
  218. std::string::size_type lPos =
  219. input.find("$<BUILD_LOCAL_INTERFACE:", lastPos);
  220. pos = std::min({ bPos, iPos, lPos });
  221. if (pos == std::string::npos) {
  222. break;
  223. }
  224. result += input.substr(lastPos, pos - lastPos);
  225. enum class FoundGenex
  226. {
  227. BuildInterface,
  228. InstallInterface,
  229. BuildLocalInterface,
  230. } foundGenex = FoundGenex::BuildInterface;
  231. if (pos == bPos) {
  232. foundGenex = FoundGenex::BuildInterface;
  233. pos += cmStrLen("$<BUILD_INTERFACE:");
  234. } else if (pos == iPos) {
  235. foundGenex = FoundGenex::InstallInterface;
  236. pos += cmStrLen("$<INSTALL_INTERFACE:");
  237. } else if (pos == lPos) {
  238. foundGenex = FoundGenex::BuildLocalInterface;
  239. pos += cmStrLen("$<BUILD_LOCAL_INTERFACE:");
  240. } else {
  241. assert(false && "Invalid position found");
  242. }
  243. nestingLevel = 1;
  244. char const* c = input.c_str() + pos;
  245. char const* const cStart = c;
  246. for (; *c; ++c) {
  247. if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
  248. ++nestingLevel;
  249. ++c;
  250. continue;
  251. }
  252. if (c[0] == '>') {
  253. --nestingLevel;
  254. if (nestingLevel != 0) {
  255. continue;
  256. }
  257. if (context == cmGeneratorExpression::BuildInterface &&
  258. foundGenex == FoundGenex::BuildInterface) {
  259. result += input.substr(pos, c - cStart);
  260. } else if (context == cmGeneratorExpression::InstallInterface &&
  261. foundGenex == FoundGenex::InstallInterface) {
  262. std::string const content = input.substr(pos, c - cStart);
  263. if (!importPrefix.empty()) {
  264. prefixItems(content, result, importPrefix);
  265. } else {
  266. result += content;
  267. }
  268. }
  269. break;
  270. }
  271. }
  272. std::string::size_type const traversed = (c - cStart) + 1;
  273. if (!*c) {
  274. auto remaining = input.substr(pos, traversed);
  275. switch (foundGenex) {
  276. case FoundGenex::BuildInterface:
  277. result = cmStrCat(result, "$<BUILD_INTERFACE:", remaining);
  278. break;
  279. case FoundGenex::InstallInterface:
  280. result = cmStrCat(result, "$<INSTALL_INTERFACE:", remaining);
  281. break;
  282. case FoundGenex::BuildLocalInterface:
  283. result = cmStrCat(result, "$<BUILD_LOCAL_INTERFACE:", remaining);
  284. break;
  285. }
  286. }
  287. pos += traversed;
  288. lastPos = pos;
  289. }
  290. if (nestingLevel == 0) {
  291. result += input.substr(lastPos);
  292. }
  293. return cmGeneratorExpression::StripEmptyListElements(result);
  294. }
  295. void cmGeneratorExpression::Split(std::string const& input,
  296. std::vector<std::string>& output)
  297. {
  298. std::string::size_type pos = 0;
  299. std::string::size_type lastPos = pos;
  300. while ((pos = input.find("$<", lastPos)) != std::string::npos) {
  301. std::string part = input.substr(lastPos, pos - lastPos);
  302. std::string preGenex;
  303. if (!part.empty()) {
  304. std::string::size_type startPos = input.rfind(';', pos);
  305. if (startPos == std::string::npos) {
  306. preGenex = part;
  307. part.clear();
  308. } else if (startPos != pos - 1 && startPos >= lastPos) {
  309. part = input.substr(lastPos, startPos - lastPos);
  310. preGenex = input.substr(startPos + 1, pos - startPos - 1);
  311. }
  312. if (!part.empty()) {
  313. cmExpandList(part, output);
  314. }
  315. }
  316. pos += 2;
  317. int nestingLevel = 1;
  318. char const* c = input.c_str() + pos;
  319. char const* const cStart = c;
  320. for (; *c; ++c) {
  321. if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
  322. ++nestingLevel;
  323. ++c;
  324. continue;
  325. }
  326. if (c[0] == '>') {
  327. --nestingLevel;
  328. if (nestingLevel == 0) {
  329. break;
  330. }
  331. }
  332. }
  333. for (; *c; ++c) {
  334. // Capture the part after the genex and before the next ';'
  335. if (c[0] == ';') {
  336. --c;
  337. break;
  338. }
  339. }
  340. std::string::size_type const traversed = (c - cStart) + 1;
  341. output.push_back(preGenex + "$<" + input.substr(pos, traversed));
  342. pos += traversed;
  343. lastPos = pos;
  344. }
  345. if (lastPos < input.size()) {
  346. cmExpandList(input.substr(lastPos), output);
  347. }
  348. }
  349. std::string cmGeneratorExpression::Preprocess(std::string const& input,
  350. PreprocessContext context,
  351. cm::string_view importPrefix)
  352. {
  353. if (context == StripAllGeneratorExpressions) {
  354. return stripAllGeneratorExpressions(input);
  355. }
  356. if (context == BuildInterface || context == InstallInterface) {
  357. return stripExportInterface(input, context, importPrefix);
  358. }
  359. assert(false &&
  360. "cmGeneratorExpression::Preprocess called with invalid args");
  361. return std::string();
  362. }
  363. std::string cmGeneratorExpression::Collect(
  364. std::string const& input,
  365. std::map<std::string, std::vector<std::string>>& collected)
  366. {
  367. return extractAllGeneratorExpressions(input, &collected);
  368. }
  369. cm::string_view::size_type cmGeneratorExpression::Find(
  370. cm::string_view const& 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. }