1
0

cmGeneratorExpressionEvaluator.cxx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2012 Stephen Kelly <[email protected]>
  4. Distributed under the OSI-approved BSD License (the "License");
  5. see accompanying file Copyright.txt for details.
  6. This software is distributed WITHOUT ANY WARRANTY; without even the
  7. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  8. See the License for more information.
  9. ============================================================================*/
  10. #include "cmMakefile.h"
  11. #include "cmGeneratorExpressionEvaluator.h"
  12. #include "cmGeneratorExpressionParser.h"
  13. //----------------------------------------------------------------------------
  14. static void reportError(cmGeneratorExpressionContext *context,
  15. const std::string &expr, const std::string &result)
  16. {
  17. context->HadError = true;
  18. if (context->Quiet)
  19. {
  20. return;
  21. }
  22. cmOStringStream e;
  23. e << "Error evaluating generator expression:\n"
  24. << " " << expr << "\n"
  25. << result;
  26. context->Makefile->GetCMakeInstance()
  27. ->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(),
  28. context->Backtrace);
  29. }
  30. //----------------------------------------------------------------------------
  31. struct cmGeneratorExpressionNode
  32. {
  33. virtual ~cmGeneratorExpressionNode() {}
  34. virtual bool GeneratesContent() const { return true; }
  35. virtual bool AcceptsSingleArbitraryContentParameter() const
  36. { return false; }
  37. virtual int NumExpectedParameters() const { return 1; }
  38. virtual std::string Evaluate(const std::vector<std::string> &parameters,
  39. cmGeneratorExpressionContext *context,
  40. const GeneratorExpressionContent *content
  41. ) const = 0;
  42. };
  43. //----------------------------------------------------------------------------
  44. static const struct ZeroNode : public cmGeneratorExpressionNode
  45. {
  46. ZeroNode() {}
  47. virtual bool GeneratesContent() const { return false; }
  48. std::string Evaluate(const std::vector<std::string> &,
  49. cmGeneratorExpressionContext *,
  50. const GeneratorExpressionContent *) const
  51. {
  52. // Unreachable
  53. return std::string();
  54. }
  55. } zeroNode;
  56. //----------------------------------------------------------------------------
  57. static const struct OneNode : public cmGeneratorExpressionNode
  58. {
  59. OneNode() {}
  60. virtual bool AcceptsSingleArbitraryContentParameter() const { return true; }
  61. std::string Evaluate(const std::vector<std::string> &,
  62. cmGeneratorExpressionContext *,
  63. const GeneratorExpressionContent *) const
  64. {
  65. // Unreachable
  66. return std::string();
  67. }
  68. } oneNode;
  69. //----------------------------------------------------------------------------
  70. #define BOOLEAN_OP_NODE(OPNAME, OP, SUCCESS_VALUE, FAILURE_VALUE) \
  71. static const struct OP ## Node : public cmGeneratorExpressionNode \
  72. { \
  73. OP ## Node () {} \
  74. /* We let -1 carry the meaning 'at least one' */ \
  75. virtual int NumExpectedParameters() const { return -1; } \
  76. \
  77. std::string Evaluate(const std::vector<std::string> &parameters, \
  78. cmGeneratorExpressionContext *context, \
  79. const GeneratorExpressionContent *content) const \
  80. { \
  81. std::vector<std::string>::const_iterator it = parameters.begin(); \
  82. const std::vector<std::string>::const_iterator end = parameters.end(); \
  83. for ( ; it != end; ++it) \
  84. { \
  85. if (*it == #FAILURE_VALUE) \
  86. { \
  87. return #FAILURE_VALUE; \
  88. } \
  89. else if (*it != #SUCCESS_VALUE) \
  90. { \
  91. reportError(context, content->GetOriginalExpression(), \
  92. "Parameters to $<" #OP "> must resolve to either '0' or '1'."); \
  93. return std::string(); \
  94. } \
  95. } \
  96. return #SUCCESS_VALUE; \
  97. } \
  98. } OPNAME;
  99. BOOLEAN_OP_NODE(andNode, AND, 1, 0)
  100. BOOLEAN_OP_NODE(orNode, OR, 0, 1)
  101. #undef BOOLEAN_OP_NODE
  102. //----------------------------------------------------------------------------
  103. static const struct NotNode : public cmGeneratorExpressionNode
  104. {
  105. NotNode() {}
  106. std::string Evaluate(const std::vector<std::string> &parameters,
  107. cmGeneratorExpressionContext *context,
  108. const GeneratorExpressionContent *content) const
  109. {
  110. if (*parameters.begin() != "0" && *parameters.begin() != "1")
  111. {
  112. reportError(context, content->GetOriginalExpression(),
  113. "$<NOT> parameter must resolve to exactly one '0' or '1' value.");
  114. return std::string();
  115. }
  116. return *parameters.begin() == "0" ? "1" : "0";
  117. }
  118. } notNode;
  119. //----------------------------------------------------------------------------
  120. static const struct ConfigurationNode : public cmGeneratorExpressionNode
  121. {
  122. ConfigurationNode() {}
  123. virtual int NumExpectedParameters() const { return 0; }
  124. std::string Evaluate(const std::vector<std::string> &,
  125. cmGeneratorExpressionContext *context,
  126. const GeneratorExpressionContent *) const
  127. {
  128. return context->Config ? context->Config : "";
  129. }
  130. } configurationNode;
  131. //----------------------------------------------------------------------------
  132. static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
  133. {
  134. ConfigurationTestNode() {}
  135. virtual int NumExpectedParameters() const { return 1; }
  136. std::string Evaluate(const std::vector<std::string> &parameters,
  137. cmGeneratorExpressionContext *context,
  138. const GeneratorExpressionContent *content) const
  139. {
  140. if (!context->Config)
  141. {
  142. return std::string();
  143. }
  144. cmsys::RegularExpression configValidator;
  145. configValidator.compile("^[A-Za-z0-9_]*$");
  146. if (!configValidator.find(parameters.begin()->c_str()))
  147. {
  148. reportError(context, content->GetOriginalExpression(),
  149. "Expression syntax not recognized.");
  150. return std::string();
  151. }
  152. return *parameters.begin() == context->Config ? "1" : "0";
  153. }
  154. } configurationTestNode;
  155. //----------------------------------------------------------------------------
  156. template<bool linker, bool soname>
  157. struct TargetFilesystemArtifactResultCreator
  158. {
  159. static std::string Create(cmTarget* target,
  160. cmGeneratorExpressionContext *context,
  161. const GeneratorExpressionContent *content);
  162. };
  163. //----------------------------------------------------------------------------
  164. template<>
  165. struct TargetFilesystemArtifactResultCreator<false, true>
  166. {
  167. static std::string Create(cmTarget* target,
  168. cmGeneratorExpressionContext *context,
  169. const GeneratorExpressionContent *content)
  170. {
  171. // The target soname file (.so.1).
  172. if(target->IsDLLPlatform())
  173. {
  174. ::reportError(context, content->GetOriginalExpression(),
  175. "TARGET_SONAME_FILE is not allowed "
  176. "for DLL target platforms.");
  177. return std::string();
  178. }
  179. if(target->GetType() != cmTarget::SHARED_LIBRARY)
  180. {
  181. ::reportError(context, content->GetOriginalExpression(),
  182. "TARGET_SONAME_FILE is allowed only for "
  183. "SHARED libraries.");
  184. return std::string();
  185. }
  186. std::string result = target->GetDirectory(context->Config);
  187. result += "/";
  188. result += target->GetSOName(context->Config);
  189. return result;
  190. }
  191. };
  192. //----------------------------------------------------------------------------
  193. template<>
  194. struct TargetFilesystemArtifactResultCreator<true, false>
  195. {
  196. static std::string Create(cmTarget* target,
  197. cmGeneratorExpressionContext *context,
  198. const GeneratorExpressionContent *content)
  199. {
  200. // The file used to link to the target (.so, .lib, .a).
  201. if(!target->IsLinkable())
  202. {
  203. ::reportError(context, content->GetOriginalExpression(),
  204. "TARGET_LINKER_FILE is allowed only for libraries and "
  205. "executables with ENABLE_EXPORTS.");
  206. return std::string();
  207. }
  208. return target->GetFullPath(context->Config,
  209. target->HasImportLibrary());
  210. }
  211. };
  212. //----------------------------------------------------------------------------
  213. template<>
  214. struct TargetFilesystemArtifactResultCreator<false, false>
  215. {
  216. static std::string Create(cmTarget* target,
  217. cmGeneratorExpressionContext *context,
  218. const GeneratorExpressionContent *)
  219. {
  220. return target->GetFullPath(context->Config, false, true);
  221. }
  222. };
  223. //----------------------------------------------------------------------------
  224. template<bool dirQual, bool nameQual>
  225. struct TargetFilesystemArtifactResultGetter
  226. {
  227. static std::string Get(const std::string &result);
  228. };
  229. //----------------------------------------------------------------------------
  230. template<>
  231. struct TargetFilesystemArtifactResultGetter<false, true>
  232. {
  233. static std::string Get(const std::string &result)
  234. { return cmSystemTools::GetFilenameName(result); }
  235. };
  236. //----------------------------------------------------------------------------
  237. template<>
  238. struct TargetFilesystemArtifactResultGetter<true, false>
  239. {
  240. static std::string Get(const std::string &result)
  241. { return cmSystemTools::GetFilenamePath(result); }
  242. };
  243. //----------------------------------------------------------------------------
  244. template<>
  245. struct TargetFilesystemArtifactResultGetter<false, false>
  246. {
  247. static std::string Get(const std::string &result)
  248. { return result; }
  249. };
  250. //----------------------------------------------------------------------------
  251. template<bool linker, bool soname, bool dirQual, bool nameQual>
  252. struct TargetFilesystemArtifact : public cmGeneratorExpressionNode
  253. {
  254. TargetFilesystemArtifact() {}
  255. virtual int NumExpectedParameters() const { return 1; }
  256. std::string Evaluate(const std::vector<std::string> &parameters,
  257. cmGeneratorExpressionContext *context,
  258. const GeneratorExpressionContent *content) const
  259. {
  260. // Lookup the referenced target.
  261. std::string name = *parameters.begin();
  262. cmsys::RegularExpression targetValidator;
  263. targetValidator.compile("^[A-Za-z0-9_]+$");
  264. if (!targetValidator.find(name.c_str()))
  265. {
  266. ::reportError(context, content->GetOriginalExpression(),
  267. "Expression syntax not recognized.");
  268. return std::string();
  269. }
  270. cmTarget* target = context->Makefile->FindTargetToUse(name.c_str());
  271. if(!target)
  272. {
  273. ::reportError(context, content->GetOriginalExpression(),
  274. "No target \"" + name + "\"");
  275. return std::string();
  276. }
  277. if(target->GetType() >= cmTarget::UTILITY &&
  278. target->GetType() != cmTarget::UNKNOWN_LIBRARY)
  279. {
  280. ::reportError(context, content->GetOriginalExpression(),
  281. "Target \"" + name + "\" is not an executable or library.");
  282. return std::string();
  283. }
  284. context->Targets.insert(target);
  285. std::string result =
  286. TargetFilesystemArtifactResultCreator<linker, soname>::Create(
  287. target,
  288. context,
  289. content);
  290. if (context->HadError)
  291. {
  292. return std::string();
  293. }
  294. return
  295. TargetFilesystemArtifactResultGetter<dirQual, nameQual>::Get(result);
  296. }
  297. };
  298. //----------------------------------------------------------------------------
  299. static const
  300. TargetFilesystemArtifact<false, false, false, false> targetFileNode;
  301. static const
  302. TargetFilesystemArtifact<true, false, false, false> targetLinkerFileNode;
  303. static const
  304. TargetFilesystemArtifact<false, true, false, false> targetSoNameFileNode;
  305. static const
  306. TargetFilesystemArtifact<false, false, false, true> targetFileNameNode;
  307. static const
  308. TargetFilesystemArtifact<true, false, false, true> targetLinkerFileNameNode;
  309. static const
  310. TargetFilesystemArtifact<false, true, false, true> targetSoNameFileNameNode;
  311. static const
  312. TargetFilesystemArtifact<false, false, true, false> targetFileDirNode;
  313. static const
  314. TargetFilesystemArtifact<true, false, true, false> targetLinkerFileDirNode;
  315. static const
  316. TargetFilesystemArtifact<false, true, true, false> targetSoNameFileDirNode;
  317. //----------------------------------------------------------------------------
  318. static const
  319. cmGeneratorExpressionNode* GetNode(const std::string &identifier)
  320. {
  321. if (identifier == "0")
  322. return &zeroNode;
  323. if (identifier == "1")
  324. return &oneNode;
  325. if (identifier == "AND")
  326. return &andNode;
  327. if (identifier == "OR")
  328. return &orNode;
  329. if (identifier == "NOT")
  330. return &notNode;
  331. else if (identifier == "CONFIGURATION")
  332. return &configurationNode;
  333. else if (identifier == "CONFIG")
  334. return &configurationTestNode;
  335. else if (identifier == "TARGET_FILE")
  336. return &targetFileNode;
  337. else if (identifier == "TARGET_LINKER_FILE")
  338. return &targetLinkerFileNode;
  339. else if (identifier == "TARGET_SONAME_FILE")
  340. return &targetSoNameFileNode;
  341. else if (identifier == "TARGET_FILE_NAME")
  342. return &targetFileNameNode;
  343. else if (identifier == "TARGET_LINKER_FILE_NAME")
  344. return &targetLinkerFileNameNode;
  345. else if (identifier == "TARGET_SONAME_FILE_NAME")
  346. return &targetSoNameFileNameNode;
  347. else if (identifier == "TARGET_FILE_DIR")
  348. return &targetFileDirNode;
  349. else if (identifier == "TARGET_LINKER_FILE_DIR")
  350. return &targetLinkerFileDirNode;
  351. else if (identifier == "TARGET_SONAME_FILE_DIR")
  352. return &targetSoNameFileDirNode;
  353. return 0;
  354. }
  355. //----------------------------------------------------------------------------
  356. GeneratorExpressionContent::GeneratorExpressionContent(
  357. const char *startContent,
  358. unsigned int length)
  359. : StartContent(startContent), ContentLength(length)
  360. {
  361. }
  362. //----------------------------------------------------------------------------
  363. std::string GeneratorExpressionContent::GetOriginalExpression() const
  364. {
  365. return std::string(this->StartContent, this->ContentLength);
  366. }
  367. //----------------------------------------------------------------------------
  368. std::string GeneratorExpressionContent::Evaluate(
  369. cmGeneratorExpressionContext *context) const
  370. {
  371. std::string identifier;
  372. {
  373. std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
  374. = this->IdentifierChildren.begin();
  375. const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
  376. = this->IdentifierChildren.end();
  377. for ( ; it != end; ++it)
  378. {
  379. identifier += (*it)->Evaluate(context);
  380. if (context->HadError)
  381. {
  382. return std::string();
  383. }
  384. }
  385. }
  386. const cmGeneratorExpressionNode *node = GetNode(identifier);
  387. if (!node)
  388. {
  389. reportError(context, this->GetOriginalExpression(),
  390. "Expression did not evaluate to a known generator expression");
  391. return std::string();
  392. }
  393. if (!node->GeneratesContent())
  394. {
  395. return std::string();
  396. }
  397. if (node->AcceptsSingleArbitraryContentParameter())
  398. {
  399. std::string result;
  400. std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
  401. pit = this->ParamChildren.begin();
  402. const
  403. std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
  404. pend = this->ParamChildren.end();
  405. for ( ; pit != pend; ++pit)
  406. {
  407. if (!result.empty())
  408. {
  409. result += ",";
  410. }
  411. std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
  412. = pit->begin();
  413. const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
  414. = pit->end();
  415. for ( ; it != end; ++it)
  416. {
  417. result += (*it)->Evaluate(context);
  418. if (context->HadError)
  419. {
  420. return std::string();
  421. }
  422. }
  423. }
  424. return result;
  425. }
  426. std::vector<std::string> parameters;
  427. {
  428. std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
  429. pit = this->ParamChildren.begin();
  430. const
  431. std::vector<std::vector<cmGeneratorExpressionEvaluator*> >::const_iterator
  432. pend = this->ParamChildren.end();
  433. for ( ; pit != pend; ++pit)
  434. {
  435. std::string parameter;
  436. std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it =
  437. pit->begin();
  438. const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end =
  439. pit->end();
  440. for ( ; it != end; ++it)
  441. {
  442. parameter += (*it)->Evaluate(context);
  443. if (context->HadError)
  444. {
  445. return std::string();
  446. }
  447. }
  448. parameters.push_back(parameter);
  449. }
  450. }
  451. int numExpected = node->NumExpectedParameters();
  452. if ((numExpected != -1 && (unsigned int)numExpected != parameters.size()))
  453. {
  454. if (numExpected == 0)
  455. {
  456. reportError(context, this->GetOriginalExpression(),
  457. "$<" + identifier + "> expression requires no parameters.");
  458. }
  459. else if (numExpected == 1)
  460. {
  461. reportError(context, this->GetOriginalExpression(),
  462. "$<" + identifier + "> expression requires "
  463. "exactly one parameter.");
  464. }
  465. else
  466. {
  467. cmOStringStream e;
  468. e << "$<" + identifier + "> expression requires "
  469. << numExpected
  470. << " comma separated parameters, but got "
  471. << parameters.size() << " instead.";
  472. reportError(context, this->GetOriginalExpression(), e.str());
  473. }
  474. return std::string();
  475. }
  476. if (numExpected == -1 && parameters.empty())
  477. {
  478. reportError(context, this->GetOriginalExpression(), "$<" + identifier
  479. + "> expression requires at least one parameter.");
  480. return std::string();
  481. }
  482. return node->Evaluate(parameters, context, this);
  483. }
  484. //----------------------------------------------------------------------------
  485. static void deleteAll(const std::vector<cmGeneratorExpressionEvaluator*> &c)
  486. {
  487. std::vector<cmGeneratorExpressionEvaluator*>::const_iterator it
  488. = c.begin();
  489. const std::vector<cmGeneratorExpressionEvaluator*>::const_iterator end
  490. = c.end();
  491. for ( ; it != end; ++it)
  492. {
  493. delete *it;
  494. }
  495. }
  496. //----------------------------------------------------------------------------
  497. GeneratorExpressionContent::~GeneratorExpressionContent()
  498. {
  499. deleteAll(this->IdentifierChildren);
  500. typedef std::vector<cmGeneratorExpressionEvaluator*> EvaluatorVector;
  501. typedef std::vector<cmGeneratorExpressionToken> TokenVector;
  502. std::vector<EvaluatorVector>::const_iterator pit =
  503. this->ParamChildren.begin();
  504. const std::vector<EvaluatorVector>::const_iterator pend =
  505. this->ParamChildren.end();
  506. for ( ; pit != pend; ++pit)
  507. {
  508. deleteAll(*pit);
  509. }
  510. }