Răsfoiți Sursa

Merge topic 'cmake-server-more-info'

4fb2b41a server-mode: Add debug support
537efe05 server-mode: Report Messages from cmake to clients
ca779948 server-mode: Automate progress reporting
70b8ba9a cmake-server: Use consistent constant naming style
Brad King 9 ani în urmă
părinte
comite
eca2af6e66

+ 49 - 0
Help/manual/cmake-server.7.rst

@@ -68,6 +68,40 @@ Messages sent to and from the process are wrapped in magic strings::
 The server is now ready to accept further requests via stdin.
 
 
+Debugging
+=========
+
+CMake server mode can be asked to provide statistics on execution times, etc.
+or to dump a copy of the response into a file. This is done passing a "debug"
+JSON object as a child of the request.
+
+The debug object supports the "showStats" key, which takes a boolean and makes
+the server mode return a "zzzDebug" object with stats as part of its response.
+"dumpToFile" takes a string value and will cause the cmake server to copy
+the response into the given filename.
+
+This is a response from the cmake server with "showStats" set to true::
+
+  [== CMake Server ==[
+  {
+    "cookie":"",
+    "errorMessage":"Waiting for type \"handshake\".",
+    "inReplyTo":"unknown",
+   "type":"error",
+    "zzzDebug": {
+      "dumpFile":"/tmp/error.txt",
+      "jsonSerialization":0.011016,
+      "size":111,
+      "totalTime":0.025995
+    }
+  }
+  ]== CMake Server ==]
+
+The server has made a copy of this response into the file /tmp/error.txt and
+took 0.011 seconds to turn the JSON response into a string, and it took 0.025
+seconds to process the request in total. The reply has a size of 111 bytes.
+
+
 Protocol API
 ============
 
@@ -132,6 +166,21 @@ a message of type "reply" or "error" that complete the request.
 the request that triggered the responses was delivered.
 
 
+Type "message"
+^^^^^^^^^^^^^^
+
+A message is triggered when the server processes a request and produces some
+form of output that should be displayed to the user. A Message has a "message"
+with the actual text to display as well as a "title" with a suggested dialog
+box title.
+
+Example::
+
+  [== CMake Server ==[
+  {"cookie":"","message":"Something happened.","title":"Title Text","inReplyTo":"handshake","type":"message"}
+  ]== CMake Server ==]
+
+
 Specific Message Types
 ----------------------
 

+ 139 - 32
Source/cmServer.cxx

@@ -14,6 +14,7 @@
 #include "cmServer.h"
 
 #include "cmServerProtocol.h"
+#include "cmSystemTools.h"
 #include "cmVersionMacros.h"
 #include "cmake.h"
 
@@ -22,17 +23,22 @@
 #include "cm_jsoncpp_value.h"
 #endif
 
-const char kTYPE_KEY[] = "type";
-const char kCOOKIE_KEY[] = "cookie";
-const char REPLY_TO_KEY[] = "inReplyTo";
-const char ERROR_MESSAGE_KEY[] = "errorMessage";
+#include <fstream>
+#include <iostream>
+#include <memory>
 
-const char ERROR_TYPE[] = "error";
-const char REPLY_TYPE[] = "reply";
-const char PROGRESS_TYPE[] = "progress";
+static const std::string kTYPE_KEY = "type";
+static const std::string kCOOKIE_KEY = "cookie";
+static const std::string kREPLY_TO_KEY = "inReplyTo";
+static const std::string kERROR_MESSAGE_KEY = "errorMessage";
 
