浏览代码

Merge branch 'dev/fix-variable-watch-crash' into cmake-syntax

Resolve conflict in Source/cmVariableWatchCommand.cxx by integrating the
changes from both sides.
Brad King 12 年之前
父节点
当前提交
b93982fb64

+ 38 - 12
Source/cmVariableWatch.cxx

@@ -37,37 +37,63 @@ cmVariableWatch::cmVariableWatch()
 
 cmVariableWatch::~cmVariableWatch()
 {
+  cmVariableWatch::StringToVectorOfPairs::iterator svp_it;
+
+  for ( svp_it = this->WatchMap.begin();
+        svp_it != this->WatchMap.end(); ++svp_it )
+    {
+    cmVariableWatch::VectorOfPairs::iterator p_it;
+
+    for ( p_it = svp_it->second.begin();
+          p_it != svp_it->second.end(); ++p_it )
+      {
+      delete *p_it;
+      }
+    }
 }
 
-void cmVariableWatch::AddWatch(const std::string& variable,
-                               WatchMethod method, void* client_data /*=0*/)
+bool cmVariableWatch::AddWatch(const std::string& variable,
+                               WatchMethod method, void* client_data /*=0*/,
+                               DeleteData delete_data /*=0*/)
 {
-  cmVariableWatch::Pair p;
-  p.Method = method;
-  p.ClientData = client_data;
+  cmVariableWatch::Pair* p = new cmVariableWatch::Pair;
+  p->Method = method;
+  p->ClientData = client_data;
+  p->DeleteDataCall = delete_data;
   cmVariableWatch::VectorOfPairs* vp = &this->WatchMap[variable];
   cmVariableWatch::VectorOfPairs::size_type cc;
   for ( cc = 0; cc < vp->size(); cc ++ )
     {
-    cmVariableWatch::Pair* pair = &(*vp)[cc];
-    if ( pair->Method == method )
+    cmVariableWatch::Pair* pair = (*vp)[cc];
+    if ( pair->Method == method &&
+         client_data && client_data == pair->ClientData)
       {
-      (*vp)[cc] = p;
-      return;
+      // Callback already exists
+      return false;
       }
     }
   vp->push_back(p);
+  return true;
 }
 
 void cmVariableWatch::RemoveWatch(const std::string& variable,
-                                  WatchMethod method)
+                                  WatchMethod method,
+                                  void* client_data /*=0*/)
 {
+  if ( !this->WatchMap.count(variable) )
+    {
+    return;
+    }
   cmVariableWatch::VectorOfPairs* vp = &this->WatchMap[variable];
   cmVariableWatch::VectorOfPairs::iterator it;
   for ( it = vp->begin(); it != vp->end(); ++it )
     {
-    if ( it->Method == method )
+    if ( (*it)->Method == method &&
+         // If client_data is NULL, we want to disconnect all watches against
+         // the given method; otherwise match ClientData as well.
+         (!client_data || (client_data == (*it)->ClientData)))
       {
+      delete *it;
       vp->erase(it);
       return;
       }
@@ -87,7 +113,7 @@ 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);
       }
     }

+ 15 - 5
Source/cmVariableWatch.h

@@ -26,6 +26,7 @@ class cmVariableWatch
 public:
   typedef void (*WatchMethod)(const std::string& variable, int access_type,
     void* client_data, const char* newValue, const cmMakefile* mf);
+  typedef void (*DeleteData)(void* client_data);
 
   cmVariableWatch();
   ~cmVariableWatch();
@@ -33,9 +34,10 @@ public:
   /**
    * Add watch to the variable
    */
