From 2e56fa4808109552610cbcd360c473d2db895520 Mon Sep 17 00:00:00 2001 From: Alexander Neundorf Date: Mon, 2 Jul 2007 15:43:21 -0400 Subject: [PATCH] ENH: add framework for unified handling of arguments to cmake commands, example see cmExportCommand.cxx Alex --- Source/cmBootstrapCommands.cxx | 1 + Source/cmCommand.h | 2 + Source/cmCommandArgumentsHelper.cxx | 308 ++++++++++++++++++++++++++++ Source/cmCommandArgumentsHelper.h | 208 +++++++++++++++++++ Source/cmExportCommand.cxx | 160 +++++---------- Source/cmExportCommand.h | 10 +- 6 files changed, 580 insertions(+), 109 deletions(-) create mode 100644 Source/cmCommandArgumentsHelper.cxx create mode 100644 Source/cmCommandArgumentsHelper.h diff --git a/Source/cmBootstrapCommands.cxx b/Source/cmBootstrapCommands.cxx index bce5d4555..4f3f8aa06 100644 --- a/Source/cmBootstrapCommands.cxx +++ b/Source/cmBootstrapCommands.cxx @@ -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" diff --git a/Source/cmCommand.h b/Source/cmCommand.h index 55672a658..3111d46f8 100644 --- a/Source/cmCommand.h +++ b/Source/cmCommand.h @@ -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; diff --git a/Source/cmCommandArgumentsHelper.cxx b/Source/cmCommandArgumentsHelper.cxx new file mode 100644 index 000000000..ffcd15ae1 --- /dev/null +++ b/Source/cmCommandArgumentsHelper.cxx @@ -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::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_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::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::iterator + it = this->ContainedArguments.begin(); + it != this->ContainedArguments.end(); + ++it) + { + (*it)->Follows(arg); + } +} + +void cmCommandArgumentGroup::FollowsGroup(const cmCommandArgumentGroup* group) +{ + for(std::vector::iterator + it = this->ContainedArguments.begin(); + it != this->ContainedArguments.end(); + ++it) + { + (*it)->FollowsGroup(group); + } +} + +void cmCommandArgumentsHelper::Parse(const std::vector* args, + std::vector* unconsumedArgs) +{ + if(args==0) + { + return; + } + + for(std::vector::iterator + argIt = this->Arguments.begin(); + argIt != this->Arguments.end(); + ++argIt) + { + (*argIt)->ApplyOwnGroup(); + (*argIt)->Reset(); + } + + cmCommandArgument* activeArgument = 0; + const cmCommandArgument* previousArgument = 0; + for(std::vector::const_iterator it = args->begin(); + it != args->end(); + ++it) + { + for(std::vector::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); +} + diff --git a/Source/cmCommandArgumentsHelper.h b/Source/cmCommandArgumentsHelper.h new file mode 100644 index 000000000..1d7ba617a --- /dev/null +++ b/Source/cmCommandArgumentsHelper.h @@ -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 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& 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 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 ContainedArguments; +}; + +class cmCommandArgumentsHelper +{ + public: + /// Parse the argument list + void Parse(const std::vector* args, + std::vector* unconsumedArgs); + + /// Add an argument. + void AddArgument(cmCommandArgument* arg); + private: + std::vector Arguments; +}; + + +#endif diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx index 95756903d..b16665005 100644 --- a/Source/cmExportCommand.cxx +++ b/Source/cmExportCommand.cxx @@ -22,6 +22,21 @@ #include +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 const& args) @@ -32,42 +47,50 @@ bool cmExportCommand return false; } - std::string filename; - std::string prefix; - std::string exportName; - std::vector targets; - bool append = false; - if (!this->ParseArgs(args, filename, prefix, exportName, targets, append)) + std::vector 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 foutPtr; - if(append) + if(this->Append.IsEnabled()) { cmsys::auto_ptr 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 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::const_iterator currentTarget = targets.begin(); - currentTarget != targets.end(); + for(std::vector::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::const_iterator currentTarget = targets.begin(); - currentTarget != targets.end(); + for(std::vector::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::const_iterator @@ -172,82 +201,3 @@ bool cmExportCommand return true; } - -bool cmExportCommand::ParseArgs(const std::vector& args, - std::string& filename, - std::string& prefix, - std::string& exportName, - std::vector& targets, - bool& append) const -{ - bool doingFile = false; - bool doingPrefix = false; - bool doingTargets = false; - bool doingName = true; - for(std::vector::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; -} diff --git a/Source/cmExportCommand.h b/Source/cmExportCommand.h index 97f709f10..c5a13f928 100644 --- a/Source/cmExportCommand.h +++ b/Source/cmExportCommand.h @@ -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& args, std::string& filename, - std::string& prefix, std::string& exportName, - std::vector& targets, bool& append ) const; - + cmCommandArgumentGroup ArgumentGroup; + cmCAStringVector Targets; + cmCAEnabler Append; + cmCAString Prefix; + cmCAString Filename; };