cmForEachCommand.cxx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmForEachCommand.h"
  4. #include <sstream>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <utility>
  8. #include "cm_memory.hxx"
  9. #include "cm_static_string_view.hxx"
  10. #include "cm_string_view.hxx"
  11. #include "cmExecutionStatus.h"
  12. #include "cmFunctionBlocker.h"
  13. #include "cmListFileCache.h"
  14. #include "cmMakefile.h"
  15. #include "cmMessageType.h"
  16. #include "cmRange.h"
  17. #include "cmStringAlgorithms.h"
  18. #include "cmSystemTools.h"
  19. namespace {
  20. bool HandleInMode(std::vector<std::string> const& args, cmMakefile& makefile);
  21. class cmForEachFunctionBlocker : public cmFunctionBlocker
  22. {
  23. public:
  24. cmForEachFunctionBlocker(cmMakefile* mf);
  25. ~cmForEachFunctionBlocker() override;
  26. cm::string_view StartCommandName() const override { return "foreach"_s; }
  27. cm::string_view EndCommandName() const override { return "endforeach"_s; }
  28. bool ArgumentsMatch(cmListFileFunction const& lff,
  29. cmMakefile& mf) const override;
  30. bool Replay(std::vector<cmListFileFunction> functions,
  31. cmExecutionStatus& inStatus) override;
  32. std::vector<std::string> Args;
  33. private:
  34. cmMakefile* Makefile;
  35. };
  36. cmForEachFunctionBlocker::cmForEachFunctionBlocker(cmMakefile* mf)
  37. : Makefile(mf)
  38. {
  39. this->Makefile->PushLoopBlock();
  40. }
  41. cmForEachFunctionBlocker::~cmForEachFunctionBlocker()
  42. {
  43. this->Makefile->PopLoopBlock();
  44. }
  45. bool cmForEachFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
  46. cmMakefile& mf) const
  47. {
  48. std::vector<std::string> expandedArguments;
  49. mf.ExpandArguments(lff.Arguments, expandedArguments);
  50. return expandedArguments.empty() || expandedArguments[0] == this->Args[0];
  51. }
  52. bool cmForEachFunctionBlocker::Replay(
  53. std::vector<cmListFileFunction> functions, cmExecutionStatus& inStatus)
  54. {
  55. cmMakefile& mf = inStatus.GetMakefile();
  56. // at end of for each execute recorded commands
  57. // store the old value
  58. std::string oldDef;
  59. if (mf.GetDefinition(this->Args[0])) {
  60. oldDef = mf.GetDefinition(this->Args[0]);
  61. }
  62. for (std::string const& arg : cmMakeRange(this->Args).advance(1)) {
  63. // set the variable to the loop value
  64. mf.AddDefinition(this->Args[0], arg);
  65. // Invoke all the functions that were collected in the block.
  66. for (cmListFileFunction const& func : functions) {
  67. cmExecutionStatus status(mf);
  68. mf.ExecuteCommand(func, status);
  69. if (status.GetReturnInvoked()) {
  70. inStatus.SetReturnInvoked();
  71. // restore the variable to its prior value
  72. mf.AddDefinition(this->Args[0], oldDef);
  73. return true;
  74. }
  75. if (status.GetBreakInvoked()) {
  76. // restore the variable to its prior value
  77. mf.AddDefinition(this->Args[0], oldDef);
  78. return true;
  79. }
  80. if (status.GetContinueInvoked()) {
  81. break;
  82. }
  83. if (cmSystemTools::GetFatalErrorOccured()) {
  84. return true;
  85. }
  86. }
  87. }
  88. // restore the variable to its prior value
  89. mf.AddDefinition(this->Args[0], oldDef);
  90. return true;
  91. }
  92. }
  93. bool cmForEachCommand(std::vector<std::string> const& args,
  94. cmExecutionStatus& status)
  95. {
  96. if (args.empty()) {
  97. status.SetError("called with incorrect number of arguments");
  98. return false;
  99. }
  100. if (args.size() > 1 && args[1] == "IN") {
  101. return HandleInMode(args, status.GetMakefile());
  102. }
  103. // create a function blocker
  104. auto fb = cm::make_unique<cmForEachFunctionBlocker>(&status.GetMakefile());
  105. if (args.size() > 1) {
  106. if (args[1] == "RANGE") {
  107. int start = 0;
  108. int stop = 0;
  109. int step = 0;
  110. if (args.size() == 3) {
  111. stop = atoi(args[2].c_str());
  112. }
  113. if (args.size() == 4) {
  114. start = atoi(args[2].c_str());
  115. stop = atoi(args[3].c_str());
  116. }
  117. if (args.size() == 5) {
  118. start = atoi(args[2].c_str());
  119. stop = atoi(args[3].c_str());
  120. step = atoi(args[4].c_str());
  121. }
  122. if (step == 0) {
  123. if (start > stop) {
  124. step = -1;
  125. } else {
  126. step = 1;
  127. }
  128. }
  129. if ((start > stop && step > 0) || (start < stop && step < 0) ||
  130. step == 0) {
  131. std::ostringstream str;
  132. str << "called with incorrect range specification: start ";
  133. str << start << ", stop " << stop << ", step " << step;
  134. status.SetError(str.str());
  135. return false;
  136. }
  137. std::vector<std::string> range;
  138. char buffer[100];
  139. range.push_back(args[0]);
  140. int cc;
  141. for (cc = start;; cc += step) {
  142. if ((step > 0 && cc > stop) || (step < 0 && cc < stop)) {
  143. break;
  144. }
  145. sprintf(buffer, "%d", cc);
  146. range.emplace_back(buffer);
  147. if (cc == stop) {
  148. break;
  149. }
  150. }
  151. fb->Args = range;
  152. } else {
  153. fb->Args = args;
  154. }
  155. } else {
  156. fb->Args = args;
  157. }
  158. status.GetMakefile().AddFunctionBlocker(std::move(fb));
  159. return true;
  160. }
  161. namespace {
  162. bool HandleInMode(std::vector<std::string> const& args, cmMakefile& makefile)
  163. {
  164. auto fb = cm::make_unique<cmForEachFunctionBlocker>(&makefile);
  165. fb->Args.push_back(args[0]);
  166. enum Doing
  167. {
  168. DoingNone,
  169. DoingLists,
  170. DoingItems
  171. };
  172. Doing doing = DoingNone;
  173. for (unsigned int i = 2; i < args.size(); ++i) {
  174. if (doing == DoingItems) {
  175. fb->Args.push_back(args[i]);
  176. } else if (args[i] == "LISTS") {
  177. doing = DoingLists;
  178. } else if (args[i] == "ITEMS") {
  179. doing = DoingItems;
  180. } else if (doing == DoingLists) {
  181. const char* value = makefile.GetDefinition(args[i]);
  182. if (value && *value) {
  183. cmExpandList(value, fb->Args, true);
  184. }
  185. } else {
  186. std::ostringstream e;
  187. e << "Unknown argument:\n"
  188. << " " << args[i] << "\n";
  189. makefile.IssueMessage(MessageType::FATAL_ERROR, e.str());
  190. return true;
  191. }
  192. }
  193. makefile.AddFunctionBlocker(std::move(fb));
  194. return true;
  195. }
  196. }