cmGeneratorExpression.cxx 7.1 KB

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