From 70b8ba9a0f4403729b9d68719503243982b883f4 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Tue, 20 Sep 2016 15:29:33 +0200 Subject: cmake-server: Use consistent constant naming style --- Source/cmServer.cxx | 43 +++++++++++++++++++++---------------------- Source/cmServerProtocol.cxx | 15 ++++++--------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index 208fac6..c0575af 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -22,17 +22,17 @@ #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"; +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 ERROR_TYPE[] = "error"; -const char REPLY_TYPE[] = "reply"; -const char PROGRESS_TYPE[] = "progress"; +static const std::string kERROR_TYPE = "error"; +static const std::string kREPLY_TYPE = "reply"; +static const std::string kPROGRESS_TYPE = "progress"; -const char START_MAGIC[] = "[== CMake Server ==["; -const char END_MAGIC[] = "]== CMake Server ==]"; +static const std::string kSTART_MAGIC = "[== CMake Server ==["; +static const std::string kEND_MAGIC = "]== CMake Server ==]"; typedef struct { @@ -154,11 +154,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) { @@ -296,9 +296,8 @@ void cmServer::WriteJsonObject(const Json::Value& jsonValue) 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"); + std::string result = std::string("\n") + kSTART_MAGIC + std::string("\n") + + writer.write(jsonValue) + kEND_MAGIC + std::string("\n"); this->Writing = true; write_data(this->OutputStream, result, on_stdout_write); @@ -328,8 +327,8 @@ 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; @@ -342,9 +341,9 @@ void cmServer::WriteProgress(const cmServerRequest& request, int min, 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); @@ -356,10 +355,10 @@ void cmServer::WriteResponse(const cmServerResponse& response) const 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); diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index d53ac28..ce6be83 100644 --- a/Source/cmServerProtocol.cxx +++ b/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) -- cgit v0.12 From ca7799482cbbb38b667fd5d045a0fc95d09a8cf2 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 9 Sep 2016 10:01:44 +0200 Subject: server-mode: Automate progress reporting Wire up cmake::SetProgressUpdate to do progress reporting via the cmake server. --- Source/cmServer.cxx | 20 ++++++++++++++++++-- Source/cmServer.h | 2 ++ Source/cmServerProtocol.h | 8 +++++--- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index c0575af..8fb452b 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -134,8 +134,13 @@ void cmServer::PopOne() return; } - this->WriteResponse(this->Protocol ? this->Protocol->Process(request) - : this->SetProtocolVersion(request)); + if (this->Protocol) { + this->Protocol->CMakeInstance()->SetProgressCallback( + reportProgress, const_cast(&request)); + this->WriteResponse(this->Protocol->Process(request)); + } else { + this->WriteResponse(this->SetProtocolVersion(request)); + } } void cmServer::handleData(const std::string& data) @@ -210,6 +215,17 @@ void cmServer::PrintHello() const this->WriteJsonObject(hello); } +void cmServer::reportProgress(const char* msg, float progress, void* data) +{ + const cmServerRequest* request = static_cast(data); + assert(request); + if (progress < 0.0 || progress > 1.0) { + request->ReportProgress(0, 0, 0, msg); + } else { + request->ReportProgress(0, static_cast(progress * 1000), 1000, msg); + } +} + cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request) { if (request.Type != "handshake") diff --git a/Source/cmServer.h b/Source/cmServer.h index 4a9c3f5..031ab64 100644 --- a/Source/cmServer.h +++ b/Source/cmServer.h @@ -43,6 +43,8 @@ public: private: void RegisterProtocol(cmServerProtocol* protocol); + static void reportProgress(const char* msg, float progress, void* data); + // Handle requests: cmServerResponse SetProtocolVersion(const cmServerRequest& request); diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h index e95c2f1..03d4300 100644 --- a/Source/cmServerProtocol.h +++ b/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,9 @@ 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; + cmServer* m_Server; friend class cmServer; @@ -95,6 +95,8 @@ protected: private: std::unique_ptr m_CMakeInstance; + + friend class cmServer; }; class cmServerProtocol1_0 : public cmServerProtocol -- cgit v0.12 From 537efe0561ef6a690fd7aac34ce1e4a46614f2e4 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 9 Sep 2016 10:01:44 +0200 Subject: server-mode: Report Messages from cmake to clients Pass messages sent from cmake via Error(...) or Message(...) on to clients. --- Help/manual/cmake-server.7.rst | 15 +++++++++++++++ Source/cmServer.cxx | 38 +++++++++++++++++++++++++++++++++++++- Source/cmServer.h | 4 ++++ Source/cmServerProtocol.cxx | 6 ++++++ Source/cmServerProtocol.h | 2 ++ 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/Help/manual/cmake-server.7.rst b/Help/manual/cmake-server.7.rst index fd0c9ee..7edb6d3 100644 --- a/Help/manual/cmake-server.7.rst +++ b/Help/manual/cmake-server.7.rst @@ -132,6 +132,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 ---------------------- diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index 8fb452b..5d1c73c 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -14,6 +14,7 @@ #include "cmServer.h" #include "cmServerProtocol.h" +#include "cmSystemTools.h" #include "cmVersionMacros.h" #include "cmake.h" @@ -30,6 +31,7 @@ static const std::string kERROR_MESSAGE_KEY = "errorMessage"; 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 ==]"; @@ -134,6 +136,8 @@ void cmServer::PopOne() return; } + cmSystemTools::SetMessageCallback(reportMessage, + const_cast(&request)); if (this->Protocol) { this->Protocol->CMakeInstance()->SetProgressCallback( reportProgress, const_cast(&request)); @@ -220,12 +224,25 @@ void cmServer::reportProgress(const char* msg, float progress, void* data) const cmServerRequest* request = static_cast(data); assert(request); if (progress < 0.0 || progress > 1.0) { - request->ReportProgress(0, 0, 0, msg); + request->ReportMessage(msg, ""); } else { request->ReportProgress(0, static_cast(progress * 1000), 1000, msg); } } +void cmServer::reportMessage(const char* msg, const char* title, + bool& /* cancel */, void* data) +{ + const cmServerRequest* request = static_cast(data); + assert(request); + assert(msg); + std::string titleString; + if (title) { + titleString = title; + } + request->ReportMessage(std::string(msg), titleString); +} + cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request) { if (request.Type != "handshake") @@ -354,6 +371,25 @@ void cmServer::WriteProgress(const cmServerRequest& request, int min, this->WriteJsonObject(obj); } +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); +} + void cmServer::WriteParseError(const std::string& message) const { Json::Value obj = Json::objectValue; diff --git a/Source/cmServer.h b/Source/cmServer.h index 031ab64..29b61bf 100644 --- a/Source/cmServer.h +++ b/Source/cmServer.h @@ -44,6 +44,8 @@ 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); @@ -53,6 +55,8 @@ private: // Write responses: void WriteProgress(const cmServerRequest& request, int min, int current, int max, const std::string& message) const; + void WriteMessage(const cmServerRequest& request, const std::string& message, + const std::string& title) const; void WriteResponse(const cmServerResponse& response) const; void WriteParseError(const std::string& message) const; diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index ce6be83..26942d3 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -46,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); diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h index 03d4300..bab949b 100644 --- a/Source/cmServerProtocol.h +++ b/Source/cmServerProtocol.h @@ -70,6 +70,8 @@ private: 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; -- cgit v0.12 From 4fb2b41a58fa3bd82649d8b19ecbe038e594e753 Mon Sep 17 00:00:00 2001 From: Tobias Hunger Date: Fri, 9 Sep 2016 10:01:44 +0200 Subject: server-mode: Add debug support Enable the server to support development with some helper tools: You can now request debug information with statistics on how long execution of a command took, how long it took to serialize the JSON files, and how big the serialized JSON string is. Also allow to dump results into a file. --- Help/manual/cmake-server.7.rst | 34 ++++++++++++++++++ Source/cmServer.cxx | 82 +++++++++++++++++++++++++++++++++++------- Source/cmServer.h | 8 +++-- 3 files changed, 109 insertions(+), 15 deletions(-) diff --git a/Help/manual/cmake-server.7.rst b/Help/manual/cmake-server.7.rst index 7edb6d3..75aa0ee 100644 --- a/Help/manual/cmake-server.7.rst +++ b/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 ============ diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index 5d1c73c..be001a7 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -23,6 +23,10 @@ #include "cm_jsoncpp_value.h" #endif +#include +#include +#include + static const std::string kTYPE_KEY = "type"; static const std::string kCOOKIE_KEY = "cookie"; static const std::string kREPLY_TO_KEY = "inReplyTo"; @@ -87,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) { @@ -126,13 +144,21 @@ void cmServer::PopOne() return; } + std::unique_ptr debug; + Json::Value debugValue = value["debug"]; + if (!debugValue.isNull()) { + debug = std::make_unique(); + 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; } @@ -141,9 +167,9 @@ void cmServer::PopOne() if (this->Protocol) { this->Protocol->CMakeInstance()->SetProgressCallback( reportProgress, const_cast(&request)); - this->WriteResponse(this->Protocol->Process(request)); + this->WriteResponse(this->Protocol->Process(request), debug.get()); } else { - this->WriteResponse(this->SetProtocolVersion(request)); + this->WriteResponse(this->SetProtocolVersion(request), debug.get()); } } @@ -216,7 +242,7 @@ void cmServer::PrintHello() const protocolVersions.append(tmp); } - this->WriteJsonObject(hello); + this->WriteJsonObject(hello, nullptr); } void cmServer::reportProgress(const char* msg, float progress, void* data) @@ -325,15 +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") + kSTART_MAGIC + std::string("\n") + - writer.write(jsonValue) + kEND_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(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( @@ -368,7 +423,7 @@ void cmServer::WriteProgress(const cmServerRequest& request, int min, obj["progressMaximum"] = max; obj["progressCurrent"] = current; - this->WriteJsonObject(obj); + this->WriteJsonObject(obj, nullptr); } void cmServer::WriteMessage(const cmServerRequest& request, @@ -387,7 +442,7 @@ void cmServer::WriteMessage(const cmServerRequest& request, obj["title"] = title; } - WriteJsonObject(obj); + WriteJsonObject(obj, nullptr); } void cmServer::WriteParseError(const std::string& message) const @@ -398,10 +453,11 @@ void cmServer::WriteParseError(const std::string& message) const 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()); @@ -413,5 +469,5 @@ void cmServer::WriteResponse(const cmServerResponse& response) const obj[kERROR_MESSAGE_KEY] = response.ErrorMessage(); } - this->WriteJsonObject(obj); + this->WriteJsonObject(obj, debug); } diff --git a/Source/cmServer.h b/Source/cmServer.h index 29b61bf..38a11bb 100644 --- a/Source/cmServer.h +++ b/Source/cmServer.h @@ -31,6 +31,8 @@ class cmServerResponse; class cmServer { public: + class DebugInfo; + cmServer(bool supportExperimental); ~cmServer(); @@ -57,10 +59,12 @@ private: int max, const std::string& message) const; void WriteMessage(const cmServerRequest& request, const std::string& message, const std::string& title) const; - void WriteResponse(const cmServerResponse& response) 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& protocols, int major, int minor); -- cgit v0.12