-  void AddWatch(const std::string& variable, WatchMethod method,
-                void* client_data=0);
-  void RemoveWatch(const std::string& variable, WatchMethod method);
+  bool AddWatch(const std::string& variable, WatchMethod method,
+                void* client_data=0, DeleteData delete_data=0);
+  void RemoveWatch(const std::string& variable, WatchMethod method,
+                   void* client_data=0);
 
   /**
    * This method is called when variable is accessed
@@ -67,10 +69,18 @@ protected:
   {
     WatchMethod Method;
     void*        ClientData;
-    Pair() : Method(0), ClientData(0) {}
+    DeleteData   DeleteDataCall;
+    Pair() : Method(0), ClientData(0), DeleteDataCall(0) {}
+    ~Pair()
+      {
+      if (this->DeleteDataCall && this->ClientData)
+        {
+        this->DeleteDataCall(this->ClientData);
+        }
+      }
   };
 
-  typedef std::vector< Pair > VectorOfPairs;
+  typedef std::vector< Pair* > VectorOfPairs;
   typedef std::map<cmStdString, VectorOfPairs > StringToVectorOfPairs;
 
   StringToVectorOfPairs WatchMap;

+ 85 - 56
Source/cmVariableWatchCommand.cxx

@@ -14,63 +14,27 @@
 #include "cmVariableWatch.h"
 
 //----------------------------------------------------------------------------
-static void cmVariableWatchCommandVariableAccessed(
-  const std::string& variable, int access_type, void* client_data,
-  const char* newValue, const cmMakefile* mf)
+struct cmVariableWatchCallbackData
 {
-  cmVariableWatchCommand* command
-    = static_cast<cmVariableWatchCommand*>(client_data);
-  command->VariableAccessed(variable, access_type, newValue, mf);
-}
+  bool InCallback;
+  std::string Command;
+};
 
 //----------------------------------------------------------------------------
-cmVariableWatchCommand::cmVariableWatchCommand()
+static void cmVariableWatchCommandVariableAccessed(
+  const std::string& variable, int access_type, void* client_data,
+  const char* newValue, const cmMakefile* mf)
 {
-  this->InCallback = false;
-}
+  cmVariableWatchCallbackData* data
+    = static_cast<cmVariableWatchCallbackData*>(client_data);
 
-//----------------------------------------------------------------------------
-bool cmVariableWatchCommand
-::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
-{
-  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 )
+  if ( data->InCallback )
     {
     return;
     }
-  this->InCallback = true;
+  data->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);
@@ -80,10 +44,8 @@ void cmVariableWatchCommand::VariableAccessed(const std::string& variable,
   cmMakefile* makefile = const_cast<cmMakefile*>(mf);
 
   std::string stack = makefile->GetProperty("LISTFILE_STACK");
-  for ( it = handler->Commands.begin(); it != handler->Commands.end();
-    ++ it )
+  if ( !data->Command.empty() )
     {
-    std::string command = *it;
     newLFF.Arguments.clear();
     newLFF.Arguments.push_back(
       cmListFileArgument(variable, cmListFileArgument::Quoted,
@@ -100,7 +62,7 @@ void cmVariableWatchCommand::VariableAccessed(const std::string& variable,
     newLFF.Arguments.push_back(
       cmListFileArgument(stack, cmListFileArgument::Quoted,
                          "unknown", 9999));
-    newLFF.Name = command;
+    newLFF.Name = data->Command;
     newLFF.FilePath = "Some weird path";
     newLFF.Line = 9999;
     cmExecutionStatus status;
@@ -111,10 +73,10 @@ void cmVariableWatchCommand::VariableAccessed(const std::string& variable,
       cmOStringStream error;
       error << "Error in cmake code at\n"
         << arg.FilePath << ":" << arg.Line << ":\n"
-        << "A command failed during the invocation of callback\""
-        << command << "\".";
+        << "A command failed during the invocation of callback \""
+        << data->Command << "\".";
       cmSystemTools::Error(error.str().c_str());
-      this->InCallback = false;
+      data->InCallback = false;
       return;
       }
     processed = true;
@@ -123,8 +85,75 @@ void cmVariableWatchCommand::VariableAccessed(const std::string& variable,
     {
     cmOStringStream msg;
     msg << "Variable \"" << variable.c_str() << "\" was accessed using "
-        << accessString << " with value \"" << newValue << "\".";
+        << accessString << " with value \"" << (newValue?newValue:"") << "\".";
     makefile->IssueMessage(cmake::LOG, msg.str());
     }
-  this->InCallback = false;
+
+  data->InCallback = false;
+}
+
+//----------------------------------------------------------------------------
+static void deleteVariableWatchCallbackData(void* client_data)
+{
+  cmVariableWatchCallbackData* data
+    = static_cast<cmVariableWatchCallbackData*>(client_data);
+  delete data;
+}
+
+//----------------------------------------------------------------------------
+cmVariableWatchCommand::cmVariableWatchCommand()
+{
+}
+
+//----------------------------------------------------------------------------
+cmVariableWatchCommand::~cmVariableWatchCommand()
+{
+  std::set<std::string>::const_iterator it;
+  for ( it = this->WatchedVariables.begin();
+        it != this->WatchedVariables.end();
+        ++it )
+    {
+    this->Makefile->GetCMakeInstance()->GetVariableWatch()->RemoveWatch(
+      *it, cmVariableWatchCommandVariableAccessed);
+    }
+}
+
+//----------------------------------------------------------------------------
+bool cmVariableWatchCommand
+::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &)
+{
+  if ( args.size() < 1 )
+    {
+    this->SetError("must be called with at least one argument.");
+    return false;
+    }
+  std::string variable = args[0];
+  std::string command;
+  if ( args.size() > 1 )
+    {
+    command = 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;
+    }
+
+  cmVariableWatchCallbackData* data = new cmVariableWatchCallbackData;
+
+  data->InCallback = false;
+  data->Command = command;
+
+  this->WatchedVariables.insert(variable);
+  if ( !this->Makefile->GetCMakeInstance()->GetVariableWatch()->AddWatch(
+          variable, cmVariableWatchCommandVariableAccessed,
+          data, deleteVariableWatchCallbackData) )
+    {
+    deleteVariableWatchCallbackData(data);
+    return false;
+    }
+
+  return true;
 }

+ 4 - 13
Source/cmVariableWatchCommand.h

@@ -14,13 +14,6 @@
 
 #include "cmCommand.h"
 
-class cmVariableWatchCommandHandler
-{
-public:
-  typedef std::vector<std::string> VectorOfCommands;
-  VectorOfCommands Commands;
-};
-
 /** \class cmVariableWatchCommand
  * \brief Watch when the variable changes and invoke command
  *
@@ -39,6 +32,9 @@ public:
   //! Default constructor
   cmVariableWatchCommand();
 
+  //! Destructor.
+  ~cmVariableWatchCommand();
+
   /**
    * This is called when the command is first encountered in
    * the CMakeLists.txt file.
@@ -83,13 +79,8 @@ public:
 
   cmTypeMacro(cmVariableWatchCommand, cmCommand);
 
-  void VariableAccessed(const std::string& variable, int access_type,
-    const char* newValue, const cmMakefile* mf);
-
 protected:
-  std::map<std::string, cmVariableWatchCommandHandler> Handlers;
-
-  bool InCallback;
+  std::set<std::string> WatchedVariables;
 };
 
 

+ 1 - 0
Tests/RunCMake/variable_watch/RunCMakeTest.cmake

@@ -2,3 +2,4 @@ include(RunCMake)
 
 run_cmake(ModifiedAccess)
 run_cmake(NoWatcher)
+run_cmake(WatchTwice)

+ 2 - 0
Tests/RunCMake/variable_watch/WatchTwice-stderr.txt

@@ -0,0 +1,2 @@
+From watch1
+From watch2

+ 11 - 0
Tests/RunCMake/variable_watch/WatchTwice.cmake

@@ -0,0 +1,11 @@
+function (watch1)
+    message("From watch1")
+endfunction ()
+
+function (watch2)
+    message("From watch2")
+endfunction ()
+
+variable_watch(watched watch1)
+variable_watch(watched watch2)
+set(access "${watched}")