diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx index 3ecc82cdb..81a9d2569 100644 --- a/Source/cmCommands.cxx +++ b/Source/cmCommands.cxx @@ -52,6 +52,8 @@ #include "cmUseMangledMesaCommand.cxx" #include "cmUtilitySourceCommand.cxx" #include "cmVariableRequiresCommand.cxx" +#include "cmVariableWatchCommand.cxx" + #include "cmWhileCommand.cxx" #include "cmWriteFileCommand.cxx" @@ -105,6 +107,7 @@ void GetPredefinedCommands(std::list& commands.push_back(new cmUseMangledMesaCommand); commands.push_back(new cmUtilitySourceCommand); commands.push_back(new cmVariableRequiresCommand); + commands.push_back(new cmVariableWatchCommand); commands.push_back(new cmWhileCommand); commands.push_back(new cmWriteFileCommand); #endif diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx index b4118d9a7..518d3a9ec 100644 --- a/Source/cmFindPackageCommand.cxx +++ b/Source/cmFindPackageCommand.cxx @@ -22,8 +22,10 @@ #endif void cmFindPackageNeedBackwardsCompatibility(const std::string& variable, - int access_type, void* ) + int access_type, void*, const char* newValue, + const cmMakefile*) { + (void)newValue; #ifdef CMAKE_BUILD_WITH_CMAKE if(access_type == cmVariableWatch::UNKNOWN_VARIABLE_READ_ACCESS) { diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 428ea6a36..a698b6abb 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -1215,7 +1215,9 @@ void cmMakefile::AddDefinition(const char* name, const char* value) if ( vv ) { vv->VariableAccessed(this->TemporaryDefinitionKey, - cmVariableWatch::VARIABLE_MODIFIED_ACCESS); + cmVariableWatch::VARIABLE_MODIFIED_ACCESS, + value, + this); } #endif } @@ -1275,7 +1277,8 @@ void cmMakefile::AddDefinition(const char* name, bool value) cmVariableWatch* vv = this->GetVariableWatch(); if ( vv ) { - vv->VariableAccessed(name, cmVariableWatch::VARIABLE_MODIFIED_ACCESS); + vv->VariableAccessed(name, cmVariableWatch::VARIABLE_MODIFIED_ACCESS, + value?"ON":"OFF", this); } #endif } @@ -1304,7 +1307,8 @@ void cmMakefile::RemoveDefinition(const char* name) cmVariableWatch* vv = this->GetVariableWatch(); if ( vv ) { - vv->VariableAccessed(name, cmVariableWatch::VARIABLE_REMOVED_ACCESS); + vv->VariableAccessed(name, cmVariableWatch::VARIABLE_REMOVED_ACCESS, + 0, this); } #endif } @@ -1649,7 +1653,8 @@ const char* cmMakefile::GetDefinition(const char* name) const { if ( def ) { - vv->VariableAccessed(name, cmVariableWatch::VARIABLE_READ_ACCESS); + vv->VariableAccessed(name, cmVariableWatch::VARIABLE_READ_ACCESS, + def, this); } else { @@ -1659,13 +1664,13 @@ const char* cmMakefile::GetDefinition(const char* name) const if (pos2 != this->Definitions.end() && cmSystemTools::IsOn((*pos2).second.c_str())) { - vv->VariableAccessed - (name, cmVariableWatch::ALLOWED_UNKNOWN_VARIABLE_READ_ACCESS); + vv->VariableAccessed(name, + cmVariableWatch::ALLOWED_UNKNOWN_VARIABLE_READ_ACCESS, def, this); } else { - vv->VariableAccessed(name, cmVariableWatch:: - UNKNOWN_VARIABLE_READ_ACCESS); + vv->VariableAccessed(name, + cmVariableWatch::UNKNOWN_VARIABLE_READ_ACCESS, def, this); } } } diff --git a/Source/cmVariableWatch.cxx b/Source/cmVariableWatch.cxx index d267a52f1..d234f28d2 100644 --- a/Source/cmVariableWatch.cxx +++ b/Source/cmVariableWatch.cxx @@ -16,6 +16,25 @@ =========================================================================*/ #include "cmVariableWatch.h" +static const char* const cmVariableWatchAccessStrings[] = +{ + "READ_ACCESS", + "UNKNOWN_READ_ACCESS", + "ALLOWED_UNKNOWN_READ_ACCESS", + "MODIFIED_ACCESS", + "REMOVED_ACCESS", + "NO_ACCESS" +}; + +const char* cmVariableWatch::GetAccessAsString(int access_type) +{ + if ( access_type < 0 || access_type >= cmVariableWatch::NO_ACCESS ) + { + return "NO_ACCESS"; + } + return cmVariableWatchAccessStrings[access_type]; +} + cmVariableWatch::cmVariableWatch() { } @@ -60,7 +79,9 @@ void cmVariableWatch::RemoveWatch(const std::string& variable, } void cmVariableWatch::VariableAccessed(const std::string& variable, - int access_type) const + int access_type, + const char* newValue, + const cmMakefile* mf) const { cmVariableWatch::StringToVectorOfPairs::const_iterator mit = this->WatchMap.find(variable); @@ -70,7 +91,8 @@ void cmVariableWatch::VariableAccessed(const std::string& variable, cmVariableWatch::VectorOfPairs::const_iterator it; for ( it = vp->begin(); it != vp->end(); it ++ ) { - it->Method(variable, access_type, it->ClientData); + it->Method(variable, access_type, it->ClientData, + newValue, mf); } } } diff --git a/Source/cmVariableWatch.h b/Source/cmVariableWatch.h index 15e8f90c6..7f97c282c 100644 --- a/Source/cmVariableWatch.h +++ b/Source/cmVariableWatch.h @@ -19,6 +19,8 @@ #include "cmStandardIncludes.h" +class cmMakefile; + /** \class cmVariableWatch * \brief Helper class for watching of variable accesses. * @@ -28,7 +30,7 @@ class cmVariableWatch { public: typedef void (*WatchMethod)(const std::string& variable, int access_type, - void* client_data); + void* client_data, const char* newValue, const cmMakefile* mf); cmVariableWatch(); ~cmVariableWatch(); @@ -43,20 +45,26 @@ public: /** * This method is called when variable is accessed */ - void VariableAccessed(const std::string& variable, int access_type) const; + void VariableAccessed(const std::string& variable, int access_type, + const char* newValue, const cmMakefile* mf) const; /** * Different access types. */ enum { - VARIABLE_READ_ACCESS, - UNKNOWN_VARIABLE_READ_ACCESS, - ALLOWED_UNKNOWN_VARIABLE_READ_ACCESS, - VARIABLE_MODIFIED_ACCESS, - VARIABLE_REMOVED_ACCESS, - NO_ACCESS + VARIABLE_READ_ACCESS = 0, + UNKNOWN_VARIABLE_READ_ACCESS, + ALLOWED_UNKNOWN_VARIABLE_READ_ACCESS, + VARIABLE_MODIFIED_ACCESS, + VARIABLE_REMOVED_ACCESS, + NO_ACCESS }; + + /** + * Return the access as string + */ + static const char* GetAccessAsString(int access_type); protected: struct Pair diff --git a/Source/cmVariableWatchCommand.cxx b/Source/cmVariableWatchCommand.cxx new file mode 100644 index 000000000..8e83756db --- /dev/null +++ b/Source/cmVariableWatchCommand.cxx @@ -0,0 +1,136 @@ +/*========================================================================= + + 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 "cmVariableWatchCommand.h" + +#include "cmVariableWatch.h" + +//---------------------------------------------------------------------------- +static void cmVariableWatchCommandVariableAccessed( + const std::string& variable, int access_type, void* client_data, + const char* newValue, const cmMakefile* mf) +{ + cmVariableWatchCommand* command = static_cast(client_data); + command->VariableAccessed(variable, access_type, newValue, mf); +} + +//---------------------------------------------------------------------------- +cmVariableWatchCommand::cmVariableWatchCommand() +{ + this->InCallback = false; +} + +//---------------------------------------------------------------------------- +bool cmVariableWatchCommand::InitialPass(std::vector const& args) +{ + if ( args.size() < 1 ) + { + this->SetError("must be called with at least one argument."); + return false; + } + std::string variable = args[0]; + if ( args.size() > 1 ) + { + std::string command = args[1]; + this->Handlers[variable].Commands.push_back(args[1]); + } + if ( variable == "CMAKE_CURRENT_LIST_FILE" ) + { + cmOStringStream ostr; + ostr << "cannot be set on the variable: " << variable.c_str(); + this->SetError(ostr.str().c_str()); + return false; + } + + this->Makefile->GetCMakeInstance()->GetVariableWatch()->AddWatch( + variable, cmVariableWatchCommandVariableAccessed, this); + + return true; +} + +//---------------------------------------------------------------------------- +void cmVariableWatchCommand::VariableAccessed(const std::string& variable, + int access_type, const char* newValue, const cmMakefile* mf) +{ + if ( this->InCallback ) + { + return; + } + this->InCallback = true; + + cmListFileFunction newLFF; + cmVariableWatchCommandHandler *handler = &this->Handlers[variable]; + cmVariableWatchCommandHandler::VectorOfCommands::iterator it; + cmListFileArgument arg; + bool processed = false; + const char* accessString = cmVariableWatch::GetAccessAsString(access_type); + const char* currentListFile = mf->GetDefinition("CMAKE_CURRENT_LIST_FILE"); + + /// Ultra bad!! + cmMakefile* makefile = const_cast(mf); + + std::string stack = makefile->GetProperty("LISTFILE_STACK"); + for ( it = handler->Commands.begin(); it != handler->Commands.end(); + ++ it ) + { + std::string command = *it; + newLFF.Arguments.clear(); + newLFF.Arguments.push_back(cmListFileArgument(variable, true, "unknown", 9999)); + newLFF.Arguments.push_back(cmListFileArgument(accessString, true, "unknown", 9999)); + newLFF.Arguments.push_back(cmListFileArgument(newValue?newValue:"", true, "unknown", 9999)); + newLFF.Arguments.push_back(cmListFileArgument(currentListFile, true, "unknown", 9999)); + newLFF.Arguments.push_back(cmListFileArgument(stack, true, "unknown", 9999)); + newLFF.Name = command; + newLFF.FilePath = "Some weird path"; + newLFF.Line = 9999; + if(!makefile->ExecuteCommand(newLFF)) + { + arg.FilePath = "Unknown"; + arg.Line = 0; + cmOStringStream error; + error << "Error in cmake code at\n" + << arg.FilePath << ":" << arg.Line << ":\n" + << "A command failed during the invocation of callback\"" + << command << "\"."; + cmSystemTools::Error(error.str().c_str()); + this->InCallback = false; + return; + } + processed = true; + } + if ( !processed ) + { + cmOStringStream msg; + msg << "* Variable \"" << variable.c_str() << "\" was accessed using " + << accessString << " in: " << currentListFile << std::endl; + msg << " The value of the variable: \"" << newValue << "\"" << std::endl; + msg << " The list file stack: " << stack.c_str(); + cmSystemTools::Message(msg.str().c_str()); + std::vector vars = makefile->GetDefinitions(); + cmOStringStream msg2; + size_t cc; + for ( cc = 0; cc < vars.size(); cc ++ ) + { + if ( vars[cc] == variable ) + { + continue; + } + msg2 << vars[cc] << " = \"" << makefile->GetDefinition(vars[cc].c_str()) << "\"" << std::endl; + } + //cmSystemTools::Message(msg2.str().c_str()); + } + this->InCallback = false; +} diff --git a/Source/cmVariableWatchCommand.h b/Source/cmVariableWatchCommand.h new file mode 100644 index 000000000..244aea9e5 --- /dev/null +++ b/Source/cmVariableWatchCommand.h @@ -0,0 +1,98 @@ +/*========================================================================= + + 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 cmVariableWatchCommand_h +#define cmVariableWatchCommand_h + +#include "cmCommand.h" + +class cmVariableWatchCommandHandler +{ +public: + typedef std::vector VectorOfCommands; + VectorOfCommands Commands; +}; + +/** \class cmVariableWatchCommand + * \brief Watch when the variable changes and invoke command + * + */ +class cmVariableWatchCommand : public cmCommand +{ +public: + /** + * This is a virtual constructor for the command. + */ + virtual cmCommand* Clone() + { + return new cmVariableWatchCommand; + } + + //! Default constructor + cmVariableWatchCommand(); + + /** + * 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 "VARIABLE_WATCH";} + + /** + * Succinct documentation. + */ + virtual const char* GetTerseDocumentation() + { + return "Watch the CMake variable for change."; + } + + /** + * More documentation. + */ + virtual const char* GetFullDocumentation() + { + return + " VARIABLE_WATCH( [])\n" + "If the specified variable changes, the message will be printed about " + "the variable being changed. If the command is spceified, the command " + "will be executed. The command will receive the following arguments:" + " COMMAND( )"; + } + + cmTypeMacro(cmVariableWatchCommand, cmCommand); + + void VariableAccessed(const std::string& variable, int access_type, + const char* newValue, const cmMakefile* mf); + +protected: + std::map Handlers; + + bool InCallback; +}; + + +#endif + + diff --git a/Source/cmake.cxx b/Source/cmake.cxx index e2e624dbb..973fc2088 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -88,7 +88,7 @@ #include // auto_ptr void cmNeedBackwardsCompatibility(const std::string& variable, - int access_type, void* ) + int access_type, void*, const char*, const cmMakefile*) { #ifdef CMAKE_BUILD_WITH_CMAKE if (access_type == cmVariableWatch::UNKNOWN_VARIABLE_READ_ACCESS) diff --git a/Tests/CMakeTests/CMakeLists.txt b/Tests/CMakeTests/CMakeLists.txt index a1eefb516..6f5167f83 100644 --- a/Tests/CMakeTests/CMakeLists.txt +++ b/Tests/CMakeTests/CMakeLists.txt @@ -4,7 +4,8 @@ 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}) + ADD_TEST(CMake.${TestName} ${CMAKE_EXECUTABLE} -P "${CMAKE_CURRENT_BINARY_DIR}/${TestName}Test.cmake" ${Arguments}) ENDMACRO(AddCMakeTest) AddCMakeTest(List "") +AddCMakeTest(VariableWatch "") diff --git a/Tests/CMakeTests/VariableWatchTest.cmake.in b/Tests/CMakeTests/VariableWatchTest.cmake.in new file mode 100644 index 000000000..0caeeb073 --- /dev/null +++ b/Tests/CMakeTests/VariableWatchTest.cmake.in @@ -0,0 +1,22 @@ +MESSAGE("Start") + +VARIABLE_WATCH(TESTVAR MESSAGE) +VARIABLE_WATCH(TESTVAR1) + +macro(testwatch var access file stack) + MESSAGE("There was a ${access} access done on the variable: ${var} in file ${file}") + MESSAGE("List file stack is: ${stack}") +endmacro(testwatch) + +VARIABLE_WATCH(somevar testwatch) + +set(TESTVAR1 "1") +set(TESTVAR "1") +set(TESTVAR1 "0") +set(TESTVAR "0") + + +message("Variable: ${somevar}") +set(somevar "1") +message("Variable: ${somevar}") +remove(somevar)