-const char START_MAGIC[] = "[== CMake Server ==[";
-const char END_MAGIC[] = "]== CMake Server ==]";
+static const std::string kERROR_TYPE = "error";
+static const std::string kREPLY_TYPE = "reply";
+static const std::string kPROGRESS_TYPE = "progress";
+static const std::string kMESSAGE_TYPE = "message";
+
+static const std::string kSTART_MAGIC = "[== CMake Server ==[";
+static const std::string kEND_MAGIC = "]== CMake Server ==]";
 
 typedef struct
 {
@@ -85,6 +91,20 @@ void read_stdin(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
     free(buf->base);
 }
 
+class cmServer::DebugInfo
+{
+public:
+  DebugInfo()
+    : StartTime(uv_hrtime())
+  {
+  }
+
+  bool PrintStatistics = false;
+
+  std::string OutputFile;
+  uint64_t StartTime;
+};
+
 cmServer::cmServer(bool supportExperimental)
   : SupportExperimental(supportExperimental)
 {
@@ -124,18 +144,33 @@ void cmServer::PopOne()
     return;
   }
 
+  std::unique_ptr<DebugInfo> debug;
+  Json::Value debugValue = value["debug"];
+  if (!debugValue.isNull()) {
+    debug = std::make_unique<DebugInfo>();
+    debug->OutputFile = debugValue["dumpToFile"].asString();
+    debug->PrintStatistics = debugValue["showStats"].asBool();
+  }
+
   const cmServerRequest request(this, value[kTYPE_KEY].asString(),
                                 value[kCOOKIE_KEY].asString(), value);
 
   if (request.Type == "") {
     cmServerResponse response(request);
     response.SetError("No type given in request.");
-    this->WriteResponse(response);
+    this->WriteResponse(response, nullptr);
     return;
   }
 
-  this->WriteResponse(this->Protocol ? this->Protocol->Process(request)
-                                     : this->SetProtocolVersion(request));
+  cmSystemTools::SetMessageCallback(reportMessage,
+                                    const_cast<cmServerRequest*>(&request));
+  if (this->Protocol) {
+    this->Protocol->CMakeInstance()->SetProgressCallback(
+      reportProgress, const_cast<cmServerRequest*>(&request));
+    this->WriteResponse(this->Protocol->Process(request), debug.get());
+  } else {
+    this->WriteResponse(this->SetProtocolVersion(request), debug.get());
+  }
 }
 
 void cmServer::handleData(const std::string& data)
@@ -154,11 +189,11 @@ void cmServer::handleData(const std::string& data)
       line.erase(ls - 1, 1);
     this->DataBuffer.erase(this->DataBuffer.begin(),
                            this->DataBuffer.begin() + needle + 1);
-    if (line == START_MAGIC) {
+    if (line == kSTART_MAGIC) {
       this->JsonData.clear();
       continue;
     }
-    if (line == END_MAGIC) {
+    if (line == kEND_MAGIC) {
       this->Queue.push_back(this->JsonData);
       this->JsonData.clear();
       if (!this->Writing) {
@@ -207,7 +242,31 @@ void cmServer::PrintHello() const
     protocolVersions.append(tmp);
   }
 
-  this->WriteJsonObject(hello);
+  this->WriteJsonObject(hello, nullptr);
+}
+
+void cmServer::reportProgress(const char* msg, float progress, void* data)
+{
+  const cmServerRequest* request = static_cast<const cmServerRequest*>(data);
+  assert(request);
+  if (progress < 0.0 || progress > 1.0) {
+    request->ReportMessage(msg, "");
+  } else {
+    request->ReportProgress(0, static_cast<int>(progress * 1000), 1000, msg);
+  }
+}
+
+void cmServer::reportMessage(const char* msg, const char* title,
+                             bool& /* cancel */, void* data)
+{
+  const cmServerRequest* request = static_cast<const cmServerRequest*>(data);
+  assert(request);
+  assert(msg);
+  std::string titleString;
+  if (title) {
+    titleString = title;
+  }
+  request->ReportMessage(std::string(msg), titleString);
 }
 
 cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request)
@@ -292,16 +351,44 @@ bool cmServer::Serve()
   return true;
 }
 
