blob: 8d2d9725ea17282c50eb819a59ae0394a02826f2 [file] [log] [blame]
Brad King86578ec2016-09-27 19:01:081/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
Ken Martin95144412007-12-03 17:44:423#include "cmFunctionCommand.h"
4
Marc Chevrier1591f132019-07-04 16:14:225#include <utility>
6
Marc Chevrierc688b402019-08-04 08:49:167#include <cm/memory>
8#include <cm/string_view>
Marc Chevrierf7d12602019-12-09 16:39:299#include <cmext/algorithm>
Marc Chevrier8d4a9ee2020-04-27 14:38:1410#include <cmext/string_view>
Regina Pfeiferaf24e4e2019-07-30 16:15:1311
Daniel Pfeifere81c3232016-10-25 18:35:0412#include "cmExecutionStatus.h"
Regina Pfeiferc7650092019-07-23 21:00:2813#include "cmFunctionBlocker.h"
Marc Chevrier45f17e52023-06-20 14:32:2714#include "cmList.h"
Regina Pfeiferc7650092019-07-23 21:00:2815#include "cmListFileCache.h"
Daniel Pfeifere81c3232016-10-25 18:35:0416#include "cmMakefile.h"
17#include "cmPolicies.h"
Regina Pfeifer9eb0e732019-02-15 20:34:4418#include "cmRange.h"
Daniel Pfeifer64f9c282016-10-19 19:59:1419#include "cmState.h"
Sebastian Holtermannf71f7ce2019-07-29 10:16:4020#include "cmStringAlgorithms.h"
Alex Turbov90e3e2a2019-12-08 00:26:1421#include "cmSystemTools.h"
Ken Martin95144412007-12-03 17:44:4222
Gabor Bencze9bbe95a2019-07-25 17:07:0023namespace {
Alex Turbovdd542902019-11-05 21:15:5424std::string const ARGC = "ARGC";
25std::string const ARGN = "ARGN";
26std::string const ARGV = "ARGV";
Alex Turbov90e3e2a2019-12-08 00:26:1427std::string const CMAKE_CURRENT_FUNCTION = "CMAKE_CURRENT_FUNCTION";
28std::string const CMAKE_CURRENT_FUNCTION_LIST_FILE =
29 "CMAKE_CURRENT_FUNCTION_LIST_FILE";
30std::string const CMAKE_CURRENT_FUNCTION_LIST_DIR =
31 "CMAKE_CURRENT_FUNCTION_LIST_DIR";
32std::string const CMAKE_CURRENT_FUNCTION_LIST_LINE =
33 "CMAKE_CURRENT_FUNCTION_LIST_LINE";
Alex Turbovdd542902019-11-05 21:15:5434
Ken Martin95144412007-12-03 17:44:4235// define the class for function commands
Regina Pfeiferde77d352019-04-07 19:27:4136class cmFunctionHelperCommand
Ken Martin95144412007-12-03 17:44:4237{
38public:
Ken Martin95144412007-12-03 17:44:4239 /**
Ken Martin95144412007-12-03 17:44:4240 * This is called when the command is first encountered in
41 * the CMakeLists.txt file.
42 */
Regina Pfeiferde77d352019-04-07 19:27:4143 bool operator()(std::vector<cmListFileArgument> const& args,
44 cmExecutionStatus& inStatus) const;
Ken Martin95144412007-12-03 17:44:4245
Ken Martin95144412007-12-03 17:44:4246 std::vector<std::string> Args;
47 std::vector<cmListFileFunction> Functions;
Brad King3028ca72009-01-22 18:16:4748 cmPolicies::PolicyMap Policies;
Stephen Kelly569f4782015-05-23 20:21:0849 std::string FilePath;
Alex Turbov90e3e2a2019-12-08 00:26:1450 long Line;
Ken Martin95144412007-12-03 17:44:4251};
52
Regina Pfeiferde77d352019-04-07 19:27:4153bool cmFunctionHelperCommand::operator()(
54 std::vector<cmListFileArgument> const& args,
55 cmExecutionStatus& inStatus) const
Ken Martin95144412007-12-03 17:44:4256{
Regina Pfeiferde77d352019-04-07 19:27:4157 cmMakefile& makefile = inStatus.GetMakefile();
58
Ken Martin95144412007-12-03 17:44:4259 // Expand the argument list to the function.
60 std::vector<std::string> expandedArgs;
Regina Pfeiferde77d352019-04-07 19:27:4161 makefile.ExpandArguments(args, expandedArgs);
Ken Martin95144412007-12-03 17:44:4262
63 // make sure the number of arguments passed is at least the number
64 // required by the signature
Kitware Robotd9fd2f52016-05-16 14:34:0465 if (expandedArgs.size() < this->Args.size() - 1) {
Alex Turbovdd542902019-11-05 21:15:5466 auto const errorMsg = cmStrCat(
Sebastian Holtermann9b334392019-08-22 14:34:4067 "Function invoked with incorrect arguments for function named: ",
Alex Turbovdd542902019-11-05 21:15:5468 this->Args.front());
Regina Pfeiferde77d352019-04-07 19:27:4169 inStatus.SetError(errorMsg);
Ken Martin95144412007-12-03 17:44:4270 return false;
Kitware Robotd9fd2f52016-05-16 14:34:0471 }
Ken Martin95144412007-12-03 17:44:4272
Regina Pfeiferde77d352019-04-07 19:27:4173 cmMakefile::FunctionPushPop functionScope(&makefile, this->FilePath,
Stephen Kellyd5dc4162015-05-31 16:19:5874 this->Policies);
Brad King3028ca72009-01-22 18:16:4775
Ken Martin95144412007-12-03 17:44:4276 // set the value of argc
Alex Turbovdd542902019-11-05 21:15:5477 makefile.AddDefinition(ARGC, std::to_string(expandedArgs.size()));
78 makefile.MarkVariableAsUsed(ARGC);
Ken Martin95144412007-12-03 17:44:4279
80 // set the values for ARGV0 ARGV1 ...
Alex Turbovdd542902019-11-05 21:15:5481 for (auto t = 0u; t < expandedArgs.size(); ++t) {
82 auto const value = cmStrCat(ARGV, std::to_string(t));
83 makefile.AddDefinition(value, expandedArgs[t]);
84 makefile.MarkVariableAsUsed(value);
Kitware Robotd9fd2f52016-05-16 14:34:0485 }
Kitware Robot7bbaa422012-08-13 17:42:5886
Ken Martin95144412007-12-03 17:44:4287 // define the formal arguments
Alex Turbovdd542902019-11-05 21:15:5488 for (auto j = 1u; j < this->Args.size(); ++j) {
Sebastian Holtermanne91bfe42019-07-17 14:20:5889 makefile.AddDefinition(this->Args[j], expandedArgs[j - 1]);
Kitware Robotd9fd2f52016-05-16 14:34:0490 }
Ken Martin95144412007-12-03 17:44:4291
92 // define ARGV and ARGN
Marc Chevrier45f17e52023-06-20 14:32:2793 auto const argvDef = cmList::to_string(expandedArgs);
Alex Turbovdd542902019-11-05 21:15:5494 auto const eit = expandedArgs.begin() + (this->Args.size() - 1);
Marc Chevrier45f17e52023-06-20 14:32:2795 auto const argnDef = cmList::to_string(cmMakeRange(eit, expandedArgs.end()));
Alex Turbovdd542902019-11-05 21:15:5496 makefile.AddDefinition(ARGV, argvDef);
97 makefile.MarkVariableAsUsed(ARGV);
98 makefile.AddDefinition(ARGN, argnDef);
99 makefile.MarkVariableAsUsed(ARGN);
Ken Martin95144412007-12-03 17:44:42100
Alex Turbov90e3e2a2019-12-08 00:26:14101 makefile.AddDefinition(CMAKE_CURRENT_FUNCTION, this->Args.front());
102 makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION);
103 makefile.AddDefinition(CMAKE_CURRENT_FUNCTION_LIST_FILE, this->FilePath);
104 makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION_LIST_FILE);
105 makefile.AddDefinition(CMAKE_CURRENT_FUNCTION_LIST_DIR,
106 cmSystemTools::GetFilenamePath(this->FilePath));
107 makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION_LIST_DIR);
108 makefile.AddDefinition(CMAKE_CURRENT_FUNCTION_LIST_LINE,
109 std::to_string(this->Line));
110 makefile.MarkVariableAsUsed(CMAKE_CURRENT_FUNCTION_LIST_LINE);
111
Ken Martin95144412007-12-03 17:44:42112 // Invoke all the functions that were collected in the block.
Ken Martin95144412007-12-03 17:44:42113 // for each function
Pavel Solodovnikov7d509572017-09-11 10:40:26114 for (cmListFileFunction const& func : this->Functions) {
Regina Pfeiferde77d352019-04-07 19:27:41115 cmExecutionStatus status(makefile);
116 if (!makefile.ExecuteCommand(func, status) || status.GetNestedError()) {
Brad King680104a2008-03-07 13:40:36117 // The error message should have already included the call stack
118 // so we do not need to report an error here.
Stephen Kellyd5dc4162015-05-31 16:19:58119 functionScope.Quiet();
Daniel Pfeifer67a8d902016-12-26 09:30:31120 inStatus.SetNestedError();
Ken Martin95144412007-12-03 17:44:42121 return false;
Ken Martin95144412007-12-03 17:44:42122 }
Kitware Robotd9fd2f52016-05-16 14:34:04123 if (status.GetReturnInvoked()) {
Marc Chevrier838a5fa2022-08-22 14:10:56124 makefile.RaiseScope(status.GetReturnVariables());
Alex Turbovdd542902019-11-05 21:15:54125 break;
Kitware Robotd9fd2f52016-05-16 14:34:04126 }
127 }
Ken Martin95144412007-12-03 17:44:42128
129 // pop scope on the makefile
Ken Martin95144412007-12-03 17:44:42130 return true;
131}
132
Regina Pfeiferc7650092019-07-23 21:00:28133class cmFunctionFunctionBlocker : public cmFunctionBlocker
134{
135public:
Regina Pfeiferaf24e4e2019-07-30 16:15:13136 cm::string_view StartCommandName() const override { return "function"_s; }
137 cm::string_view EndCommandName() const override { return "endfunction"_s; }
138
Regina Pfeifer64912702019-07-30 20:58:40139 bool ArgumentsMatch(cmListFileFunction const&,
140 cmMakefile& mf) const override;
141
Regina Pfeifer41364822019-07-30 21:54:12142 bool Replay(std::vector<cmListFileFunction> functions,
Regina Pfeiferaf24e4e2019-07-30 16:15:13143 cmExecutionStatus& status) override;
Regina Pfeiferc7650092019-07-23 21:00:28144
145 std::vector<std::string> Args;
Regina Pfeiferc7650092019-07-23 21:00:28146};
147
Regina Pfeifer64912702019-07-30 20:58:40148bool cmFunctionFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
149 cmMakefile& mf) const
Ken Martin95144412007-12-03 17:44:42150{
Regina Pfeifer64912702019-07-30 20:58:40151 std::vector<std::string> expandedArguments;
Oleksandr Kovale6145282020-10-01 11:28:03152 mf.ExpandArguments(lff.Arguments(), expandedArguments);
Alex Turbovdd542902019-11-05 21:15:54153 return expandedArguments.empty() ||
154 expandedArguments.front() == this->Args.front();
Ken Martin95144412007-12-03 17:44:42155}
156
Regina Pfeiferef38ff22019-07-23 21:50:33157bool cmFunctionFunctionBlocker::Replay(
Regina Pfeifer41364822019-07-30 21:54:12158 std::vector<cmListFileFunction> functions, cmExecutionStatus& status)
Regina Pfeiferef38ff22019-07-23 21:50:33159{
160 cmMakefile& mf = status.GetMakefile();
161 // create a new command and add it to cmake
162 cmFunctionHelperCommand f;
163 f.Args = this->Args;
Regina Pfeifer41364822019-07-30 21:54:12164 f.Functions = std::move(functions);
Regina Pfeiferef38ff22019-07-23 21:50:33165 f.FilePath = this->GetStartingContext().FilePath;
Alex Turbov90e3e2a2019-12-08 00:26:14166 f.Line = this->GetStartingContext().Line;
Regina Pfeiferef38ff22019-07-23 21:50:33167 mf.RecordPolicies(f.Policies);
Kyle Edwards8aee7fd2020-10-22 20:50:42168 return mf.GetState()->AddScriptedCommand(
169 this->Args.front(),
170 BT<cmState::Command>(std::move(f),
171 mf.GetBacktrace().Push(this->GetStartingContext())),
172 mf);
Regina Pfeiferef38ff22019-07-23 21:50:33173}
174
Alex Turbovdd542902019-11-05 21:15:54175} // anonymous namespace
176
Gabor Bencze9bbe95a2019-07-25 17:07:00177bool cmFunctionCommand(std::vector<std::string> const& args,
178 cmExecutionStatus& status)
Ken Martin95144412007-12-03 17:44:42179{
Daniel Pfeifer73f648f2016-09-15 21:59:29180 if (args.empty()) {
Gabor Bencze9bbe95a2019-07-25 17:07:00181 status.SetError("called with incorrect number of arguments");
Ken Martin95144412007-12-03 17:44:42182 return false;
Kitware Robotd9fd2f52016-05-16 14:34:04183 }
Ken Martin95144412007-12-03 17:44:42184
185 // create a function blocker
Alex Turbovdd542902019-11-05 21:15:54186 auto fb = cm::make_unique<cmFunctionFunctionBlocker>();
Marc Chevrierf7d12602019-12-09 16:39:29187 cm::append(fb->Args, args);
Alex Turbovdd542902019-11-05 21:15:54188 status.GetMakefile().AddFunctionBlocker(std::move(fb));
189
Ken Martin95144412007-12-03 17:44:42190 return true;
191}