CMake/Source/cmWhileCommand.cxx
Brad King e456dae669 cmConditionEvaluator: Remove extra copy of execution context
The execution context passed to the constructor always matches the top
of the backtrace, so the former can be removed in favor of using only
the latter.
2020-09-28 09:49:08 -04:00

139 lines
3.8 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmWhileCommand.h"
#include <string>
#include <utility>
#include <cm/memory>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmConditionEvaluator.h"
#include "cmExecutionStatus.h"
#include "cmExpandedCommandArgument.h"
#include "cmFunctionBlocker.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmSystemTools.h"
#include "cmake.h"
class cmWhileFunctionBlocker : public cmFunctionBlocker
{
public:
cmWhileFunctionBlocker(cmMakefile* mf);
~cmWhileFunctionBlocker() override;
cm::string_view StartCommandName() const override { return "while"_s; }
cm::string_view EndCommandName() const override { return "endwhile"_s; }
bool ArgumentsMatch(cmListFileFunction const& lff,
cmMakefile& mf) const override;
bool Replay(std::vector<cmListFileFunction> functions,
cmExecutionStatus& inStatus) override;
std::vector<cmListFileArgument> Args;
private:
cmMakefile* Makefile;
};
cmWhileFunctionBlocker::cmWhileFunctionBlocker(cmMakefile* mf)
: Makefile(mf)
{
this->Makefile->PushLoopBlock();
}
cmWhileFunctionBlocker::~cmWhileFunctionBlocker()
{
this->Makefile->PopLoopBlock();
}
bool cmWhileFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
cmMakefile&) const
{
return lff.Arguments.empty() || lff.Arguments == this->Args;
}
bool cmWhileFunctionBlocker::Replay(std::vector<cmListFileFunction> functions,
cmExecutionStatus& inStatus)
{
cmMakefile& mf = inStatus.GetMakefile();
std::string errorString;
std::vector<cmExpandedCommandArgument> expandedArguments;
mf.ExpandArguments(this->Args, expandedArguments);
MessageType messageType;
cmListFileBacktrace whileBT =
mf.GetBacktrace().Push(this->GetStartingContext());
cmConditionEvaluator conditionEvaluator(mf, whileBT);
bool isTrue =
conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
while (isTrue) {
if (!errorString.empty()) {
std::string err = "had incorrect arguments: ";
for (cmListFileArgument const& arg : this->Args) {
err += (arg.Delim ? "\"" : "");
err += arg.Value;
err += (arg.Delim ? "\"" : "");
err += " ";
}
err += "(";
err += errorString;
err += ").";
mf.GetCMakeInstance()->IssueMessage(messageType, err, whileBT);
if (messageType == MessageType::FATAL_ERROR) {
cmSystemTools::SetFatalErrorOccured();
return true;
}
}
// Invoke all the functions that were collected in the block.
for (cmListFileFunction const& fn : functions) {
cmExecutionStatus status(mf);
mf.ExecuteCommand(fn, status);
if (status.GetReturnInvoked()) {
inStatus.SetReturnInvoked();
return true;
}
if (status.GetBreakInvoked()) {
return true;
}
if (status.GetContinueInvoked()) {
break;
}
if (cmSystemTools::GetFatalErrorOccured()) {
return true;
}
}
expandedArguments.clear();
mf.ExpandArguments(this->Args, expandedArguments);
isTrue =
conditionEvaluator.IsTrue(expandedArguments, errorString, messageType);
}
return true;
}
bool cmWhileCommand(std::vector<cmListFileArgument> const& args,
cmExecutionStatus& status)
{
if (args.empty()) {
status.SetError("called with incorrect number of arguments");
return false;
}
// create a function blocker
{
cmMakefile& makefile = status.GetMakefile();
auto fb = cm::make_unique<cmWhileFunctionBlocker>(&makefile);
fb->Args = args;
makefile.AddFunctionBlocker(std::move(fb));
}
return true;
}