ENH: Refactor logical block enforcement

This uses a stack of 'barriers' to efficiently divide function blockers
into groups corresponding to each input file.  It simplifies detection
of missing block close commands and factors it out of ReadListFile.
This commit is contained in:
Brad King 2009-01-21 09:48:00 -05:00
parent f4d37eebb2
commit b8f5a934ec
2 changed files with 86 additions and 40 deletions

View File

@ -206,7 +206,7 @@ cmMakefile::~cmMakefile()
delete d->second;
}
}
std::list<cmFunctionBlocker *>::iterator pos;
std::vector<cmFunctionBlocker*>::iterator pos;
for (pos = this->FunctionBlockers.begin();
pos != this->FunctionBlockers.end(); ++pos)
{
@ -453,10 +453,6 @@ bool cmMakefile::ReadListFile(const char* filename_in,
= this->GetSafeDefinition("CMAKE_CURRENT_LIST_FILE");
this->AddDefinition("CMAKE_PARENT_LIST_FILE", filename_in);
// used to watch for blockers going out of scope
// e.g. mismatched IF statement
std::set<cmFunctionBlocker *> originalBlockers;
const char* external = 0;
std::string external_abs;
@ -487,14 +483,6 @@ bool cmMakefile::ReadListFile(const char* filename_in,
}
}
// loop over current function blockers and record them
for (std::list<cmFunctionBlocker *>::iterator pos
= this->FunctionBlockers.begin();
pos != this->FunctionBlockers.end(); ++pos)
{
originalBlockers.insert(*pos);
}
// Now read the input file
const char *filenametoread= filename;
@ -541,6 +529,10 @@ bool cmMakefile::ReadListFile(const char* filename_in,
}
// add this list file to the list of dependencies
this->ListFiles.push_back( filenametoread);
// Enforce balanced blocks (if/endif, function/endfunction, etc.).
{
LexicalPushPop lexScope(this);
bool endScopeNicely = true;
// Save the current policy stack depth.
@ -552,11 +544,16 @@ bool cmMakefile::ReadListFile(const char* filename_in,
{
cmExecutionStatus status;
this->ExecuteCommand(cacheFile.Functions[i],status);
if (status.GetReturnInvoked() ||
cmSystemTools::GetFatalErrorOccured() )
if(cmSystemTools::GetFatalErrorOccured())
{
// Exit early from processing this file.
// Exit early due to error.
endScopeNicely = false;
lexScope.Quiet();
break;
}
if(status.GetReturnInvoked())
{
// Exit early due to return command.
break;
}
}
@ -571,23 +568,7 @@ bool cmMakefile::ReadListFile(const char* filename_in,
}
this->PopPolicy(false);
}
// send scope ended to and function blockers
if (endScopeNicely)
{
// loop over all function blockers to see if any block this command
for (std::list<cmFunctionBlocker *>::iterator pos
= this->FunctionBlockers.begin();
pos != this->FunctionBlockers.end(); ++pos)
{
// if this blocker was not in the original then send a
// scope ended message
if (originalBlockers.find(*pos) == originalBlockers.end())
{
(*pos)->ScopeEnded(*this);
}
}
}
}
// If this is the directory-level CMakeLists.txt file then perform
// some extra checks.
@ -2353,7 +2334,7 @@ bool cmMakefile::IsFunctionBlocked(const cmListFileFunction& lff,
// loop over all function blockers to see if any block this command
// evaluate in reverse, this is critical for balanced IF statements etc
std::list<cmFunctionBlocker *>::reverse_iterator pos;
std::vector<cmFunctionBlocker*>::reverse_iterator pos;
for (pos = this->FunctionBlockers.rbegin();
pos != this->FunctionBlockers.rend(); ++pos)
{
@ -2366,6 +2347,32 @@ bool cmMakefile::IsFunctionBlocked(const cmListFileFunction& lff,
return false;
}
//----------------------------------------------------------------------------
void cmMakefile::PushFunctionBlockerBarrier()
{
this->FunctionBlockerBarriers.push_back(this->FunctionBlockers.size());
}
//----------------------------------------------------------------------------
void cmMakefile::PopFunctionBlockerBarrier(bool reportError)
{
// Remove any extra entries pushed on the barrier.
FunctionBlockersType::size_type barrier =
this->FunctionBlockerBarriers.back();
while(this->FunctionBlockers.size() > barrier)
{
cmsys::auto_ptr<cmFunctionBlocker> fb(this->FunctionBlockers.back());
this->FunctionBlockers.pop_back();
if(reportError)
{
fb->ScopeEnded(*this);
}
}
// Remove the barrier.
this->FunctionBlockerBarriers.pop_back();
}
bool cmMakefile::ExpandArguments(
std::vector<cmListFileArgument> const& inArgs,
std::vector<std::string>& outArgs)
@ -2398,15 +2405,24 @@ bool cmMakefile::ExpandArguments(
cmsys::auto_ptr<cmFunctionBlocker>
cmMakefile::RemoveFunctionBlocker(const cmListFileFunction& lff)
{
// loop over all function blockers to see if any block this command
std::list<cmFunctionBlocker *>::reverse_iterator pos;
for (pos = this->FunctionBlockers.rbegin();
pos != this->FunctionBlockers.rend(); ++pos)
// Find the function blocker stack barrier for the current scope.
// We only remove a blocker whose index is not less than the barrier.
FunctionBlockersType::size_type barrier = 0;
if(!this->FunctionBlockerBarriers.empty())
{
barrier = this->FunctionBlockerBarriers.back();
}
// Search for the function blocker whose scope this command ends.
for(FunctionBlockersType::size_type
i = this->FunctionBlockers.size(); i > barrier; --i)
{
std::vector<cmFunctionBlocker*>::iterator pos =
this->FunctionBlockers.begin() + (i - 1);
if ((*pos)->ShouldRemove(lff, *this))
{
cmFunctionBlocker* b = *pos;
this->FunctionBlockers.remove(b);
this->FunctionBlockers.erase(pos);
return cmsys::auto_ptr<cmFunctionBlocker>(b);
}
}
@ -2414,6 +2430,19 @@ cmMakefile::RemoveFunctionBlocker(const cmListFileFunction& lff)
return cmsys::auto_ptr<cmFunctionBlocker>();
}
//----------------------------------------------------------------------------
cmMakefile::LexicalPushPop::LexicalPushPop(cmMakefile* mf):
Makefile(mf), ReportError(true)
{
this->Makefile->PushFunctionBlockerBarrier();
}
//----------------------------------------------------------------------------
cmMakefile::LexicalPushPop::~LexicalPushPop()
{
this->Makefile->PopFunctionBlockerBarrier(this->ReportError);
}
void cmMakefile::SetHomeDirectory(const char* dir)
{
this->cmHomeDirectory = dir;

View File

@ -98,6 +98,19 @@ public:
cmsys::auto_ptr<cmFunctionBlocker>
RemoveFunctionBlocker(const cmListFileFunction& lff);
/** Push/pop a lexical (function blocker) barrier automatically. */
class LexicalPushPop
{
public:
LexicalPushPop(cmMakefile* mf);
~LexicalPushPop();
void Quiet() { this->ReportError = false; }
private:
cmMakefile* Makefile;
bool ReportError;
};
friend class LexicalPushPop;
/**
* Try running cmake and building a file. This is used for dynalically
* loaded commands, not as part of the usual build process.
@ -876,7 +889,11 @@ private:
const std::vector<std::string>& v) const;
void AddDefaultDefinitions();
std::list<cmFunctionBlocker *> FunctionBlockers;
typedef std::vector<cmFunctionBlocker*> FunctionBlockersType;
FunctionBlockersType FunctionBlockers;
std::vector<FunctionBlockersType::size_type> FunctionBlockerBarriers;
void PushFunctionBlockerBarrier();
void PopFunctionBlockerBarrier(bool reportError = true);
typedef std::map<cmStdString, cmData*> DataMapType;
DataMapType DataMap;