cmGeneratorExpression.cxx 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2000-2009 Kitware, Inc., Insight Software Consortium
  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 "cmGeneratorExpression.h"
  11. #include "cmMakefile.h"
  12. #include "cmTarget.h"
  13. #include <cmsys/String.h>
  14. //----------------------------------------------------------------------------
  15. cmGeneratorExpression::cmGeneratorExpression(
  16. cmMakefile* mf, const char* config,
  17. cmListFileBacktrace const& backtrace, bool quiet):
  18. Makefile(mf), Config(config), Backtrace(backtrace), Quiet(quiet)
  19. {
  20. this->TargetInfo.compile("^\\$<TARGET"
  21. "(|_SONAME|_LINKER)" // File with what purpose?
  22. "_FILE(|_NAME|_DIR):" // Filename component.
  23. "([A-Za-z0-9_.-]+)" // Target name.
  24. ">$");
  25. this->TestConfig.compile("^\\$<CONFIG:([A-Za-z0-9_]*)>$");
  26. }
  27. //----------------------------------------------------------------------------
  28. const char* cmGeneratorExpression::Process(std::string const& input)
  29. {
  30. return this->Process(input.c_str());
  31. }
  32. //----------------------------------------------------------------------------
  33. const char* cmGeneratorExpression::Process(const char* input)
  34. {
  35. this->Data.clear();
  36. // We construct and evaluate expressions directly in the output
  37. // buffer. Each expression is replaced by its own output value
  38. // after evaluation. A stack of barriers records the starting
  39. // indices of open (pending) expressions.
  40. for(const char* c = input; *c; ++c)
  41. {
  42. if(c[0] == '$' && c[1] == '<')
  43. {
  44. this->Barriers.push(this->Data.size());
  45. this->Data.push_back('$');
  46. this->Data.push_back('<');
  47. c += 1;
  48. }
  49. else if(c[0] == '>' && !this->Barriers.empty())
  50. {
  51. this->Data.push_back('>');
  52. if(!this->Evaluate()) { break; }
  53. this->Barriers.pop();
  54. }
  55. else
  56. {
  57. this->Data.push_back(c[0]);
  58. }
  59. }
  60. // Return a null-terminated output value.
  61. this->Data.push_back('\0');
  62. return &*this->Data.begin();
  63. }
  64. //----------------------------------------------------------------------------
  65. bool cmGeneratorExpression::Evaluate()
  66. {
  67. // The top-most barrier points at the beginning of the expression.
  68. size_t barrier = this->Barriers.top();
  69. // Construct a null-terminated representation of the expression.
  70. this->Data.push_back('\0');
  71. const char* expr = &*(this->Data.begin()+barrier);
  72. // Evaluate the expression.
  73. std::string result;
  74. if(this->Evaluate(expr, result))
  75. {
  76. // Success. Replace the expression with its evaluation result.
  77. this->Data.erase(this->Data.begin()+barrier, this->Data.end());
  78. this->Data.insert(this->Data.end(), result.begin(), result.end());
  79. return true;
  80. }
  81. else if(!this->Quiet)
  82. {
  83. // Failure. Report the error message.
  84. cmOStringStream e;
  85. e << "Error evaluating generator expression:\n"
  86. << " " << expr << "\n"
  87. << result;
  88. this->Makefile->GetCMakeInstance()
  89. ->IssueMessage(cmake::FATAL_ERROR, e.str().c_str(),
  90. this->Backtrace);
  91. return false;
  92. }
  93. return true;
  94. }
  95. //----------------------------------------------------------------------------
  96. static bool cmGeneratorExpressionBool(const char* c, std::string& result,
  97. const char* name,
  98. const char* a, const char* b)
  99. {
  100. result = a;
  101. while((c[0] == '0' || c[0] == '1') && (c[1] == ',' || c[1] == '>'))
  102. {
  103. if(c[0] == b[0]) { result = b; }
  104. c += 2;
  105. }
  106. if(c[0])
  107. {
  108. result = name;
  109. result += " requires one or more comma-separated '0' or '1' values.";
  110. return false;
  111. }
  112. return true;
  113. }
  114. //----------------------------------------------------------------------------
  115. bool cmGeneratorExpression::Evaluate(const char* expr, std::string& result)
  116. {
  117. if(this->TargetInfo.find(expr))
  118. {
  119. if(!this->EvaluateTargetInfo(result))
  120. {
  121. return false;
  122. }
  123. }
  124. else if(strcmp(expr, "$<CONFIGURATION>") == 0)
  125. {
  126. result = this->Config? this->Config : "";
  127. }
  128. else if(strncmp(expr, "$<0:",4) == 0)
  129. {
  130. result = "";
  131. }
  132. else if(strncmp(expr, "$<1:",4) == 0)
  133. {
  134. result = std::string(expr+4, strlen(expr)-5);
  135. }
  136. else if(strncmp(expr, "$<NOT:",6) == 0)
  137. {
  138. const char* c = expr+6;
  139. if((c[0] != '0' && c[0] != '1') || c[1] != '>' || c[2])
  140. {
  141. result = "NOT requires exactly one '0' or '1' value.";
  142. return false;
  143. }
  144. result = c[0] == '1'? "0" : "1";
  145. }
  146. else if(strncmp(expr, "$<AND:",6) == 0)
  147. {
  148. return cmGeneratorExpressionBool(expr+6, result, "AND", "1", "0");
  149. }
  150. else if(strncmp(expr, "$<OR:",5) == 0)
  151. {
  152. return cmGeneratorExpressionBool(expr+5, result, "OR", "0", "1");
  153. }
  154. else if(this->TestConfig.find(expr))
  155. {
  156. result = cmsysString_strcasecmp(this->TestConfig.match(1).c_str(),
  157. this->Config? this->Config:"") == 0
  158. ? "1":"0";
  159. }
  160. else
  161. {
  162. result = "Expression syntax not recognized.";
  163. return false;
  164. }
  165. return true;
  166. }
  167. //----------------------------------------------------------------------------
  168. bool cmGeneratorExpression::EvaluateTargetInfo(std::string& result)
  169. {
  170. // Lookup the referenced target.
  171. std::string name = this->TargetInfo.match(3);
  172. cmTarget* target = this->Makefile->FindTargetToUse(name.c_str());
  173. if(!target)
  174. {
  175. result = "No target \"" + name + "\"";
  176. return false;
  177. }
  178. if(target->GetType() >= cmTarget::UTILITY &&
  179. target->GetType() != cmTarget::UNKNOWN_LIBRARY)
  180. {
  181. result = "Target \"" + name + "\" is not an executable or library.";
  182. return false;
  183. }
  184. this->Targets.insert(target);
  185. // Lookup the target file with the given purpose.
  186. std::string purpose = this->TargetInfo.match(1);
  187. if(purpose == "")
  188. {
  189. // The target implementation file (.so.1.2, .dll, .exe, .a).
  190. result = target->GetFullPath(this->Config, false, true);
  191. }
  192. else if(purpose == "_LINKER")
  193. {
  194. // The file used to link to the target (.so, .lib, .a).
  195. if(!target->IsLinkable())
  196. {
  197. result = ("TARGET_LINKER_FILE is allowed only for libraries and "
  198. "executables with ENABLE_EXPORTS.");
  199. return false;
  200. }
  201. result = target->GetFullPath(this->Config, target->HasImportLibrary());
  202. }
  203. else if(purpose == "_SONAME")
  204. {
  205. // The target soname file (.so.1).
  206. if(target->IsDLLPlatform())
  207. {
  208. result = "TARGET_SONAME_FILE is not allowed for DLL target platforms.";
  209. return false;
  210. }
  211. if(target->GetType() != cmTarget::SHARED_LIBRARY)
  212. {
  213. result = "TARGET_SONAME_FILE is allowed only for SHARED libraries.";
  214. return false;
  215. }
  216. result = target->GetDirectory(this->Config);
  217. result += "/";
  218. result += target->GetSOName(this->Config);
  219. }
  220. // Extract the requested portion of the full path.
  221. std::string part = this->TargetInfo.match(2);
  222. if(part == "_NAME")
  223. {
  224. result = cmSystemTools::GetFilenameName(result);
  225. }
  226. else if(part == "_DIR")
  227. {
  228. result = cmSystemTools::GetFilenamePath(result);
  229. }
  230. return true;
  231. }