ENH: add framework for unified handling of arguments to cmake commands,

example see cmExportCommand.cxx

Alex
This commit is contained in:
Alexander Neundorf 2007-07-02 15:43:21 -04:00
parent 7f29f8966d
commit 2e56fa4808
6 changed files with 580 additions and 109 deletions

View File

@ -29,6 +29,7 @@
#include "cmAddTestCommand.cxx"
#include "cmBuildCommand.cxx"
#include "cmCMakeMinimumRequired.cxx"
#include "cmCommandArgumentsHelper.cxx"
#include "cmConfigureFileCommand.cxx"
#include "cmCoreTryCompile.cxx"
#include "cmCreateTestSourceList.cxx"

View File

@ -20,6 +20,7 @@
#include "cmObject.h"
#include "cmListFileCache.h"
#include "cmMakefile.h"
#include "cmCommandArgumentsHelper.h"
/** \class cmCommand
* \brief Superclass for all commands in CMake.
@ -174,6 +175,7 @@ public:
protected:
cmMakefile* Makefile;
cmCommandArgumentsHelper Helper;
private:
bool Enabled;

View File

@ -0,0 +1,308 @@
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#include "cmCommandArgumentsHelper.h"
cmCommandArgument::cmCommandArgument(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group)
:Key(key)
,Group(group)
,WasActive(false)
,CurrentIndex(0)
{
if (args!=0)
{
args->AddArgument(this);
}
if (this->Group!=0)
{
this->Group->ContainedArguments.push_back(this);
}
}
void cmCommandArgument::Reset()
{
this->WasActive =false;
this->CurrentIndex = 0;
this->DoReset();
}
void cmCommandArgument::Follows(const cmCommandArgument* arg)
{
this->ArgumentsBefore.insert(arg);
}
void cmCommandArgument::FollowsGroup(const cmCommandArgumentGroup* group)
{
if (group!=0)
{
for(std::vector<cmCommandArgument*>::const_iterator
argIt= group->ContainedArguments.begin();
argIt != group->ContainedArguments.end();
++argIt)
{
this->ArgumentsBefore.insert(*argIt);
}
}
}
bool cmCommandArgument::MayFollow(const cmCommandArgument* current) const
{
if (this->ArgumentsBefore.empty())
{
return true;
}
std::set<const cmCommandArgument*>::const_iterator argIt
= this->ArgumentsBefore.find(current);
if (argIt != this->ArgumentsBefore.end())
{
return true;
}
return false;
}
bool cmCommandArgument::KeyMatches(const std::string& key) const
{
if ((this->Key==0) || (this->Key[0]=='\0'))
{
return true;
}
return (key==this->Key);
}
void cmCommandArgument::ApplyOwnGroup()
{
if (this->Group!=0)
{
for (std::vector<cmCommandArgument*>::const_iterator
it = this->Group->ContainedArguments.begin();
it != this->Group->ContainedArguments.end();
++it)
{
if(*it != this)
{
this->ArgumentsBefore.insert(*it);
}
}
}
}
void cmCommandArgument::Activate()
{
this->WasActive = true;
this->CurrentIndex = 0;
}
bool cmCommandArgument::Consume(const std::string& arg)
{
bool res=this->DoConsume(arg, this->CurrentIndex);
this->CurrentIndex++;
return res;
}
cmCAStringVector::cmCAStringVector(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group)
:cmCommandArgument(args, key, group)
{
if ((key==0) || (*key==0))
{
this->DataStart = 0;
}
else
{
this->DataStart = 1;
}
}
bool cmCAStringVector::DoConsume(const std::string& arg,unsigned int index)
{
if (index >= this->DataStart)
{
if ((this->Ignore==0) || (arg != this->Ignore))
{
this->Vector.push_back(arg);
}
}
return false;
}
void cmCAStringVector::DoReset()
{
this->Vector.clear();
}
cmCAString::cmCAString(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group)
:cmCommandArgument(args, key, group)
{
if ((key==0) || (*key==0))
{
this->DataStart = 0;
}
else
{
this->DataStart = 1;
}
}
bool cmCAString::DoConsume(const std::string& arg, unsigned int index)
{
if (index == this->DataStart)
{
this->String = arg;
}
return index >= this->DataStart;
}
void cmCAString::DoReset()
{
this->String = "";
}
cmCAEnabler::cmCAEnabler(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group)
:cmCommandArgument(args, key, group)
,Enabled(false)
{}
bool cmCAEnabler::DoConsume(const std::string& arg, unsigned int index)
{
if (index==0)
{
this->Enabled = true;
}
return true;
}
void cmCAEnabler::DoReset()
{
this->Enabled = false;
}
cmCADisabler::cmCADisabler(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group)
:cmCommandArgument(args, key, group)
,Enabled(true)
{}
bool cmCADisabler::DoConsume(const std::string& arg, unsigned int index)
{
if (index==0)
{
this->Enabled = false;
}
return true;
}
void cmCADisabler::DoReset()
{
this->Enabled = true;
}
void cmCommandArgumentGroup::Follows(const cmCommandArgument* arg)
{
for(std::vector<cmCommandArgument*>::iterator
it = this->ContainedArguments.begin();
it != this->ContainedArguments.end();
++it)
{
(*it)->Follows(arg);
}
}
void cmCommandArgumentGroup::FollowsGroup(const cmCommandArgumentGroup* group)
{
for(std::vector<cmCommandArgument*>::iterator
it = this->ContainedArguments.begin();
it != this->ContainedArguments.end();
++it)
{
(*it)->FollowsGroup(group);
}
}
void cmCommandArgumentsHelper::Parse(const std::vector<std::string>* args,
std::vector<std::string>* unconsumedArgs)
{
if(args==0)
{
return;
}
for(std::vector<cmCommandArgument*>::iterator
argIt = this->Arguments.begin();
argIt != this->Arguments.end();
++argIt)
{
(*argIt)->ApplyOwnGroup();
(*argIt)->Reset();
}
cmCommandArgument* activeArgument = 0;
const cmCommandArgument* previousArgument = 0;
for(std::vector<std::string>::const_iterator it = args->begin();
it != args->end();
++it)
{
for(std::vector<cmCommandArgument*>::iterator
argIt = this->Arguments.begin();
argIt != this->Arguments.end();
++argIt)
{
if ((*argIt)->KeyMatches(*it) && ((*argIt)->MayFollow(previousArgument)))
{
activeArgument = *argIt;
activeArgument->Activate();
break;
}
}
if (activeArgument)
{
bool argDone = activeArgument->Consume(*it);
previousArgument = activeArgument;
if (argDone)
{
activeArgument = 0;
}
}
else
{
if (unconsumedArgs!=0)
{
unconsumedArgs->push_back(*it);
}
}
}
}
void cmCommandArgumentsHelper::AddArgument(cmCommandArgument* arg)
{
this->Arguments.push_back(arg);
}

View File

@ -0,0 +1,208 @@
/*=========================================================================
Program: CMake - Cross-Platform Makefile Generator
Module: $RCSfile$
Language: C++
Date: $Date$
Version: $Revision$
Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=========================================================================*/
#ifndef cmCommandArgumentsHelper_h
#define cmCommandArgumentsHelper_h
#include "cmStandardIncludes.h"
class cmCommandArgumentsHelper;
class cmCommandArgumentGroup;
/* cmCommandArgumentsHelper, cmCommandArgumentGroup and cmCommandArgument (i.e.
its derived classes cmCAXXX can be used to simplify the processing of
arguments to cmake commands. Maybe they can also be used to generate
documentation.
For every argument supported by a command one cmCommandArgument is created
and added to cmCommandArgumentsHelper. cmCommand has a cmCommandArgumentsHelper
as member variable so this should be used.
The order of the arguments is defined using the Follows(arg) method. It says
that this argument follows immediateley the given argument. It can be used
with multiple arguments if the argument can follow after different arguments.
Arguments can be arranged in groups using cmCommandArgumentGroup. Every
member of a group can follow any other member of the group. These groups
can also be used to define the order.
Once all arguments and groups are set up, cmCommandArgumentsHelper::Parse()
is called and afterwards the values of the arguments can be evaluated.
For an example see cmExportCommand.cxx.
*/
class cmCommandArgument
{
public:
cmCommandArgument(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group=0);
virtual ~cmCommandArgument() {}
/// this argument may follow after arg. 0 means it comes first.
void Follows(const cmCommandArgument* arg);
/// this argument may follow after any of the arguments in the given group
void FollowsGroup(const cmCommandArgumentGroup* group);
/// Returns true if the argument was found in the argument list
bool WasFound() const {return this->WasActive;}
// The following methods are only called from
// cmCommandArgumentsHelper::Parse(), but making this a friend would
// give it access to everything
/// Make the current argument the currently active argument
void Activate();
/// Consume the current string
bool Consume(const std::string& arg);
/// Return true if this argument may follow after the given argument.
bool MayFollow(const cmCommandArgument* current) const;
/** Returns true if the given key matches the key for this argument.
If this argument has an empty key everything matches. */
bool KeyMatches(const std::string& key) const;
/// Make this argument follow all members of the own group
void ApplyOwnGroup();
/// Reset argument, so it's back to its initial state
void Reset();
private:
const char* Key;
std::set<const cmCommandArgument*> ArgumentsBefore;
cmCommandArgumentGroup* Group;
bool WasActive;
unsigned int CurrentIndex;
virtual bool DoConsume(const std::string& arg, unsigned int index) = 0;
virtual void DoReset() = 0;
};
/** cmCAStringVector is to be used for arguments which can consist of more
than one string, e.g. the FILES argument in INSTALL(FILES f1 f2 f3 ...). */
class cmCAStringVector : public cmCommandArgument
{
public:
cmCAStringVector(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group=0);
/// Return the vector of strings
const std::vector<std::string>& GetVector() const {return this->Vector;}
/** Is there a keyword which should be skipped in
the arguments (e.g. ARGS for ADD_CUSTOM_COMMAND) ? */
void SetIgnore(const char* ignore) {this->Ignore=ignore;}
private:
std::vector<std::string> Vector;
unsigned int DataStart;
const char* Ignore;
cmCAStringVector();
virtual bool DoConsume(const std::string& arg, unsigned int index);
virtual void DoReset();
};
/** cmCAString is to be used for arguments which consist of one value,
e.g. the executable name in ADD_EXECUTABLE(). */
class cmCAString : public cmCommandArgument
{
public:
cmCAString(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group=0);
/// Return the string
const std::string& GetString() const {return this->String;}
private:
std::string String;
unsigned int DataStart;
virtual bool DoConsume(const std::string& arg, unsigned int index);
virtual void DoReset();
cmCAString();
};
/** cmCAEnabler is to be used for options which are off by default and can be
enabled using a special argument, e.g. EXCLUDE_FROM_ALL in ADD_EXECUTABLE(). */
class cmCAEnabler : public cmCommandArgument
{
public:
cmCAEnabler(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group=0);
/// Has it been enabled ?
bool IsEnabled() const {return this->Enabled;}
private:
bool Enabled;
virtual bool DoConsume(const std::string& arg, unsigned int index);
virtual void DoReset();
cmCAEnabler();
};
/** cmCADisable is to be used for options which are on by default and can be
disabled using a special argument.*/
class cmCADisabler : public cmCommandArgument
{
public:
cmCADisabler(cmCommandArgumentsHelper* args,
const char* key,
cmCommandArgumentGroup* group=0);
/// Is it still enabled ?
bool IsEnabled() const {return this->Enabled;}
private:
bool Enabled;
virtual bool DoConsume(const std::string& arg, unsigned int index);
virtual void DoReset();
cmCADisabler();
};
/** Group of arguments, needed for ordering. E.g. WIN32, EXCLUDE_FROM_ALL and
MACSOX_BUNDLE from ADD_EXECUTABLE() are a group.
*/
class cmCommandArgumentGroup
{
friend class cmCommandArgument;
public:
cmCommandArgumentGroup() {}
/// All members of this group may follow the given argument
void Follows(const cmCommandArgument* arg);
/// All members of this group may follow all members of the given group
void FollowsGroup(const cmCommandArgumentGroup* group);
private:
std::vector<cmCommandArgument*> ContainedArguments;
};
class cmCommandArgumentsHelper
{
public:
/// Parse the argument list
void Parse(const std::vector<std::string>* args,
std::vector<std::string>* unconsumedArgs);
/// Add an argument.
void AddArgument(cmCommandArgument* arg);
private:
std::vector<cmCommandArgument*> Arguments;
};
#endif

