cmMacroCommand.cxx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  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 "cmMacroCommand.h"
  4. #include <sstream>
  5. #include <stdio.h>
  6. #include <utility>
  7. #include "cm_memory.hxx"
  8. #include "cmAlgorithms.h"
  9. #include "cmExecutionStatus.h"
  10. #include "cmMakefile.h"
  11. #include "cmPolicies.h"
  12. #include "cmRange.h"
  13. #include "cmState.h"
  14. #include "cmSystemTools.h"
  15. // define the class for macro commands
  16. class cmMacroHelperCommand : public cmCommand
  17. {
  18. public:
  19. /**
  20. * This is a virtual constructor for the command.
  21. */
  22. std::unique_ptr<cmCommand> Clone() override
  23. {
  24. auto newC = cm::make_unique<cmMacroHelperCommand>();
  25. // we must copy when we clone
  26. newC->Args = this->Args;
  27. newC->Functions = this->Functions;
  28. newC->FilePath = this->FilePath;
  29. newC->Policies = this->Policies;
  30. return std::unique_ptr<cmCommand>(std::move(newC));
  31. }
  32. /**
  33. * This is called when the command is first encountered in
  34. * the CMakeLists.txt file.
  35. */
  36. bool InvokeInitialPass(const std::vector<cmListFileArgument>& args,
  37. cmExecutionStatus&) override;
  38. bool InitialPass(std::vector<std::string> const&,
  39. cmExecutionStatus&) override
  40. {
  41. return false;
  42. }
  43. std::vector<std::string> Args;
  44. std::vector<cmListFileFunction> Functions;
  45. cmPolicies::PolicyMap Policies;
  46. std::string FilePath;
  47. };
  48. bool cmMacroHelperCommand::InvokeInitialPass(
  49. const std::vector<cmListFileArgument>& args, cmExecutionStatus& inStatus)
  50. {
  51. // Expand the argument list to the macro.
  52. std::vector<std::string> expandedArgs;
  53. this->Makefile->ExpandArguments(args, expandedArgs);
  54. // make sure the number of arguments passed is at least the number
  55. // required by the signature
  56. if (expandedArgs.size() < this->Args.size() - 1) {
  57. std::string errorMsg =
  58. "Macro invoked with incorrect arguments for macro named: ";
  59. errorMsg += this->Args[0];
  60. this->SetError(errorMsg);
  61. return false;
  62. }
  63. cmMakefile::MacroPushPop macroScope(this->Makefile, this->FilePath,
  64. this->Policies);
  65. // set the value of argc
  66. std::ostringstream argcDefStream;
  67. argcDefStream << expandedArgs.size();
  68. std::string argcDef = argcDefStream.str();
  69. std::vector<std::string>::const_iterator eit =
  70. expandedArgs.begin() + (this->Args.size() - 1);
  71. std::string expandedArgn = cmJoin(cmMakeRange(eit, expandedArgs.end()), ";");
  72. std::string expandedArgv = cmJoin(expandedArgs, ";");
  73. std::vector<std::string> variables;
  74. variables.reserve(this->Args.size() - 1);
  75. for (unsigned int j = 1; j < this->Args.size(); ++j) {
  76. variables.push_back("${" + this->Args[j] + "}");
  77. }
  78. std::vector<std::string> argVs;
  79. argVs.reserve(expandedArgs.size());
  80. char argvName[60];
  81. for (unsigned int j = 0; j < expandedArgs.size(); ++j) {
  82. sprintf(argvName, "${ARGV%u}", j);
  83. argVs.emplace_back(argvName);
  84. }
  85. // Invoke all the functions that were collected in the block.
  86. cmListFileFunction newLFF;
  87. // for each function
  88. for (cmListFileFunction const& func : this->Functions) {
  89. // Replace the formal arguments and then invoke the command.
  90. newLFF.Arguments.clear();
  91. newLFF.Arguments.reserve(func.Arguments.size());
  92. newLFF.Name = func.Name;
  93. newLFF.Line = func.Line;
  94. // for each argument of the current function
  95. for (cmListFileArgument const& k : func.Arguments) {
  96. cmListFileArgument arg;
  97. arg.Value = k.Value;
  98. if (k.Delim != cmListFileArgument::Bracket) {
  99. // replace formal arguments
  100. for (unsigned int j = 0; j < variables.size(); ++j) {
  101. cmSystemTools::ReplaceString(arg.Value, variables[j],
  102. expandedArgs[j]);
  103. }
  104. // replace argc
  105. cmSystemTools::ReplaceString(arg.Value, "${ARGC}", argcDef);
  106. cmSystemTools::ReplaceString(arg.Value, "${ARGN}", expandedArgn);
  107. cmSystemTools::ReplaceString(arg.Value, "${ARGV}", expandedArgv);
  108. // if the current argument of the current function has ${ARGV in it
  109. // then try replacing ARGV values
  110. if (arg.Value.find("${ARGV") != std::string::npos) {
  111. for (unsigned int t = 0; t < expandedArgs.size(); ++t) {
  112. cmSystemTools::ReplaceString(arg.Value, argVs[t], expandedArgs[t]);
  113. }
  114. }
  115. }
  116. arg.Delim = k.Delim;
  117. arg.Line = k.Line;
  118. newLFF.Arguments.push_back(std::move(arg));
  119. }
  120. cmExecutionStatus status(*this->GetMakefile());
  121. if (!this->Makefile->ExecuteCommand(newLFF, status) ||
  122. status.GetNestedError()) {
  123. // The error message should have already included the call stack
  124. // so we do not need to report an error here.
  125. macroScope.Quiet();
  126. inStatus.SetNestedError();
  127. return false;
  128. }
  129. if (status.GetReturnInvoked()) {
  130. inStatus.SetReturnInvoked();
  131. return true;
  132. }
  133. if (status.GetBreakInvoked()) {
  134. inStatus.SetBreakInvoked();
  135. return true;
  136. }
  137. }
  138. return true;
  139. }
  140. bool cmMacroFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
  141. cmMakefile& mf,
  142. cmExecutionStatus&)
  143. {
  144. // record commands until we hit the ENDMACRO
  145. // at the ENDMACRO call we shift gears and start looking for invocations
  146. if (lff.Name.Lower == "macro") {
  147. this->Depth++;
  148. } else if (lff.Name.Lower == "endmacro") {
  149. // if this is the endmacro for this macro then execute
  150. if (!this->Depth) {
  151. mf.AppendProperty("MACROS", this->Args[0].c_str());
  152. // create a new command and add it to cmake
  153. auto f = cm::make_unique<cmMacroHelperCommand>();
  154. f->Args = this->Args;
  155. f->Functions = this->Functions;
  156. f->FilePath = this->GetStartingContext().FilePath;
  157. mf.RecordPolicies(f->Policies);
  158. mf.GetState()->AddScriptedCommand(this->Args[0], std::move(f));
  159. // remove the function blocker now that the macro is defined
  160. mf.RemoveFunctionBlocker(this, lff);
  161. return true;
  162. }
  163. // decrement for each nested macro that ends
  164. this->Depth--;
  165. }
  166. // if it wasn't an endmacro and we are not executing then we must be
  167. // recording
  168. this->Functions.push_back(lff);
  169. return true;
  170. }
  171. bool cmMacroFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
  172. cmMakefile& mf)
  173. {
  174. if (lff.Name.Lower == "endmacro") {
  175. std::vector<std::string> expandedArguments;
  176. mf.ExpandArguments(lff.Arguments, expandedArguments,
  177. this->GetStartingContext().FilePath.c_str());
  178. // if the endmacro has arguments make sure they
  179. // match the arguments of the macro
  180. if ((expandedArguments.empty() ||
  181. (expandedArguments[0] == this->Args[0]))) {
  182. return true;
  183. }
  184. }
  185. return false;
  186. }
  187. bool cmMacroCommand::InitialPass(std::vector<std::string> const& args,
  188. cmExecutionStatus&)
  189. {
  190. if (args.empty()) {
  191. this->SetError("called with incorrect number of arguments");
  192. return false;
  193. }
  194. // create a function blocker
  195. {
  196. auto fb = cm::make_unique<cmMacroFunctionBlocker>();
  197. cmAppend(fb->Args, args);
  198. this->Makefile->AddFunctionBlocker(std::move(fb));
  199. }
  200. return true;
  201. }