Merge topic 'cmake-server-more-info'
4fb2b41a
server-mode: Add debug support537efe05
server-mode: Report Messages from cmake to clientsca779948
server-mode: Automate progress reporting70b8ba9a
cmake-server: Use consistent constant naming style
This commit is contained in:
commit
eca2af6e66
|
@ -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.
|
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
|
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.
|
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
|
Specific Message Types
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "cmServer.h"
|
#include "cmServer.h"
|
||||||
|
|
||||||
#include "cmServerProtocol.h"
|
#include "cmServerProtocol.h"
|
||||||
|
#include "cmSystemTools.h"
|
||||||
#include "cmVersionMacros.h"
|
#include "cmVersionMacros.h"
|
||||||
#include "cmake.h"
|
#include "cmake.h"
|
||||||
|
|
||||||
|
@ -22,17 +23,22 @@
|
||||||
#include "cm_jsoncpp_value.h"
|
#include "cm_jsoncpp_value.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const char kTYPE_KEY[] = "type";
|
#include <fstream>
|
||||||
const char kCOOKIE_KEY[] = "cookie";
|
#include <iostream>
|
||||||
const char REPLY_TO_KEY[] = "inReplyTo";
|
#include <memory>
|
||||||
const char ERROR_MESSAGE_KEY[] = "errorMessage";
|
|
||||||
|
|
||||||
const char ERROR_TYPE[] = "error";
|
static const std::string kTYPE_KEY = "type";
|
||||||
const char REPLY_TYPE[] = "reply";
|
static const std::string kCOOKIE_KEY = "cookie";
|
||||||
const char PROGRESS_TYPE[] = "progress";
|
static const std::string kREPLY_TO_KEY = "inReplyTo";
|
||||||
|
static const std::string kERROR_MESSAGE_KEY = "errorMessage";
|
||||||
|
|
||||||
const char START_MAGIC[] = "[== CMake Server ==[";
|
static const std::string kERROR_TYPE = "error";
|
||||||
const char END_MAGIC[] = "]== CMake Server ==]";
|
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
|
typedef struct
|
||||||
{
|
{
|
||||||
|
@ -85,6 +91,20 @@ void read_stdin(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
|
||||||
free(buf->base);
|
free(buf->base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class cmServer::DebugInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DebugInfo()
|
||||||
|
: StartTime(uv_hrtime())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrintStatistics = false;
|
||||||
|
|
||||||
|
std::string OutputFile;
|
||||||
|
uint64_t StartTime;
|
||||||
|
};
|
||||||
|
|
||||||
cmServer::cmServer(bool supportExperimental)
|
cmServer::cmServer(bool supportExperimental)
|
||||||
: SupportExperimental(supportExperimental)
|
: SupportExperimental(supportExperimental)
|
||||||
{
|
{
|
||||||
|
@ -124,18 +144,33 @@ void cmServer::PopOne()
|
||||||
return;
|
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(),
|
const cmServerRequest request(this, value[kTYPE_KEY].asString(),
|
||||||
value[kCOOKIE_KEY].asString(), value);
|
value[kCOOKIE_KEY].asString(), value);
|
||||||
|
|
||||||
if (request.Type == "") {
|
if (request.Type == "") {
|
||||||
cmServerResponse response(request);
|
cmServerResponse response(request);
|
||||||
response.SetError("No type given in request.");
|
response.SetError("No type given in request.");
|
||||||
this->WriteResponse(response);
|
this->WriteResponse(response, nullptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->WriteResponse(this->Protocol ? this->Protocol->Process(request)
|
cmSystemTools::SetMessageCallback(reportMessage,
|
||||||
: this->SetProtocolVersion(request));
|
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)
|
void cmServer::handleData(const std::string& data)
|
||||||
|
@ -154,11 +189,11 @@ void cmServer::handleData(const std::string& data)
|
||||||
line.erase(ls - 1, 1);
|
line.erase(ls - 1, 1);
|
||||||
this->DataBuffer.erase(this->DataBuffer.begin(),
|
this->DataBuffer.erase(this->DataBuffer.begin(),
|
||||||
this->DataBuffer.begin() + needle + 1);
|
this->DataBuffer.begin() + needle + 1);
|
||||||
if (line == START_MAGIC) {
|
if (line == kSTART_MAGIC) {
|
||||||
this->JsonData.clear();
|
this->JsonData.clear();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line == END_MAGIC) {
|
if (line == kEND_MAGIC) {
|
||||||
this->Queue.push_back(this->JsonData);
|
this->Queue.push_back(this->JsonData);
|
||||||
this->JsonData.clear();
|
this->JsonData.clear();
|
||||||
if (!this->Writing) {
|
if (!this->Writing) {
|
||||||
|
@ -207,7 +242,31 @@ void cmServer::PrintHello() const
|
||||||
protocolVersions.append(tmp);
|
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)
|
cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request)
|
||||||
|
@ -292,16 +351,44 @@ bool cmServer::Serve()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmServer::WriteJsonObject(const Json::Value& jsonValue) const
|
void cmServer::WriteJsonObject(const Json::Value& jsonValue,
|
||||||
|
const DebugInfo* debug) const
|
||||||
{
|
{
|
||||||
Json::FastWriter writer;
|
Json::FastWriter writer;
|
||||||
|
|
||||||
std::string result = std::string("\n") + std::string(START_MAGIC) +
|
auto beforeJson = uv_hrtime();
|
||||||
std::string("\n") + writer.write(jsonValue) + std::string(END_MAGIC) +
|
std::string result = writer.write(jsonValue);
|
||||||
std::string("\n");
|
|
||||||
|
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;
|
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(
|
cmServerProtocol* cmServer::FindMatchingProtocol(
|
||||||
|
@ -328,39 +415,59 @@ void cmServer::WriteProgress(const cmServerRequest& request, int min,
|
||||||
assert(message.length() != 0);
|
assert(message.length() != 0);
|
||||||
|
|
||||||
Json::Value obj = Json::objectValue;
|
Json::Value obj = Json::objectValue;
|
||||||
obj[kTYPE_KEY] = PROGRESS_TYPE;
|
obj[kTYPE_KEY] = kPROGRESS_TYPE;
|
||||||
obj[REPLY_TO_KEY] = request.Type;
|
obj[kREPLY_TO_KEY] = request.Type;
|
||||||
obj[kCOOKIE_KEY] = request.Cookie;
|
obj[kCOOKIE_KEY] = request.Cookie;
|
||||||
obj["progressMessage"] = message;
|
obj["progressMessage"] = message;
|
||||||
obj["progressMinimum"] = min;
|
obj["progressMinimum"] = min;
|
||||||
obj["progressMaximum"] = max;
|
obj["progressMaximum"] = max;
|
||||||
obj["progressCurrent"] = current;
|
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
|
void cmServer::WriteParseError(const std::string& message) const
|
||||||
{
|
{
|
||||||
Json::Value obj = Json::objectValue;
|
Json::Value obj = Json::objectValue;
|
||||||
obj[kTYPE_KEY] = ERROR_TYPE;
|
obj[kTYPE_KEY] = kERROR_TYPE;
|
||||||
obj[ERROR_MESSAGE_KEY] = message;
|
obj[kERROR_MESSAGE_KEY] = message;
|
||||||
obj[REPLY_TO_KEY] = "";
|
obj[kREPLY_TO_KEY] = "";
|
||||||
obj[kCOOKIE_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());
|
assert(response.IsComplete());
|
||||||
|
|
||||||
Json::Value obj = response.Data();
|
Json::Value obj = response.Data();
|
||||||
obj[kCOOKIE_KEY] = response.Cookie;
|
obj[kCOOKIE_KEY] = response.Cookie;
|
||||||
obj[kTYPE_KEY] = response.IsError() ? ERROR_TYPE : REPLY_TYPE;
|
obj[kTYPE_KEY] = response.IsError() ? kERROR_TYPE : kREPLY_TYPE;
|
||||||
obj[REPLY_TO_KEY] = response.Type;
|
obj[kREPLY_TO_KEY] = response.Type;
|
||||||
if (response.IsError()) {
|
if (response.IsError()) {
|
||||||
obj[ERROR_MESSAGE_KEY] = response.ErrorMessage();
|
obj[kERROR_MESSAGE_KEY] = response.ErrorMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->WriteJsonObject(obj);
|
this->WriteJsonObject(obj, debug);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ class cmServerResponse;
|
||||||
class cmServer
|
class cmServer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
class DebugInfo;
|
||||||
|
|
||||||
cmServer(bool supportExperimental);
|
cmServer(bool supportExperimental);
|
||||||
~cmServer();
|
~cmServer();
|
||||||
|
|
||||||
|
@ -43,6 +45,10 @@ public:
|
||||||
private:
|
private:
|
||||||
void RegisterProtocol(cmServerProtocol* protocol);
|
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:
|
// Handle requests:
|
||||||
cmServerResponse SetProtocolVersion(const cmServerRequest& request);
|
cmServerResponse SetProtocolVersion(const cmServerRequest& request);
|
||||||
|
|
||||||
|
@ -51,10 +57,14 @@ private:
|
||||||
// Write responses:
|
// Write responses:
|
||||||
void WriteProgress(const cmServerRequest& request, int min, int current,
|
void WriteProgress(const cmServerRequest& request, int min, int current,
|
||||||
int max, const std::string& message) const;
|
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 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(
|
static cmServerProtocol* FindMatchingProtocol(
|
||||||
const std::vector<cmServerProtocol*>& protocols, int major, int minor);
|
const std::vector<cmServerProtocol*>& protocols, int major, int minor);
|
||||||
|
|
|
@ -22,17 +22,14 @@
|
||||||
#include "cm_jsoncpp_value.h"
|
#include "cm_jsoncpp_value.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace {
|
|
||||||
// Vocabulary:
|
// Vocabulary:
|
||||||
|
|
||||||
const std::string kBUILD_DIRECTORY_KEY = "buildDirectory";
|
static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory";
|
||||||
const std::string kCOOKIE_KEY = "cookie";
|
static const std::string kCOOKIE_KEY = "cookie";
|
||||||
const std::string kEXTRA_GENERATOR_KEY = "extraGenerator";
|
static const std::string kEXTRA_GENERATOR_KEY = "extraGenerator";
|
||||||
const std::string kGENERATOR_KEY = "generator";
|
static const std::string kGENERATOR_KEY = "generator";
|
||||||
const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory";
|
static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory";
|
||||||
const std::string kTYPE_KEY = "type";
|
static const std::string kTYPE_KEY = "type";
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
cmServerRequest::cmServerRequest(cmServer* server, const std::string& t,
|
cmServerRequest::cmServerRequest(cmServer* server, const std::string& t,
|
||||||
const std::string& c, const Json::Value& d)
|
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);
|
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 cmServerRequest::Reply(const Json::Value& data) const
|
||||||
{
|
{
|
||||||
cmServerResponse response(*this);
|
cmServerResponse response(*this);
|
||||||
|
|
|
@ -57,9 +57,6 @@ private:
|
||||||
class cmServerRequest
|
class cmServerRequest
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void ReportProgress(int min, int current, int max,
|
|
||||||
const std::string& message) const;
|
|
||||||
|
|
||||||
cmServerResponse Reply(const Json::Value& data) const;
|
cmServerResponse Reply(const Json::Value& data) const;
|
||||||
cmServerResponse ReportError(const std::string& message) const;
|
cmServerResponse ReportError(const std::string& message) const;
|
||||||
|
|
||||||
|
@ -71,6 +68,11 @@ private:
|
||||||
cmServerRequest(cmServer* server, const std::string& t, const std::string& c,
|
cmServerRequest(cmServer* server, const std::string& t, const std::string& c,
|
||||||
const Json::Value& d);
|
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;
|
cmServer* m_Server;
|
||||||
|
|
||||||
friend class cmServer;
|
friend class cmServer;
|
||||||
|
@ -95,6 +97,8 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<cmake> m_CMakeInstance;
|
std::unique_ptr<cmake> m_CMakeInstance;
|
||||||
|
|
||||||
|
friend class cmServer;
|
||||||
};
|
};
|
||||||
|
|
||||||
class cmServerProtocol1_0 : public cmServerProtocol
|
class cmServerProtocol1_0 : public cmServerProtocol
|
||||||
|
|
Loading…
Reference in New Issue