Files
CMake/Source/cmForEachCommand.cxx
Daniel Pfeifer 1eebc29563 cmCommand: deprecate functions GetMakefile and SetError
Replace the members for the Makefile and the Error with a
cmExecutionStatus.  Re-implement GetMakefile and SetError based on that.

Both functions should be called directly on the cmExecutionStatus that is
passed to InitialPass.  This will help us make all Commands immutable and
remove the need for cloning.
2019-07-21 09:25:32 +02:00

221 lines
6.1 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmForEachCommand.h"
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <utility>
#include "cm_memory.hxx"
#include "cmExecutionStatus.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmRange.h"
#include "cmSystemTools.h"
cmForEachFunctionBlocker::cmForEachFunctionBlocker(cmMakefile* mf)
: Makefile(mf)
, Depth(0)
{
this->Makefile->PushLoopBlock();
}
cmForEachFunctionBlocker::~cmForEachFunctionBlocker()
{
this->Makefile->PopLoopBlock();
}
bool cmForEachFunctionBlocker::IsFunctionBlocked(const cmListFileFunction& lff,
cmMakefile& mf,
cmExecutionStatus& inStatus)
{
if (lff.Name.Lower == "foreach") {
// record the number of nested foreach commands
this->Depth++;
} else if (lff.Name.Lower == "endforeach") {
// if this is the endofreach for this statement
if (!this->Depth) {
// Remove the function blocker for this scope or bail.
std::unique_ptr<cmFunctionBlocker> fb(
mf.RemoveFunctionBlocker(this, lff));
if (!fb) {
return false;
}
// at end of for each execute recorded commands
// store the old value
std::string oldDef;
if (mf.GetDefinition(this->Args[0])) {
oldDef = mf.GetDefinition(this->Args[0]);
}
for (std::string const& arg : cmMakeRange(this->Args).advance(1)) {
// set the variable to the loop value
mf.AddDefinition(this->Args[0], arg.c_str());
// Invoke all the functions that were collected in the block.
cmExecutionStatus status(mf);
for (cmListFileFunction const& func : this->Functions) {
status.Clear();
mf.ExecuteCommand(func, status);
if (status.GetReturnInvoked()) {
inStatus.SetReturnInvoked();
// restore the variable to its prior value
mf.AddDefinition(this->Args[0], oldDef.c_str());
return true;
}
if (status.GetBreakInvoked()) {
// restore the variable to its prior value
mf.AddDefinition(this->Args[0], oldDef.c_str());
return true;
}
if (status.GetContinueInvoked()) {
break;
}
if (cmSystemTools::GetFatalErrorOccured()) {
return true;
}
}
}
// restore the variable to its prior value
mf.AddDefinition(this->Args[0], oldDef.c_str());
return true;
}
// close out a nested foreach
this->Depth--;
}
// record the command
this->Functions.push_back(lff);
// always return true
return true;
}
bool cmForEachFunctionBlocker::ShouldRemove(const cmListFileFunction& lff,
cmMakefile& mf)
{
if (lff.Name.Lower == "endforeach") {
std::vector<std::string> expandedArguments;
mf.ExpandArguments(lff.Arguments, expandedArguments);
// if the endforeach has arguments then make sure
// they match the begin foreach arguments
if ((expandedArguments.empty() ||
(expandedArguments[0] == this->Args[0]))) {
return true;
}
}
return false;
}
bool cmForEachCommand::InitialPass(std::vector<std::string> const& args,
cmExecutionStatus&)
{
if (args.empty()) {
this->SetError("called with incorrect number of arguments");
return false;
}
if (args.size() > 1 && args[1] == "IN") {
return this->HandleInMode(args);
}
// create a function blocker
auto fb = cm::make_unique<cmForEachFunctionBlocker>(this->Makefile);
if (args.size() > 1) {
if (args[1] == "RANGE") {
int start = 0;
int stop = 0;
int step = 0;
if (args.size() == 3) {
stop = atoi(args[2].c_str());
}
if (args.size() == 4) {
start = atoi(args[2].c_str());
stop = atoi(args[3].c_str());
}
if (args.size() == 5) {
start = atoi(args[2].c_str());
stop = atoi(args[3].c_str());
step = atoi(args[4].c_str());
}
if (step == 0) {
if (start > stop) {
step = -1;
} else {
step = 1;
}
}
if ((start > stop && step > 0) || (start < stop && step < 0) ||
step == 0) {
std::ostringstream str;
str << "called with incorrect range specification: start ";
str << start << ", stop " << stop << ", step " << step;
this->SetError(str.str());
return false;
}
std::vector<std::string> range;
char buffer[100];
range.push_back(args[0]);
int cc;
for (cc = start;; cc += step) {
if ((step > 0 && cc > stop) || (step < 0 && cc < stop)) {
break;
}
sprintf(buffer, "%d", cc);
range.emplace_back(buffer);
if (cc == stop) {
break;
}
}
fb->Args = range;
} else {
fb->Args = args;
}
} else {
fb->Args = args;
}
this->Makefile->AddFunctionBlocker(std::move(fb));
return true;
}
bool cmForEachCommand::HandleInMode(std::vector<std::string> const& args)
{
auto fb = cm::make_unique<cmForEachFunctionBlocker>(this->Makefile);
fb->Args.push_back(args[0]);
enum Doing
{
DoingNone,
DoingLists,
DoingItems
};
Doing doing = DoingNone;
for (unsigned int i = 2; i < args.size(); ++i) {
if (doing == DoingItems) {
fb->Args.push_back(args[i]);
} else if (args[i] == "LISTS") {
doing = DoingLists;
} else if (args[i] == "ITEMS") {
doing = DoingItems;
} else if (doing == DoingLists) {
const char* value = this->Makefile->GetDefinition(args[i]);
if (value && *value) {
cmSystemTools::ExpandListArgument(value, fb->Args, true);
}
} else {
std::ostringstream e;
e << "Unknown argument:\n"
<< " " << args[i] << "\n";
this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
return true;
}
}
this->Makefile->AddFunctionBlocker(std::move(fb));
return true;
}