View File

@ -22,6 +22,21 @@
#include <cmsys/auto_ptr.hxx>
cmExportCommand::cmExportCommand()
:cmCommand()
,ArgumentGroup()
,Targets(&this->Helper, "TARGETS")
,Append(&this->Helper, "APPEND", &ArgumentGroup)
,Prefix(&this->Helper, "PREFIX", &ArgumentGroup)
,Filename(&this->Helper, "FILE", &ArgumentGroup)
{
// at first TARGETS
this->Targets.Follows(0);
// and after that the other options in any order
this->ArgumentGroup.Follows(&this->Targets);
}
// cmExportCommand
bool cmExportCommand
::InitialPass(std::vector<std::string> const& args)
@ -32,42 +47,50 @@ bool cmExportCommand
return false;
}
std::string filename;
std::string prefix;
std::string exportName;
std::vector<std::string> targets;
bool append = false;
if (!this->ParseArgs(args, filename, prefix, exportName, targets, append))
std::vector<std::string> unknownArgs;
this->Helper.Parse(&args, &unknownArgs);
if (!unknownArgs.empty())
{
this->SetError("Unknown arguments.");
cmSystemTools::SetFatalErrorOccured();
return false;
}
if ( !this->Makefile->CanIWriteThisFile(filename.c_str()) )
if (this->Targets.WasFound() == false)
{
std::string e = "attempted to write a file: " + filename
this->SetError("TARGETS option missing.");
cmSystemTools::SetFatalErrorOccured();
return false;
}
if ( !this->Makefile->CanIWriteThisFile(this->Filename.GetString().c_str()) )
{
std::string e = "attempted to write a file: " + this->Filename.GetString()
+ " into a source directory.";
this->SetError(e.c_str());
cmSystemTools::SetFatalErrorOccured();
return false;
}
if ((targets.empty()) || (filename.empty()))
if((this->Targets.GetVector().empty())||(this->Filename.GetString().empty()))
{
return true;
}
// Use copy-if-different if not appending.
cmsys::auto_ptr<std::ofstream> foutPtr;
if(append)
if(this->Append.IsEnabled())
{
cmsys::auto_ptr<std::ofstream> ap(
new std::ofstream(filename.c_str(), std::ios::app));
new std::ofstream(this->Filename.GetString().c_str(), std::ios::app));
foutPtr = ap;
}
else
{
cmsys::auto_ptr<cmGeneratedFileStream> ap(
new cmGeneratedFileStream(filename.c_str(), true));
new cmGeneratedFileStream(this->Filename.GetString().c_str(), true));
ap->SetCopyIfDifferent(true);
foutPtr = ap;
}
@ -75,7 +98,7 @@ bool cmExportCommand
if (!fout)
{
cmSystemTools::Error("Error Writing ", filename.c_str());
cmSystemTools::Error("Error Writing ", this->Filename.GetString().c_str());
cmSystemTools::ReportLastSystemError("");
return true;
}
@ -97,8 +120,9 @@ bool cmExportCommand
}
}
for(std::vector<std::string>::const_iterator currentTarget = targets.begin();
currentTarget != targets.end();
for(std::vector<std::string>::const_iterator
currentTarget = this->Targets.GetVector().begin();
currentTarget != this->Targets.GetVector().end();
++currentTarget)
{
cmTarget* target = this->Makefile->GetLocalGenerator()->
@ -112,8 +136,9 @@ bool cmExportCommand
}
}
for(std::vector<std::string>::const_iterator currentTarget = targets.begin();
currentTarget != targets.end();
for(std::vector<std::string>::const_iterator
currentTarget = this->Targets.GetVector().begin();
currentTarget != this->Targets.GetVector().end();
++currentTarget)
{
// Look for a CMake target with the given name, which is an executable
@ -129,26 +154,30 @@ bool cmExportCommand
switch (target->GetType())
{
case cmTarget::EXECUTABLE:
fout << "ADD_EXECUTABLE(" << prefix.c_str() << currentTarget->c_str()
<< " IMPORT )\n";
fout << "ADD_EXECUTABLE("
<< this->Prefix.GetString().c_str() << currentTarget->c_str()
<< " IMPORT )\n";
break;
case cmTarget::STATIC_LIBRARY:
fout << "ADD_LIBRARY(" << prefix.c_str() << currentTarget->c_str()
<< " STATIC IMPORT )\n";
fout << "ADD_LIBRARY("
<< this->Prefix.GetString().c_str() << currentTarget->c_str()
<< " STATIC IMPORT )\n";
break;
case cmTarget::SHARED_LIBRARY:
fout << "ADD_LIBRARY(" << prefix.c_str() << currentTarget->c_str()
<< " SHARED IMPORT )\n";
fout << "ADD_LIBRARY("
<< this->Prefix.GetString().c_str() << currentTarget->c_str()
<< " SHARED IMPORT )\n";
break;
case cmTarget::MODULE_LIBRARY:
fout << "ADD_LIBRARY(" << prefix.c_str() << currentTarget->c_str()
<< " MODULE IMPORT )\n";
fout << "ADD_LIBRARY("
<< this->Prefix.GetString().c_str() << currentTarget->c_str()
<< " MODULE IMPORT )\n";
break;
default: // should never happen
break;
}
fout << "SET_TARGET_PROPERTIES(" << prefix.c_str()
fout << "SET_TARGET_PROPERTIES(" << this->Prefix.GetString().c_str()
<< currentTarget->c_str() << " PROPERTIES \n"
<< " LOCATION " << target->GetLocation(0) << "\n";
for(std::vector<std::string>::const_iterator
@ -172,82 +201,3 @@ bool cmExportCommand
return true;
}
bool cmExportCommand::ParseArgs(const std::vector<std::string>& args,
std::string& filename,
std::string& prefix,
std::string& exportName,
std::vector<std::string>& targets,
bool& append) const
{
bool doingFile = false;
bool doingPrefix = false;
bool doingTargets = false;
bool doingName = true;
for(std::vector<std::string>::const_iterator it = args.begin();
it != args.end();
++it)
{
if (*it == "FILE")
{
doingFile = true;
doingPrefix = false;
doingName = false;
doingTargets = false;
}
else if (*it == "PREFIX")
{
doingFile = false;
doingPrefix = true;
doingName = false;
doingTargets = false;
}
else if (*it == "TARGETS")
{
doingFile = false;
doingPrefix = false;
doingName = false;
doingTargets = true;
}
else if (*it == "APPEND")
{
append = true;
doingFile = false;
doingPrefix = false;
doingName = false;
doingTargets = false;
}
else if (doingFile)
{
filename = *it;
doingFile = false;
doingPrefix = false;
doingName = false;
doingTargets = false;
}
else if (doingPrefix)
{
prefix = *it;
doingFile = false;
doingPrefix = false;
doingName = false;
doingTargets = false;
}
else if (doingTargets)
{
targets.push_back(*it);
}
else if (doingName)
{
exportName = *it;
doingFile = false;
doingPrefix = false;
doingName = false;
doingTargets = false;
}
else
{
}
}
return true;
}

View File

@ -28,6 +28,7 @@
class cmExportCommand : public cmCommand
{
public:
cmExportCommand();
/**
* This is a virtual constructor for the command.
*/
@ -75,10 +76,11 @@ public:
cmTypeMacro(cmExportCommand, cmCommand);
private:
bool ParseArgs(const std::vector<std::string>& args, std::string& filename,
std::string& prefix, std::string& exportName,
std::vector<std::string>& targets, bool& append ) const;
cmCommandArgumentGroup ArgumentGroup;
cmCAStringVector Targets;
cmCAEnabler Append;
cmCAString Prefix;
cmCAString Filename;
};