From f87271d0eae52e6c670d10b53e72eed72e80bfd1 Mon Sep 17 00:00:00 2001 From: Andy Cedilnik Date: Fri, 10 Feb 2006 14:11:12 -0500 Subject: ENH: Add initial implementation of the list command --- CMakeLists.txt | 2 +- Source/cmCommands.cxx | 2 + Source/cmListCommand.cxx | 359 +++++++++++++++++++++++++++++++++++++ Source/cmListCommand.h | 99 ++++++++++ Tests/CMakeTests/CMakeLists.txt | 10 ++ Tests/CMakeTests/ListTest.cmake.in | 50 ++++++ 6 files changed, 521 insertions(+), 1 deletion(-) create mode 100644 Source/cmListCommand.cxx create mode 100644 Source/cmListCommand.h create mode 100644 Tests/CMakeTests/CMakeLists.txt create mode 100644 Tests/CMakeTests/ListTest.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a2ca4c..cdc73e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,7 @@ IF(BUILD_CursesDialog) SUBDIRS(Source/CursesDialog/form) ENDIF(BUILD_CursesDialog) -SUBDIRS(Source Modules Templates Utilities) +SUBDIRS(Source Modules Templates Utilities Tests/CMakeTests) ENABLE_TESTING() # Include the standard Dart testing module diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index 22d4e32..da4683d 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -28,6 +28,7 @@ #include "cmIncludeExternalMSProjectCommand.cxx" #include "cmInstallCommand.cxx" #include "cmLinkLibrariesCommand.cxx" +#include "cmListCommand.cxx" #include "cmLoadCacheCommand.cxx" #include "cmMathCommand.cxx" #include "cmOutputRequiredFilesCommand.cxx" @@ -71,6 +72,7 @@ void GetPredefinedCommands(std::list& commands.push_back(new cmIncludeExternalMSProjectCommand); commands.push_back(new cmInstallCommand); commands.push_back(new cmLinkLibrariesCommand); + commands.push_back(new cmListCommand); commands.push_back(new cmLoadCacheCommand); commands.push_back(new cmLoadCommandCommand); commands.push_back(new cmMathCommand); diff --git a/Source/cmListCommand.cxx b/Source/cmListCommand.cxx new file mode 100644 index 0000000..60cf9e3 --- /dev/null +++ b/Source/cmListCommand.cxx @@ -0,0 +1,359 @@ +/*========================================================================= + + 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 "cmListCommand.h" +#include +#include + +#include // required for atoi +#include +//---------------------------------------------------------------------------- +bool cmListCommand::InitialPass(std::vector const& args) +{ + if(args.size() < 1) + { + this->SetError("must be called with at least one argument."); + return false; + } + + const std::string &subCommand = args[0]; + if(subCommand == "LENGTH") + { + return this->HandleLengthCommand(args); + } + if(subCommand == "GET") + { + return this->HandleGetCommand(args); + } + if(subCommand == "SET") + { + return this->HandleSetCommand(args); + } + if(subCommand == "INSERT") + { + return this->HandleInsertCommand(args); + } + if(subCommand == "REMOVE") + { + return this->HandleRemoveCommand(args); + } + if(subCommand == "REMOVE_ITEM") + { + return this->HandleRemoveItemCommand(args); + } + + std::string e = "does not recognize sub-command "+subCommand; + this->SetError(e.c_str()); + return false; +} + +//---------------------------------------------------------------------------- +bool cmListCommand::GetListString(std::string& listString, const char* var) +{ + if ( !var ) + { + return false; + } + // get the old value + const char* cacheValue + = m_Makefile->GetDefinition(var); + if(!cacheValue) + { + cmOStringStream str; + str << "cannot find variable: " << var; + this->SetError(str.str().c_str()); + return false; + } + listString = cacheValue; + return true; +} + +//---------------------------------------------------------------------------- +bool cmListCommand::GetList(std::vector& list, const char* var) +{ + std::string listString; + if ( !this->GetListString(listString, var) ) + { + return false; + } + // expand the variable + cmSystemTools::ExpandListArgument(listString, list); + return true; +} + +//---------------------------------------------------------------------------- +bool cmListCommand::HandleLengthCommand(std::vector const& args) +{ + if(args.size() != 3) + { + this->SetError("sub-command LENGTH requires two arguments."); + return false; + } + + const std::string& listName = args[1]; + const std::string& variableName = args[args.size() - 1]; + std::vector varArgsExpanded; + if ( !this->GetList(varArgsExpanded, listName.c_str()) ) + { + return false; + } + + size_t length = varArgsExpanded.size(); + char buffer[1024]; + sprintf(buffer, "%d", static_cast(length)); + + m_Makefile->AddDefinition(variableName.c_str(), buffer); + return true; +} + +//---------------------------------------------------------------------------- +bool cmListCommand::HandleGetCommand(std::vector const& args) +{ + if(args.size() < 4) + { + this->SetError("sub-command GET requires at least three arguments."); + return false; + } + + const std::string& listName = args[1]; + const std::string& variableName = args[args.size() - 1]; + // expand the variable + std::vector varArgsExpanded; + if ( !this->GetList(varArgsExpanded, listName.c_str()) ) + { + return false; + } + + std::string value; + size_t cc; + for ( cc = 2; cc < args.size()-1; cc ++ ) + { + int item = atoi(args[cc].c_str()); + if (value.size()) + { + value += ";"; + } + int nitem = varArgsExpanded.size(); + if ( item < 0 ) + { + item = nitem + item; + } + if ( item < 0 || nitem <= item ) + { + cmOStringStream str; + str << "index: " << item << " out of range (-" << varArgsExpanded.size() << ", " << varArgsExpanded.size()-1 << ")"; + this->SetError(str.str().c_str()); + return false; + } + value += varArgsExpanded[item]; + } + + m_Makefile->AddDefinition(variableName.c_str(), value.c_str()); + return true; +} + +//---------------------------------------------------------------------------- +bool cmListCommand::HandleSetCommand(std::vector const& args) +{ + if(args.size() < 3) + { + this->SetError("sub-command SET requires at least two arguments."); + return false; + } + + const std::string& listName = args[1]; + // expand the variable + std::string listString; + if ( !this->GetListString(listString, listName.c_str()) ) + { + return false; + } + size_t cc; + for ( cc = 2; cc < args.size(); ++ cc ) + { + if ( listString.size() ) + { + listString += ";"; + } + listString += args[cc]; + } + + m_Makefile->AddDefinition(listName.c_str(), listString.c_str()); + return true; +} + +//---------------------------------------------------------------------------- +bool cmListCommand::HandleInsertCommand(std::vector const& args) +{ + if(args.size() < 4) + { + this->SetError("sub-command INSERT requires at least three arguments."); + return false; + } + + const std::string& listName = args[1]; + // expand the variable + std::vector varArgsExpanded; + if ( !this->GetList(varArgsExpanded, listName.c_str()) ) + { + return false; + } + + int item = atoi(args[2].c_str()); + + int nitem = varArgsExpanded.size(); + if ( item < 0 ) + { + item = nitem + item; + } + if ( item < 0 || nitem <= item ) + { + cmOStringStream str; + str << "index: " << item << " out of range (-" << varArgsExpanded.size() << ", " << varArgsExpanded.size()-1 << ")"; + this->SetError(str.str().c_str()); + return false; + } + size_t cc; + size_t cnt = 0; + for ( cc = 3; cc < args.size(); ++ cc ) + { + varArgsExpanded.insert(varArgsExpanded.begin()+item+cnt, args[cc]); + cnt ++; + } + + std::string value; + for ( cc = 0; cc < varArgsExpanded.size(); cc ++ ) + { + if (value.size()) + { + value += ";"; + } + value += varArgsExpanded[cc]; + } + + m_Makefile->AddDefinition(listName.c_str(), value.c_str()); + return true; +} + +//---------------------------------------------------------------------------- +bool cmListCommand::HandleRemoveCommand(std::vector const& args) +{ + if(args.size() < 3) + { + this->SetError("sub-command REMOVE requires at least two arguments."); + return false; + } + + const std::string& listName = args[1]; + // expand the variable + std::vector varArgsExpanded; + if ( !this->GetList(varArgsExpanded, listName.c_str()) ) + { + return false; + } + + size_t cc; + for ( cc = 2; cc < args.size(); ++ cc ) + { + size_t kk = 0; + while ( kk < varArgsExpanded.size() ) + { + if ( varArgsExpanded[kk] == args[cc] ) + { + varArgsExpanded.erase(varArgsExpanded.begin()+kk); + } + kk ++; + } + } + + std::string value; + for ( cc = 0; cc < varArgsExpanded.size(); cc ++ ) + { + if (value.size()) + { + value += ";"; + } + value += varArgsExpanded[cc]; + } + + m_Makefile->AddDefinition(listName.c_str(), value.c_str()); + return true; +} + +//---------------------------------------------------------------------------- +bool cmListCommand::HandleRemoveItemCommand(std::vector const& args) +{ + if(args.size() < 3) + { + this->SetError("sub-command REMOVE_ITEM requires at least two arguments."); + return false; + } + + const std::string& listName = args[1]; + // expand the variable + std::vector varArgsExpanded; + if ( !this->GetList(varArgsExpanded, listName.c_str()) ) + { + return false; + } + + size_t cc; + std::vector removed; + for ( cc = 2; cc < args.size(); ++ cc ) + { + int item = atoi(args[cc].c_str()); + int nitem = varArgsExpanded.size(); + if ( item < 0 ) + { + item = nitem + item; + } + if ( item < 0 || nitem <= item ) + { + cmOStringStream str; + str << "index: " << item << " out of range (-" << varArgsExpanded.size() << ", " << varArgsExpanded.size()-1 << ")"; + this->SetError(str.str().c_str()); + return false; + } + removed.push_back(static_cast(item)); + } + + std::string value; + for ( cc = 0; cc < varArgsExpanded.size(); ++ cc ) + { + size_t kk; + bool found = false; + for ( kk = 0; kk < removed.size(); ++ kk ) + { + if ( cc == removed[kk] ) + { + found = true; + } + } + + if ( !found ) + { + if (value.size()) + { + value += ";"; + } + value += varArgsExpanded[cc]; + } + } + + m_Makefile->AddDefinition(listName.c_str(), value.c_str()); + return true; +} + diff --git a/Source/cmListCommand.h b/Source/cmListCommand.h new file mode 100644 index 0000000..5df06dd --- /dev/null +++ b/Source/cmListCommand.h @@ -0,0 +1,99 @@ +/*========================================================================= + + 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 cmListCommand_h +#define cmListCommand_h + +#include "cmCommand.h" + +/** \class cmListCommand + * \brief Common list operations + * + */ +class cmListCommand : public cmCommand +{ +public: + /** + * This is a virtual constructor for the command. + */ + virtual cmCommand* Clone() + { + return new cmListCommand; + } + + /** + * This is called when the command is first encountered in + * the CMakeLists.txt file. + */ + virtual bool InitialPass(std::vector const& args); + + /** + * This determines if the command is invoked when in script mode. + */ + virtual bool IsScriptable() { return true; } + + /** + * The name of the command as specified in CMakeList.txt. + */ + virtual const char* GetName() { return "LIST";} + + /** + * Succinct documentation. + */ + virtual const char* GetTerseDocumentation() + { + return "List operations."; + } + + /** + * More documentation. + */ + virtual const char* GetFullDocumentation() + { + return + " LIST(LENGTH )\n" + " LIST(GET [ ...] )\n" + " LIST(SET [ ...])\n" + " LIST(INSERT [ ...])\n" + " LIST(REMOVE [ ...])\n" + " LIST(REMOVE_ITEM [ ...])\n" + " LIST(SORT )\n" + " LIST(REVERSE )\n" + "LENGTH will return a given list's length.\n" + "GET will return list of elements specified by indices from the list.\n" + "SET will append elements to the list.\n" + "INSERT will insert elements to the list to the specified location.\n" + "When specifying an index, negative value corresponds to index from the end " + "of the list.\n" + ; + } + + cmTypeMacro(cmListCommand, cmCommand); +protected: + bool HandleLengthCommand(std::vector const& args); + bool HandleGetCommand(std::vector const& args); + bool HandleSetCommand(std::vector const& args); + bool HandleInsertCommand(std::vector const& args); + bool HandleRemoveCommand(std::vector const& args); + bool HandleRemoveItemCommand(std::vector const& args); + + + bool GetList(std::vector& list, const char* var); + bool GetListString(std::string& listString, const char* var); +}; + + +#endif diff --git a/Tests/CMakeTests/CMakeLists.txt b/Tests/CMakeTests/CMakeLists.txt new file mode 100644 index 0000000..a1eefb5 --- /dev/null +++ b/Tests/CMakeTests/CMakeLists.txt @@ -0,0 +1,10 @@ +SET(CMAKE_EXECUTABLE "${EXECUTABLE_OUTPUT_PATH}/cmake") + + +MACRO(AddCMakeTest TestName Arguments) + CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/${TestName}Test.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${TestName}Test.cmake" @ONLY IMMEDIATE) + ADD_TEST(CMake.List ${CMAKE_EXECUTABLE} -P "${CMAKE_CURRENT_BINARY_DIR}/ListTest.cmake" ${Arguments}) +ENDMACRO(AddCMakeTest) + +AddCMakeTest(List "") diff --git a/Tests/CMakeTests/ListTest.cmake.in b/Tests/CMakeTests/ListTest.cmake.in new file mode 100644 index 0000000..48b157c --- /dev/null +++ b/Tests/CMakeTests/ListTest.cmake.in @@ -0,0 +1,50 @@ +MACRO(TEST command expected) + IF("x${result}" STREQUAL "x${expected}") + MESSAGE("TEST \"${command}\" success: \"${result}\" expected: \"${expected}\"") + ELSE("x${result}" STREQUAL "x${expected}") + MESSAGE(SEND_ERROR "TEST \"${command}\" failed: \"${result}\" expected: \"${expected}\"") + ENDIF("x${result}" STREQUAL "x${expected}") +ENDMACRO(TEST command expected) + +SET(mylist andy bill ken brad) + +LIST(LENGTH mylist result) +TEST("LENGTH mylist result" "4") +LIST(LENGTH "mylist" result) +TEST("LENGTH \"mylist\" result" "4") + +LIST(GET mylist 3 2 1 0 result) +TEST("GET mylist 3 2 1 0 result" "brad;ken;bill;andy") + +LIST(GET mylist 0 item0) +LIST(GET mylist 1 item1) +LIST(GET mylist 2 item2) +LIST(GET mylist 3 item3) +SET(result "${item3}" "${item0}" "${item1}" "${item2}") +TEST("GET individual 3 2 1 0 result" "brad;andy;bill;ken") + +LIST(GET mylist -1 -2 -3 -4 result) +TEST("GET mylist -1 -2 -3 -4 result" "brad;ken;bill;andy") + +LIST(GET mylist -1 2 -3 0 result) +TEST("GET mylist -1 2 -3 0 ${result}" "brad;ken;bill;andy") + +SET(result andy) +LIST(SET result brad) +TEST("SET result brad" "andy;brad") + +SET(result andy brad) +LIST(INSERT result -1 bill ken) +TEST("INSERT result -1 bill ken" "andy;bill;ken;brad") + +SET(result andy bill brad ken bob) +LIST(REMOVE result bob) +TEST("REMOVE result bob" "andy;bill;brad;ken") + +SET(result andy bill bob brad ken peter) +LIST(REMOVE result peter bob) +TEST("REMOVE result peter bob" "andy;bill;brad;ken") + +SET(result andy bill bob brad ken peter) +LIST(REMOVE_ITEM result 2 -1) +TEST("REMOVE_ITEM result 2 -1" "andy;bill;brad;ken") -- cgit v0.12