-void cmServer::WriteJsonObject(const Json::Value& jsonValue) const
+void cmServer::WriteJsonObject(const Json::Value& jsonValue,
+                               const DebugInfo* debug) const
 {
   Json::FastWriter writer;
 
-  std::string result = std::string("\n") + std::string(START_MAGIC) +
-    std::string("\n") + writer.write(jsonValue) + std::string(END_MAGIC) +
-    std::string("\n");
+  auto beforeJson = uv_hrtime();
+  std::string result = writer.write(jsonValue);
+
+  if (debug) {
+    Json::Value copy = jsonValue;
+    if (debug->PrintStatistics) {
+      Json::Value stats = Json::objectValue;
+      auto endTime = uv_hrtime();
+
+      stats["jsonSerialization"] = double(endTime - beforeJson) / 1000000.0;
+      stats["totalTime"] = double(endTime - debug->StartTime) / 1000000.0;
+      stats["size"] = static_cast<int>(result.size());
+      if (!debug->OutputFile.empty()) {
+        stats["dumpFile"] = debug->OutputFile;
+      }
+
+      copy["zzzDebug"] = stats;
+
+      result = writer.write(copy); // Update result to include debug info
+    }
+
+    if (!debug->OutputFile.empty()) {
+      std::ofstream myfile;
+      myfile.open(debug->OutputFile);
+      myfile << result;
+      myfile.close();
+    }
+  }
 
   this->Writing = true;
-  write_data(this->OutputStream, result, on_stdout_write);
+  write_data(this->OutputStream, std::string("\n") + kSTART_MAGIC +
+               std::string("\n") + result + kEND_MAGIC + std::string("\n"),
+             on_stdout_write);
 }
 
 cmServerProtocol* cmServer::FindMatchingProtocol(
@@ -328,39 +415,59 @@ void cmServer::WriteProgress(const cmServerRequest& request, int min,
   assert(message.length() != 0);
 
   Json::Value obj = Json::objectValue;
-  obj[kTYPE_KEY] = PROGRESS_TYPE;
-  obj[REPLY_TO_KEY] = request.Type;
+  obj[kTYPE_KEY] = kPROGRESS_TYPE;
+  obj[kREPLY_TO_KEY] = request.Type;
   obj[kCOOKIE_KEY] = request.Cookie;
   obj["progressMessage"] = message;
   obj["progressMinimum"] = min;
   obj["progressMaximum"] = max;
   obj["progressCurrent"] = current;
 
-  this->WriteJsonObject(obj);
+  this->WriteJsonObject(obj, nullptr);
+}
+
+void cmServer::WriteMessage(const cmServerRequest& request,
+                            const std::string& message,
+                            const std::string& title) const
+{
+  if (message.empty())
+    return;
+
+  Json::Value obj = Json::objectValue;
+  obj[kTYPE_KEY] = kMESSAGE_TYPE;
+  obj[kREPLY_TO_KEY] = request.Type;
+  obj[kCOOKIE_KEY] = request.Cookie;
+  obj["message"] = message;
+  if (!title.empty()) {
+    obj["title"] = title;
+  }
+
+  WriteJsonObject(obj, nullptr);
 }
 
 void cmServer::WriteParseError(const std::string& message) const
 {
   Json::Value obj = Json::objectValue;
-  obj[kTYPE_KEY] = ERROR_TYPE;
-  obj[ERROR_MESSAGE_KEY] = message;
-  obj[REPLY_TO_KEY] = "";
+  obj[kTYPE_KEY] = kERROR_TYPE;
+  obj[kERROR_MESSAGE_KEY] = message;
+  obj[kREPLY_TO_KEY] = "";
   obj[kCOOKIE_KEY] = "";
 
-  this->WriteJsonObject(obj);
+  this->WriteJsonObject(obj, nullptr);
 }
 
-void cmServer::WriteResponse(const cmServerResponse& response) const
+void cmServer::WriteResponse(const cmServerResponse& response,
+                             const DebugInfo* debug) const
 {
   assert(response.IsComplete());
 
   Json::Value obj = response.Data();
   obj[kCOOKIE_KEY] = response.Cookie;
-  obj[kTYPE_KEY] = response.IsError() ? ERROR_TYPE : REPLY_TYPE;
-  obj[REPLY_TO_KEY] = response.Type;
+  obj[kTYPE_KEY] = response.IsError() ? kERROR_TYPE : kREPLY_TYPE;
+  obj[kREPLY_TO_KEY] = response.Type;
   if (response.IsError()) {
-    obj[ERROR_MESSAGE_KEY] = response.ErrorMessage();
+    obj[kERROR_MESSAGE_KEY] = response.ErrorMessage();
   }
 
-  this->WriteJsonObject(obj);
+  this->WriteJsonObject(obj, debug);
 }

+ 12 - 2
Source/cmServer.h

@@ -31,6 +31,8 @@ class cmServerResponse;
 class cmServer
 {
 public:
+  class DebugInfo;
+
   cmServer(bool supportExperimental);
   ~cmServer();
 
@@ -43,6 +45,10 @@ public:
 private:
   void RegisterProtocol(cmServerProtocol* protocol);
 
+  static void reportProgress(const char* msg, float progress, void* data);
+  static void reportMessage(const char* msg, const char* title, bool& cancel,
+                            void* data);
+
   // Handle requests:
   cmServerResponse SetProtocolVersion(const cmServerRequest& request);
 
@@ -51,10 +57,14 @@ private:
   // Write responses:
   void WriteProgress(const cmServerRequest& request, int min, int current,
                      int max, const std::string& message) const;
-  void WriteResponse(const cmServerResponse& response) const;
+  void WriteMessage(const cmServerRequest& request, const std::string& message,
+                    const std::string& title) const;
+  void WriteResponse(const cmServerResponse& response,
+                     const DebugInfo* debug) const;
   void WriteParseError(const std::string& message) const;
 
-  void WriteJsonObject(Json::Value const& jsonValue) const;
+  void WriteJsonObject(Json::Value const& jsonValue,
+                       const DebugInfo* debug) const;
 
   static cmServerProtocol* FindMatchingProtocol(
     const std::vector<cmServerProtocol*>& protocols, int major, int minor);

+ 12 - 9
Source/cmServerProtocol.cxx

@@ -22,17 +22,14 @@
 #include "cm_jsoncpp_value.h"
 #endif
 
-namespace {
 // Vocabulary:
 
-const std::string kBUILD_DIRECTORY_KEY = "buildDirectory";
-const std::string kCOOKIE_KEY = "cookie";
-const std::string kEXTRA_GENERATOR_KEY = "extraGenerator";
-const std::string kGENERATOR_KEY = "generator";
-const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory";
-const std::string kTYPE_KEY = "type";
-
-} // namespace
+static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory";
+static const std::string kCOOKIE_KEY = "cookie";
+static const std::string kEXTRA_GENERATOR_KEY = "extraGenerator";
+static const std::string kGENERATOR_KEY = "generator";
+static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory";
+static const std::string kTYPE_KEY = "type";
 
 cmServerRequest::cmServerRequest(cmServer* server, const std::string& t,
                                  const std::string& c, const Json::Value& d)
@@ -49,6 +46,12 @@ void cmServerRequest::ReportProgress(int min, int current, int max,
   this->m_Server->WriteProgress(*this, min, current, max, message);
 }
 
+void cmServerRequest::ReportMessage(const std::string& message,
+                                    const std::string& title) const
+{
+  m_Server->WriteMessage(*this, message, title);
+}
+
 cmServerResponse cmServerRequest::Reply(const Json::Value& data) const
 {
   cmServerResponse response(*this);

+ 7 - 3
Source/cmServerProtocol.h

@@ -57,9 +57,6 @@ private:
 class cmServerRequest
 {
 public:
-  void ReportProgress(int min, int current, int max,
-                      const std::string& message) const;
-
   cmServerResponse Reply(const Json::Value& data) const;
   cmServerResponse ReportError(const std::string& message) const;
 
@@ -71,6 +68,11 @@ private:
   cmServerRequest(cmServer* server, const std::string& t, const std::string& c,
                   const Json::Value& d);
 
+  void ReportProgress(int min, int current, int max,
+                      const std::string& message) const;
+  void ReportMessage(const std::string& message,
+                     const std::string& title) const;
+
   cmServer* m_Server;
 
   friend class cmServer;
@@ -95,6 +97,8 @@ protected:
 
 private:
   std::unique_ptr<cmake> m_CMakeInstance;
+
+  friend class cmServer;
 };
 
 class cmServerProtocol1_0 : public cmServerProtocol