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"
9 #include <cm/string_view>
10 #include <cmext/algorithm>
11 #include <cmext/string_view>
13 #include "cmExecutionStatus.h"
14 #include "cmFunctionBlocker.h"
15 #include "cmListFileCache.h"
16 #include "cmMakefile.h"
17 #include "cmPolicies.h"
20 #include "cmStringAlgorithms.h"
21 #include "cmSystemTools.h"
25 // define the class for macro commands
26 class cmMacroHelperCommand
30 * This is called when the command is first encountered in
31 * the CMakeLists.txt file.
33 bool operator()(std::vector<cmListFileArgument> const& args,
34 cmExecutionStatus& inStatus) const;
36 std::vector<std::string> Args;
37 std::vector<cmListFileFunction> Functions;
38 cmPolicies::PolicyMap Policies;
42 bool cmMacroHelperCommand::operator()(
43 std::vector<cmListFileArgument> const& args,
44 cmExecutionStatus& inStatus) const
46 cmMakefile& makefile = inStatus.GetMakefile();
48 // Expand the argument list to the macro.
49 std::vector<std::string> expandedArgs;
50 makefile.ExpandArguments(args, expandedArgs);
52 // make sure the number of arguments passed is at least the number
53 // required by the signature
54 if (expandedArgs.size() < this->Args.size() - 1) {
55 std::string errorMsg =
56 cmStrCat("Macro invoked with incorrect arguments for macro named: ",
58 inStatus.SetError(errorMsg);
62 cmMakefile::MacroPushPop macroScope(&makefile, this->FilePath,
65 // set the value of argc
66 std::string argcDef = std::to_string(expandedArgs.size());
68 auto eit = expandedArgs.begin() + (this->Args.size() - 1);
69 std::string expandedArgn = cmJoin(cmMakeRange(eit, expandedArgs.end()), ";");
70 std::string expandedArgv = cmJoin(expandedArgs, ";");
71 std::vector<std::string> variables;
72 variables.reserve(this->Args.size() - 1);
73 for (unsigned int j = 1; j < this->Args.size(); ++j) {
74 variables.push_back("${" + this->Args[j] + "}");
76 std::vector<std::string> argVs;
77 argVs.reserve(expandedArgs.size());
79 for (unsigned int j = 0; j < expandedArgs.size(); ++j) {
80 snprintf(argvName, sizeof(argvName), "${ARGV%u}", j);
81 argVs.emplace_back(argvName);
83 // Invoke all the functions that were collected in the block.
85 for (cmListFileFunction const& func : this->Functions) {
86 // Replace the formal arguments and then invoke the command.
87 std::vector<cmListFileArgument> newLFFArgs;
88 newLFFArgs.reserve(func.Arguments().size());
90 // for each argument of the current function
91 for (cmListFileArgument const& k : func.Arguments()) {
92 cmListFileArgument arg;
94 if (k.Delim != cmListFileArgument::Bracket) {
95 // replace formal arguments
96 for (unsigned int j = 0; j < variables.size(); ++j) {
97 cmSystemTools::ReplaceString(arg.Value, variables[j],
101 cmSystemTools::ReplaceString(arg.Value, "${ARGC}", argcDef);
103 cmSystemTools::ReplaceString(arg.Value, "${ARGN}", expandedArgn);
104 cmSystemTools::ReplaceString(arg.Value, "${ARGV}", expandedArgv);
106 // if the current argument of the current function has ${ARGV in it
107 // then try replacing ARGV values
108 if (arg.Value.find("${ARGV") != std::string::npos) {
109 for (unsigned int t = 0; t < expandedArgs.size(); ++t) {
110 cmSystemTools::ReplaceString(arg.Value, argVs[t], expandedArgs[t]);
116 newLFFArgs.push_back(std::move(arg));
118 cmListFileFunction newLFF{ func.OriginalName(), func.Line(),
119 func.LineEnd(), std::move(newLFFArgs) };
120 cmExecutionStatus status(makefile);
121 if (!makefile.ExecuteCommand(newLFF, status) || status.GetNestedError()) {
122 // The error message should have already included the call stack
123 // so we do not need to report an error here.
125 inStatus.SetNestedError();
128 if (status.GetReturnInvoked()) {
129 inStatus.SetReturnInvoked();
132 if (status.GetBreakInvoked()) {
133 inStatus.SetBreakInvoked();
140 class cmMacroFunctionBlocker : public cmFunctionBlocker
143 cm::string_view StartCommandName() const override { return "macro"_s; }
144 cm::string_view EndCommandName() const override { return "endmacro"_s; }
146 bool ArgumentsMatch(cmListFileFunction const&,
147 cmMakefile& mf) const override;
149 bool Replay(std::vector<cmListFileFunction> functions,
150 cmExecutionStatus& status) override;
152 std::vector<std::string> Args;
155 bool cmMacroFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
156 cmMakefile& mf) const
158 std::vector<std::string> expandedArguments;
159 mf.ExpandArguments(lff.Arguments(), expandedArguments);
160 return expandedArguments.empty() || expandedArguments[0] == this->Args[0];
163 bool cmMacroFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
164 cmExecutionStatus& status)
166 cmMakefile& mf = status.GetMakefile();
167 mf.AppendProperty("MACROS", this->Args[0]);
168 // create a new command and add it to cmake
169 cmMacroHelperCommand f;
171 f.Functions = std::move(functions);
172 f.FilePath = this->GetStartingContext().FilePath;
173 mf.RecordPolicies(f.Policies);
174 return mf.GetState()->AddScriptedCommand(
176 BT<cmState::Command>(std::move(f),
177 mf.GetBacktrace().Push(this->GetStartingContext())),
182 bool cmMacroCommand(std::vector<std::string> const& args,
183 cmExecutionStatus& status)
186 status.SetError("called with incorrect number of arguments");
190 // create a function blocker
192 auto fb = cm::make_unique<cmMacroFunctionBlocker>();
193 cm::append(fb->Args, args);
194 status.GetMakefile().AddFunctionBlocker(std::move(fb));