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 "cmFunctionCommand.h"
8 #include <cm/string_view>
9 #include <cmext/algorithm>
10 #include <cmext/string_view>
12 #include "cmExecutionStatus.h"
13 #include "cmFunctionBlocker.h"
14 #include "cmListFileCache.h"
15 #include "cmMakefile.h"
16 #include "cmPolicies.h"
19 #include "cmStringAlgorithms.h"
20 #include "cmSystemTools.h"
23 std::string const ARGC = "ARGC";
24 std::string const ARGN = "ARGN";
25 std::string const ARGV = "ARGV";
26 std::string const CMAKE_CURRENT_FUNCTION = "CMAKE_CURRENT_FUNCTION";
27 std::string const CMAKE_CURRENT_FUNCTION_LIST_FILE =
28 "CMAKE_CURRENT_FUNCTION_LIST_FILE";
29 std::string const CMAKE_CURRENT_FUNCTION_LIST_DIR =
30 "CMAKE_CURRENT_FUNCTION_LIST_DIR";
31 std::string const CMAKE_CURRENT_FUNCTION_LIST_LINE =
32 "CMAKE_CURRENT_FUNCTION_LIST_LINE";
34 // define the class for function commands
35 class cmFunctionHelperCommand
39 * This is called when the command is first encountered in
40 * the CMakeLists.txt file.
42 bool operator()(std::vector<cmListFileArgument> const& args,
43 cmExecutionStatus& inStatus) const;
45 std::vector<std::string> Args;
46 std::vector<cmListFileFunction> Functions;
47 cmPolicies::PolicyMap Policies;
52 bool cmFunctionHelperCommand::operator()(
53 std::vector<cmListFileArgument> const& args,
54 cmExecutionStatus& inStatus) const
56 cmMakefile& makefile = inStatus.GetMakefile();
58 // Expand the argument list to the function.
59 std::vector<std::string> expandedArgs;
60 makefile.ExpandArguments(args, expandedArgs);
62 // make sure the number of arguments passed is at least the number
63 // required by the signature
64 if (expandedArgs.size() < this->Args.size() - 1) {
65 auto const errorMsg = cmStrCat(
66 "Function invoked with incorrect arguments for function named: ",
68 inStatus.SetError(errorMsg);
72 cmMakefile::FunctionPushPop functionScope(&makefile, this->FilePath,
75 // set the value of argc
76 makefile.AddDefinition(ARGC, std::to_string(expandedArgs.size()));
77 makefile.MarkVariableAsUsed(ARGC);
79 // set the values for ARGV0 ARGV1 ...
80 for (auto t = 0u; t < expandedArgs.size(); ++t) {
81 auto const value = cmStrCat(ARGV, std::to_string(t));
82 makefile.AddDefinition(value, expandedArgs[t]);
83 makefile.MarkVariableAsUsed(value);
86 // define the formal arguments
87 for (auto j = 1u; j < this->Args.size(); ++j) {
88 makefile.AddDefinition(this->Args[j], expandedArgs[j - 1]);
91 // define ARGV and ARGN
92 auto const argvDef = cmJoin(expandedArgs, ";");
93 auto const eit = expandedArgs.begin() + (this->Args.size() - 1);
94 auto const argnDef = cmJoin(cmMakeRange(eit, expandedArgs.end()), ";");
95 makefile.AddDefinition(ARGV, argvDef);
96 makefile.MarkVariableAsUsed(ARGV);
97 makefile.AddDefinition(ARGN, argnDef);
98 makefile.MarkVariableAsUsed(ARGN);
100 makefile.AddDefinition(CMAKE_CURRENT_FUNCTION, this->Args.front());
101 makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION);
102 makefile.AddDefinition(CMAKE_CURRENT_FUNCTION_LIST_FILE, this->FilePath);
103 makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION_LIST_FILE);
104 makefile.AddDefinition(CMAKE_CURRENT_FUNCTION_LIST_DIR,
105 cmSystemTools::GetFilenamePath(this->FilePath));
106 makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION_LIST_DIR);
107 makefile.AddDefinition(CMAKE_CURRENT_FUNCTION_LIST_LINE,
108 std::to_string(this->Line));
109 makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION_LIST_LINE);
111 // Invoke all the functions that were collected in the block.
113 for (cmListFileFunction const& func : this->Functions) {
114 cmExecutionStatus status(makefile);
115 if (!makefile.ExecuteCommand(func, status) || status.GetNestedError()) {
116 // The error message should have already included the call stack
117 // so we do not need to report an error here.
118 functionScope.Quiet();
119 inStatus.SetNestedError();
122 if (status.GetReturnInvoked()) {
123 makefile.RaiseScope(status.GetReturnVariables());
128 // pop scope on the makefile
132 class cmFunctionFunctionBlocker : public cmFunctionBlocker
135 cm::string_view StartCommandName() const override { return "function"_s; }
136 cm::string_view EndCommandName() const override { return "endfunction"_s; }
138 bool ArgumentsMatch(cmListFileFunction const&,
139 cmMakefile& mf) const override;
141 bool Replay(std::vector<cmListFileFunction> functions,
142 cmExecutionStatus& status) override;
144 std::vector<std::string> Args;
147 bool cmFunctionFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
148 cmMakefile& mf) const
150 std::vector<std::string> expandedArguments;
151 mf.ExpandArguments(lff.Arguments(), expandedArguments);
152 return expandedArguments.empty() ||
153 expandedArguments.front() == this->Args.front();
156 bool cmFunctionFunctionBlocker::Replay(
157 std::vector<cmListFileFunction> functions, cmExecutionStatus& status)
159 cmMakefile& mf = status.GetMakefile();
160 // create a new command and add it to cmake
161 cmFunctionHelperCommand f;
163 f.Functions = std::move(functions);
164 f.FilePath = this->GetStartingContext().FilePath;
165 f.Line = this->GetStartingContext().Line;
166 mf.RecordPolicies(f.Policies);
167 return mf.GetState()->AddScriptedCommand(
169 BT<cmState::Command>(std::move(f),
170 mf.GetBacktrace().Push(this->GetStartingContext())),
174 } // anonymous namespace
176 bool cmFunctionCommand(std::vector<std::string> const& args,
177 cmExecutionStatus& status)
180 status.SetError("called with incorrect number of arguments");
184 // create a function blocker
185 auto fb = cm::make_unique<cmFunctionFunctionBlocker>();
186 cm::append(fb->Args, args);
187 status.GetMakefile().AddFunctionBlocker(std::move(fb));