mirror of
https://github.com/Kitware/CMake.git
synced 2025-12-31 10:50:16 -06:00
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.
221 lines
6.1 KiB
C++
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;
|
|
}
|