cmGeneratorExpressionEvaluationFile.cxx 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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 "cmGeneratorExpressionEvaluationFile.h"
  4. #include <memory>
  5. #include <sstream>
  6. #include <utility>
  7. #include "cmsys/FStream.hxx"
  8. #include "cmGenExContext.h"
  9. #include "cmGeneratedFileStream.h"
  10. #include "cmGlobalGenerator.h"
  11. #include "cmListFileCache.h"
  12. #include "cmLocalGenerator.h"
  13. #include "cmMakefile.h"
  14. #include "cmMessageType.h"
  15. #include "cmSourceFile.h"
  16. #include "cmSystemTools.h"
  17. cmGeneratorExpressionEvaluationFile::cmGeneratorExpressionEvaluationFile(
  18. std::string input, std::string target,
  19. std::unique_ptr<cmCompiledGeneratorExpression> outputFileExpr,
  20. std::unique_ptr<cmCompiledGeneratorExpression> condition,
  21. bool inputIsContent, std::string newLineCharacter, mode_t permissions,
  22. cmPolicies::PolicyStatus policyStatusCMP0070,
  23. cmPolicies::PolicyStatus policyStatusCMP0189)
  24. : Input(std::move(input))
  25. , Target(std::move(target))
  26. , OutputFileExpr(std::move(outputFileExpr))
  27. , Condition(std::move(condition))
  28. , InputIsContent(inputIsContent)
  29. , NewLineCharacter(std::move(newLineCharacter))
  30. , PolicyStatusCMP0070(policyStatusCMP0070)
  31. , PolicyStatusCMP0189(policyStatusCMP0189)
  32. , Permissions(permissions)
  33. {
  34. }
  35. void cmGeneratorExpressionEvaluationFile::Generate(
  36. cmLocalGenerator* lg, std::string const& config, std::string const& lang,
  37. cmCompiledGeneratorExpression* inputExpression,
  38. std::map<std::string, std::string>& outputFiles, mode_t perm)
  39. {
  40. cm::GenEx::Context context(lg, config, lang);
  41. context.SetCMP0189(this->PolicyStatusCMP0189);
  42. std::string rawCondition = this->Condition->GetInput();
  43. cmGeneratorTarget* target = lg->FindGeneratorTargetToUse(this->Target);
  44. if (!rawCondition.empty()) {
  45. std::string condResult =
  46. this->Condition->Evaluate(context, nullptr, target);
  47. if (condResult == "0") {
  48. return;
  49. }
  50. if (condResult != "1") {
  51. std::ostringstream e;
  52. e << "Evaluation file condition \"" << rawCondition
  53. << "\" did "
  54. "not evaluate to valid content. Got \""
  55. << condResult << "\".";
  56. lg->IssueMessage(MessageType::FATAL_ERROR, e.str());
  57. return;
  58. }
  59. }
  60. std::string const outputFileName = this->GetOutputFileName(context, target);
  61. std::string const& outputContent =
  62. inputExpression->Evaluate(context, nullptr, target);
  63. auto it = outputFiles.find(outputFileName);
  64. if (it != outputFiles.end()) {
  65. if (it->second == outputContent) {
  66. return;
  67. }
  68. std::ostringstream e;
  69. e << "Evaluation file to be written multiple times with different "
  70. "content. "
  71. "This is generally caused by the content evaluating the "
  72. "configuration type, language, or location of object files:\n "
  73. << outputFileName;
  74. lg->IssueMessage(MessageType::FATAL_ERROR, e.str());
  75. return;
  76. }
  77. lg->GetMakefile()->AddCMakeOutputFile(outputFileName);
  78. this->Files.push_back(outputFileName);
  79. outputFiles[outputFileName] = outputContent;
  80. bool openWithBinaryFlag = false;
  81. if (!this->NewLineCharacter.empty()) {
  82. openWithBinaryFlag = true;
  83. }
  84. cmGeneratedFileStream fout;
  85. fout.Open(outputFileName, false, openWithBinaryFlag);
  86. if (!fout) {
  87. lg->IssueMessage(MessageType::FATAL_ERROR,
  88. "Could not open file for write in copy operation " +
  89. outputFileName);
  90. return;
  91. }
  92. fout.SetCopyIfDifferent(true);
  93. std::istringstream iss(outputContent);
  94. std::string line;
  95. bool hasNewLine = false;
  96. while (cmSystemTools::GetLineFromStream(iss, line, &hasNewLine)) {
  97. fout << line;
  98. if (!this->NewLineCharacter.empty()) {
  99. fout << this->NewLineCharacter;
  100. } else if (hasNewLine) {
  101. // if new line character is not specified, the file will be opened in
  102. // text mode. So, "\n" will be translated to the correct newline
  103. // ending based on the platform.
  104. fout << "\n";
  105. }
  106. }
  107. if (fout.Close() && perm) {
  108. cmSystemTools::SetPermissions(outputFileName.c_str(), perm);
  109. }
  110. }
  111. void cmGeneratorExpressionEvaluationFile::CreateOutputFile(
  112. cmLocalGenerator* lg, std::string const& config)
  113. {
  114. std::vector<std::string> enabledLanguages;
  115. cmGlobalGenerator* gg = lg->GetGlobalGenerator();
  116. cmGeneratorTarget* target = lg->FindGeneratorTargetToUse(this->Target);
  117. gg->GetEnabledLanguages(enabledLanguages);
  118. for (std::string const& lang : enabledLanguages) {
  119. cm::GenEx::Context context(lg, config, lang);
  120. context.SetCMP0189(this->PolicyStatusCMP0189);
  121. std::string const name = this->GetOutputFileName(context, target);
  122. cmSourceFile* sf = lg->GetMakefile()->GetOrCreateGeneratedSource(name);
  123. // Tell the build system generators that there is no build rule
  124. // to generate the file.
  125. sf->SetProperty("__CMAKE_GENERATED_BY_CMAKE", "1");
  126. gg->SetFilenameTargetDepends(
  127. sf, this->OutputFileExpr->GetSourceSensitiveTargets());
  128. }
  129. }
  130. void cmGeneratorExpressionEvaluationFile::Generate(cmLocalGenerator* lg)
  131. {
  132. std::string inputContent;
  133. if (this->InputIsContent) {
  134. inputContent = this->Input;
  135. } else {
  136. std::string const inputFileName = this->GetInputFileName(lg);
  137. lg->GetMakefile()->AddCMakeDependFile(inputFileName);
  138. if (!this->Permissions) {
  139. cmSystemTools::GetPermissions(inputFileName.c_str(), this->Permissions);
  140. }
  141. cmsys::ifstream fin(inputFileName.c_str());
  142. if (!fin) {
  143. std::ostringstream e;
  144. e << "Evaluation file \"" << inputFileName << "\" cannot be read.";
  145. lg->IssueMessage(MessageType::FATAL_ERROR, e.str());
  146. return;
  147. }
  148. std::string line;
  149. std::string sep;
  150. while (cmSystemTools::GetLineFromStream(fin, line)) {
  151. inputContent += sep + line;
  152. sep = "\n";
  153. }
  154. inputContent += sep;
  155. }
  156. cmListFileBacktrace lfbt = this->OutputFileExpr->GetBacktrace();
  157. cmGeneratorExpression contentGE(*lg->GetCMakeInstance(), lfbt);
  158. std::unique_ptr<cmCompiledGeneratorExpression> inputExpression =
  159. contentGE.Parse(inputContent);
  160. std::map<std::string, std::string> outputFiles;
  161. std::vector<std::string> allConfigs =
  162. lg->GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
  163. std::vector<std::string> enabledLanguages;
  164. cmGlobalGenerator* gg = lg->GetGlobalGenerator();
  165. gg->GetEnabledLanguages(enabledLanguages);
  166. for (std::string const& le : enabledLanguages) {
  167. for (std::string const& li : allConfigs) {
  168. this->Generate(lg, li, le, inputExpression.get(), outputFiles,
  169. this->Permissions);
  170. if (cmSystemTools::GetFatalErrorOccurred()) {
  171. return;
  172. }
  173. }
  174. }
  175. }
  176. std::string cmGeneratorExpressionEvaluationFile::GetInputFileName(
  177. cmLocalGenerator const* lg)
  178. {
  179. std::string inputFileName = this->Input;
  180. if (cmSystemTools::FileIsFullPath(inputFileName)) {
  181. inputFileName = cmSystemTools::CollapseFullPath(inputFileName);
  182. } else {
  183. inputFileName = this->FixRelativePath(inputFileName, PathForInput, lg);
  184. }
  185. return inputFileName;
  186. }
  187. std::string cmGeneratorExpressionEvaluationFile::GetOutputFileName(
  188. cm::GenEx::Context const& context, cmGeneratorTarget* target)
  189. {
  190. std::string outputFileName =
  191. this->OutputFileExpr->Evaluate(context, nullptr, target);
  192. if (cmSystemTools::FileIsFullPath(outputFileName)) {
  193. outputFileName = cmSystemTools::CollapseFullPath(outputFileName);
  194. } else {
  195. outputFileName =
  196. this->FixRelativePath(outputFileName, PathForOutput, context.LG);
  197. }
  198. return outputFileName;
  199. }
  200. std::string cmGeneratorExpressionEvaluationFile::FixRelativePath(
  201. std::string const& relativePath, PathRole role, cmLocalGenerator const* lg)
  202. {
  203. std::string resultPath;
  204. switch (this->PolicyStatusCMP0070) {
  205. case cmPolicies::WARN: {
  206. std::string arg;
  207. switch (role) {
  208. case PathForInput:
  209. arg = "INPUT";
  210. break;
  211. case PathForOutput:
  212. arg = "OUTPUT";
  213. break;
  214. }
  215. std::ostringstream w;
  216. /* clang-format off */
  217. w <<
  218. cmPolicies::GetPolicyWarning(cmPolicies::CMP0070) << "\n"
  219. "file(GENERATE) given relative " << arg << " path:\n"
  220. " " << relativePath << "\n"
  221. "This is not defined behavior unless CMP0070 is set to NEW. "
  222. "For compatibility with older versions of CMake, the previous "
  223. "undefined behavior will be used."
  224. ;
  225. /* clang-format on */
  226. lg->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
  227. }
  228. CM_FALLTHROUGH;
  229. case cmPolicies::OLD:
  230. // OLD behavior is to use the relative path unchanged,
  231. // which ends up being used relative to the working dir.
  232. resultPath = relativePath;
  233. break;
  234. case cmPolicies::NEW:
  235. // NEW behavior is to interpret the relative path with respect
  236. // to the current source or binary directory.
  237. switch (role) {
  238. case PathForInput:
  239. resultPath = cmSystemTools::CollapseFullPath(
  240. relativePath, lg->GetCurrentSourceDirectory());
  241. break;
  242. case PathForOutput:
  243. resultPath = cmSystemTools::CollapseFullPath(
  244. relativePath, lg->GetCurrentBinaryDirectory());
  245. break;
  246. }
  247. break;
  248. }
  249. return resultPath;
  250. }