summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt12
-rw-r--r--Source/CMakeLists.txt11
-rw-r--r--Source/cmServer.cxx352
-rw-r--r--Source/cmServer.h85
-rw-r--r--Source/cmServerProtocol.cxx129
-rw-r--r--Source/cmServerProtocol.h97
-rw-r--r--Source/cmcmd.cxx18
-rw-r--r--Tests/RunCMake/CommandLine/E_server-arg-result.txt1
-rw-r--r--Tests/RunCMake/CommandLine/E_server-arg-stderr.txt1
-rw-r--r--Tests/RunCMake/CommandLine/RunCMakeTest.cmake1
10 files changed, 707 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c8bd063..2ec8b57 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -702,6 +702,18 @@ endif()
# setup some Testing support (a macro defined in this file)
CMAKE_SETUP_TESTING()
+# Check whether to build server mode or not:
+set(CMake_HAVE_SERVER_MODE 0)
+if(NOT CMake_TEST_EXTERNAL_CMAKE AND NOT CMAKE_BOOTSTRAP AND CMAKE_USE_LIBUV)
+ list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_auto_type CMake_HAVE_CXX_AUTO_TYPE)
+ list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_range_for CMake_HAVE_CXX_RANGE_FOR)
+ if(CMake_HAVE_CXX_AUTO_TYPE AND CMake_HAVE_CXX_RANGE_FOR)
+ if(CMake_HAVE_CXX_MAKE_UNIQUE)
+ set(CMake_HAVE_SERVER_MODE 1)
+ endif()
+ endif()
+endif()
+
if(NOT CMake_TEST_EXTERNAL_CMAKE)
if(NOT CMake_VERSION_IS_RELEASE)
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 39773e1..a2dead6 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -786,6 +786,17 @@ add_executable(cmake cmakemain.cxx cmcmd.cxx cmcmd.h ${MANIFEST_FILE})
list(APPEND _tools cmake)
target_link_libraries(cmake CMakeLib)
+if(CMake_HAVE_SERVER_MODE)
+ add_library(CMakeServerLib
+ cmServer.cxx cmServer.h
+ cmServerProtocol.cxx cmServerProtocol.h
+ )
+ target_link_libraries(CMakeServerLib CMakeLib)
+ set_property(SOURCE cmcmd.cxx APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SERVER_MODE=1)
+
+ target_link_libraries(cmake CMakeServerLib)
+endif()
+
# Build CTest executable
add_executable(ctest ctest.cxx ${MANIFEST_FILE})
list(APPEND _tools ctest)
diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx
new file mode 100644
index 0000000..7643b47
--- /dev/null
+++ b/Source/cmServer.cxx
@@ -0,0 +1,352 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2015 Stephen Kelly <steveire@gmail.com>
+ Copyright 2016 Tobias Hunger <tobias.hunger@qt.io>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+
+#include "cmServer.h"
+
+#include "cmServerProtocol.h"
+#include "cmVersionMacros.h"
+#include "cmake.h"
+
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+#include "cm_jsoncpp_reader.h"
+#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";
+
+const char ERROR_TYPE[] = "error";
+const char REPLY_TYPE[] = "reply";
+const char PROGRESS_TYPE[] = "progress";
+
+const char START_MAGIC[] = "[== CMake Server ==[";
+const char END_MAGIC[] = "]== CMake Server ==]";
+
+typedef struct
+{
+ uv_write_t req;
+ uv_buf_t buf;
+} write_req_t;
+
+void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
+{
+ (void)handle;
+ *buf = uv_buf_init(static_cast<char*>(malloc(suggested_size)),
+ static_cast<unsigned int>(suggested_size));
+}
+
+void free_write_req(uv_write_t* req)
+{
+ write_req_t* wr = reinterpret_cast<write_req_t*>(req);
+ free(wr->buf.base);
+ free(wr);
+}
+
+void on_stdout_write(uv_write_t* req, int status)
+{
+ (void)status;
+ auto server = reinterpret_cast<cmServer*>(req->data);
+ free_write_req(req);
+ server->PopOne();
+}
+
+void write_data(uv_stream_t* dest, std::string content, uv_write_cb cb)
+{
+ write_req_t* req = static_cast<write_req_t*>(malloc(sizeof(write_req_t)));
+ req->req.data = dest->data;
+ req->buf = uv_buf_init(static_cast<char*>(malloc(content.size())),
+ static_cast<unsigned int>(content.size()));
+ memcpy(req->buf.base, content.c_str(), content.size());
+ uv_write(reinterpret_cast<uv_write_t*>(req), static_cast<uv_stream_t*>(dest),
+ &req->buf, 1, cb);
+}
+
+void read_stdin(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
+{
+ if (nread > 0) {
+ auto server = reinterpret_cast<cmServer*>(stream->data);
+ std::string result = std::string(buf->base, buf->base + nread);
+ server->handleData(result);
+ }
+
+ if (buf->base)
+ free(buf->base);
+}
+
+cmServer::cmServer()
+{
+}
+
+cmServer::~cmServer()
+{
+ if (!this->Protocol) // Daemon was never fully started!
+ return;
+
+ uv_close(reinterpret_cast<uv_handle_t*>(this->InputStream), NULL);
+ uv_close(reinterpret_cast<uv_handle_t*>(this->OutputStream), NULL);
+ uv_loop_close(this->Loop);
+
+ for (cmServerProtocol* p : this->SupportedProtocols) {
+ delete p;
+ }
+}
+
+void cmServer::PopOne()
+{
+ this->Writing = false;
+ if (this->Queue.empty()) {
+ return;
+ }
+
+ Json::Reader reader;
+ Json::Value value;
+ const std::string input = this->Queue.front();
+ this->Queue.erase(this->Queue.begin());
+
+ if (!reader.parse(input, value)) {
+ this->WriteParseError("Failed to parse JSON input.");
+ return;
+ }
+
+ 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);
+ return;
+ }
+
+ this->WriteResponse(this->Protocol ? this->Protocol->Process(request)
+ : this->SetProtocolVersion(request));
+}
+
+void cmServer::handleData(const std::string& data)
+{
+ this->DataBuffer += data;
+
+ for (;;) {
+ auto needle = this->DataBuffer.find('\n');
+
+ if (needle == std::string::npos) {
+ return;
+ }
+ std::string line = this->DataBuffer.substr(0, needle);
+ const auto ls = line.size();
+ if (ls > 1 && line.at(ls - 1) == '\r')
+ line.erase(ls - 1, 1);
+ this->DataBuffer.erase(this->DataBuffer.begin(),
+ this->DataBuffer.begin() + needle + 1);
+ if (line == START_MAGIC) {
+ this->JsonData.clear();
+ continue;
+ }
+ if (line == END_MAGIC) {
+ this->Queue.push_back(this->JsonData);
+ this->JsonData.clear();
+ if (!this->Writing) {
+ this->PopOne();
+ }
+ } else {
+ this->JsonData += line;
+ this->JsonData += "\n";
+ }
+ }
+}
+
+void cmServer::RegisterProtocol(cmServerProtocol* protocol)
+{
+ auto version = protocol->ProtocolVersion();
+ assert(version.first >= 0);
+ assert(version.second >= 0);
+ auto it = std::find_if(this->SupportedProtocols.begin(),
+ this->SupportedProtocols.end(),
+ [version](cmServerProtocol* p) {
+ return p->ProtocolVersion() == version;
+ });
+ if (it == this->SupportedProtocols.end())
+ this->SupportedProtocols.push_back(protocol);
+}
+
+void cmServer::PrintHello() const
+{
+ Json::Value hello = Json::objectValue;
+ hello[kTYPE_KEY] = "hello";
+
+ Json::Value& protocolVersions = hello["supportedProtocolVersions"] =
+ Json::arrayValue;
+
+ for (auto const& proto : this->SupportedProtocols) {
+ auto version = proto->ProtocolVersion();
+ Json::Value tmp = Json::objectValue;
+ tmp["major"] = version.first;
+ tmp["minor"] = version.second;
+ protocolVersions.append(tmp);
+ }
+
+ this->WriteJsonObject(hello);
+}
+
+cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request)
+{
+ if (request.Type != "handshake")
+ return request.ReportError("Waiting for type \"handshake\".");
+
+ Json::Value requestedProtocolVersion = request.Data["protocolVersion"];
+ if (requestedProtocolVersion.isNull())
+ return request.ReportError(
+ "\"protocolVersion\" is required for \"handshake\".");
+
+ if (!requestedProtocolVersion.isObject())
+ return request.ReportError("\"protocolVersion\" must be a JSON object.");
+
+ Json::Value majorValue = requestedProtocolVersion["major"];
+ if (!majorValue.isInt())
+ return request.ReportError("\"major\" must be set and an integer.");
+
+ Json::Value minorValue = requestedProtocolVersion["minor"];
+ if (!minorValue.isNull() && !minorValue.isInt())
+ return request.ReportError("\"minor\" must be unset or an integer.");
+
+ const int major = majorValue.asInt();
+ const int minor = minorValue.isNull() ? -1 : minorValue.asInt();
+ if (major < 0)
+ return request.ReportError("\"major\" must be >= 0.");
+ if (!minorValue.isNull() && minor < 0)
+ return request.ReportError("\"minor\" must be >= 0 when set.");
+
+ this->Protocol =
+ this->FindMatchingProtocol(this->SupportedProtocols, major, minor);
+ if (!this->Protocol) {
+ return request.ReportError("Protocol version not supported.");
+ }
+
+ std::string errorMessage;
+ if (!this->Protocol->Activate(request, &errorMessage)) {
+ this->Protocol = CM_NULLPTR;
+ return request.ReportError("Failed to activate protocol version: " +
+ errorMessage);
+ }
+ return request.Reply(Json::objectValue);
+}
+
+void cmServer::Serve()
+{
+ assert(!this->Protocol);
+
+ this->Loop = uv_default_loop();
+
+ if (uv_guess_handle(1) == UV_TTY) {
+ uv_tty_init(this->Loop, &this->Input.tty, 0, 1);
+ uv_tty_set_mode(&this->Input.tty, UV_TTY_MODE_NORMAL);
+ this->Input.tty.data = this;
+ InputStream = reinterpret_cast<uv_stream_t*>(&this->Input.tty);
+
+ uv_tty_init(this->Loop, &this->Output.tty, 1, 0);
+ uv_tty_set_mode(&this->Output.tty, UV_TTY_MODE_NORMAL);
+ this->Output.tty.data = this;
+ OutputStream = reinterpret_cast<uv_stream_t*>(&this->Output.tty);
+ } else {
+ uv_pipe_init(this->Loop, &this->Input.pipe, 0);
+ uv_pipe_open(&this->Input.pipe, 0);
+ this->Input.pipe.data = this;
+ InputStream = reinterpret_cast<uv_stream_t*>(&this->Input.pipe);
+
+ uv_pipe_init(this->Loop, &this->Output.pipe, 0);
+ uv_pipe_open(&this->Output.pipe, 1);
+ this->Output.pipe.data = this;
+ OutputStream = reinterpret_cast<uv_stream_t*>(&this->Output.pipe);
+ }
+
+ this->PrintHello();
+
+ uv_read_start(this->InputStream, alloc_buffer, read_stdin);
+
+ uv_run(this->Loop, UV_RUN_DEFAULT);
+}
+
+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");
+
+ this->Writing = true;
+ write_data(this->OutputStream, result, on_stdout_write);
+}
+
+cmServerProtocol* cmServer::FindMatchingProtocol(
+ const std::vector<cmServerProtocol*>& protocols, int major, int minor)
+{
+ cmServerProtocol* bestMatch = nullptr;
+ for (auto protocol : protocols) {
+ auto version = protocol->ProtocolVersion();
+ if (major != version.first)
+ continue;
+ if (minor == version.second)
+ return protocol;
+ if (!bestMatch || bestMatch->ProtocolVersion().second < version.second)
+ bestMatch = protocol;
+ }
+ return minor < 0 ? bestMatch : nullptr;
+}
+
+void cmServer::WriteProgress(const cmServerRequest& request, int min,
+ int current, int max,
+ const std::string& message) const
+{
+ assert(min <= current && current <= max);
+ assert(message.length() != 0);
+
+ Json::Value obj = Json::objectValue;
+ obj[kTYPE_KEY] = PROGRESS_TYPE;
+ obj[REPLY_TO_KEY] = request.Type;
+ obj[kCOOKIE_KEY] = request.Cookie;
+ obj["progressMessage"] = message;
+ obj["progressMinimum"] = min;
+ obj["progressMaximum"] = max;
+ obj["progressCurrent"] = current;
+
+ this->WriteJsonObject(obj);
+}
+
+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[kCOOKIE_KEY] = "";
+
+ this->WriteJsonObject(obj);
+}
+
+void cmServer::WriteResponse(const cmServerResponse& response) 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;
+ if (response.IsError()) {
+ obj[ERROR_MESSAGE_KEY] = response.ErrorMessage();
+ }
+
+ this->WriteJsonObject(obj);
+}
diff --git a/Source/cmServer.h b/Source/cmServer.h
new file mode 100644
index 0000000..0ef1e17
--- /dev/null
+++ b/Source/cmServer.h
@@ -0,0 +1,85 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2015 Stephen Kelly <steveire@gmail.com>
+ Copyright 2016 Tobias Hunger <tobias.hunger@qt.io>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+
+#pragma once
+
+#include "cmListFileCache.h"
+#include "cmState.h"
+
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+#include "cm_jsoncpp_value.h"
+#include "cm_uv.h"
+#endif
+
+#include <string>
+#include <vector>
+
+class cmServerProtocol;
+class cmServerRequest;
+class cmServerResponse;
+
+class cmServer
+{
+public:
+ cmServer();
+ ~cmServer();
+
+ void Serve();
+
+ // for callbacks:
+ void PopOne();
+ void handleData(std::string const& data);
+
+private:
+ void RegisterProtocol(cmServerProtocol* protocol);
+
+ // Handle requests:
+ cmServerResponse SetProtocolVersion(const cmServerRequest& request);
+
+ void PrintHello() const;
+
+ // 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 WriteParseError(const std::string& message) const;
+
+ void WriteJsonObject(Json::Value const& jsonValue) const;
+
+ static cmServerProtocol* FindMatchingProtocol(
+ const std::vector<cmServerProtocol*>& protocols, int major, int minor);
+
+ cmServerProtocol* Protocol = nullptr;
+ std::vector<cmServerProtocol*> SupportedProtocols;
+ std::vector<std::string> Queue;
+
+ std::string DataBuffer;
+ std::string JsonData;
+
+ uv_loop_t* Loop = nullptr;
+
+ typedef union
+ {
+ uv_tty_t tty;
+ uv_pipe_t pipe;
+ } InOutUnion;
+
+ InOutUnion Input;
+ InOutUnion Output;
+ uv_stream_t* InputStream = nullptr;
+ uv_stream_t* OutputStream = nullptr;
+
+ mutable bool Writing = false;
+
+ friend class cmServerRequest;
+};
diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx
new file mode 100644
index 0000000..659aa0f
--- /dev/null
+++ b/Source/cmServerProtocol.cxx
@@ -0,0 +1,129 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2016 Tobias Hunger <tobias.hunger@qt.io>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+
+#include "cmServerProtocol.h"
+
+#include "cmExternalMakefileProjectGenerator.h"
+#include "cmServer.h"
+#include "cmake.h"
+
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+#include "cm_jsoncpp_reader.h"
+#include "cm_jsoncpp_value.h"
+#endif
+
+namespace {
+// Vocabulary:
+
+const std::string kCOOKIE_KEY = "cookie";
+const std::string kTYPE_KEY = "type";
+
+} // namespace
+
+cmServerRequest::cmServerRequest(cmServer* server, const std::string& t,
+ const std::string& c, const Json::Value& d)
+ : Type(t)
+ , Cookie(c)
+ , Data(d)
+ , m_Server(server)
+{
+}
+
+void cmServerRequest::ReportProgress(int min, int current, int max,
+ const std::string& message) const
+{
+ this->m_Server->WriteProgress(*this, min, current, max, message);
+}
+
+cmServerResponse cmServerRequest::Reply(const Json::Value& data) const
+{
+ cmServerResponse response(*this);
+ response.SetData(data);
+ return response;
+}
+
+cmServerResponse cmServerRequest::ReportError(const std::string& message) const
+{
+ cmServerResponse response(*this);
+ response.SetError(message);
+ return response;
+}
+
+cmServerResponse::cmServerResponse(const cmServerRequest& request)
+ : Type(request.Type)
+ , Cookie(request.Cookie)
+{
+}
+
+void cmServerResponse::SetData(const Json::Value& data)
+{
+ assert(this->m_Payload == PAYLOAD_UNKNOWN);
+ if (!data[kCOOKIE_KEY].isNull() || !data[kTYPE_KEY].isNull()) {
+ this->SetError("Response contains cookie or type field.");
+ return;
+ }
+ this->m_Payload = PAYLOAD_DATA;
+ this->m_Data = data;
+}
+
+void cmServerResponse::SetError(const std::string& message)
+{
+ assert(this->m_Payload == PAYLOAD_UNKNOWN);
+ this->m_Payload = PAYLOAD_ERROR;
+ this->m_ErrorMessage = message;
+}
+
+bool cmServerResponse::IsComplete() const
+{
+ return this->m_Payload != PAYLOAD_UNKNOWN;
+}
+
+bool cmServerResponse::IsError() const
+{
+ assert(this->m_Payload != PAYLOAD_UNKNOWN);
+ return this->m_Payload == PAYLOAD_ERROR;
+}
+
+std::string cmServerResponse::ErrorMessage() const
+{
+ if (this->m_Payload == PAYLOAD_ERROR)
+ return this->m_ErrorMessage;
+ else
+ return std::string();
+}
+
+Json::Value cmServerResponse::Data() const
+{
+ assert(this->m_Payload != PAYLOAD_UNKNOWN);
+ return this->m_Data;
+}
+
+bool cmServerProtocol::Activate(const cmServerRequest& request,
+ std::string* errorMessage)
+{
+ this->m_CMakeInstance = std::make_unique<cmake>();
+ const bool result = this->DoActivate(request, errorMessage);
+ if (!result)
+ this->m_CMakeInstance = CM_NULLPTR;
+ return result;
+}
+
+cmake* cmServerProtocol::CMakeInstance() const
+{
+ return this->m_CMakeInstance.get();
+}
+
+bool cmServerProtocol::DoActivate(const cmServerRequest& /*request*/,
+ std::string* /*errorMessage*/)
+{
+ return true;
+}
diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h
new file mode 100644
index 0000000..e086f72
--- /dev/null
+++ b/Source/cmServerProtocol.h
@@ -0,0 +1,97 @@
+/*============================================================================
+ CMake - Cross Platform Makefile Generator
+ Copyright 2016 Tobias Hunger <tobias.hunger@qt.io>
+
+ Distributed under the OSI-approved BSD License (the "License");
+ see accompanying file Copyright.txt for details.
+
+ This software is distributed WITHOUT ANY WARRANTY; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the License for more information.
+============================================================================*/
+
+#pragma once
+
+#include "cmListFileCache.h"
+
+#if defined(CMAKE_BUILD_WITH_CMAKE)
+#include "cm_jsoncpp_writer.h"
+#endif
+
+#include <memory>
+#include <string>
+
+class cmake;
+class cmServer;
+
+class cmServerRequest;
+
+class cmServerResponse
+{
+public:
+ explicit cmServerResponse(const cmServerRequest& request);
+
+ void SetData(const Json::Value& data);
+ void SetError(const std::string& message);
+
+ bool IsComplete() const;
+ bool IsError() const;
+ std::string ErrorMessage() const;
+ Json::Value Data() const;
+
+ const std::string Type;
+ const std::string Cookie;
+
+private:
+ enum PayLoad
+ {
+ PAYLOAD_UNKNOWN,
+ PAYLOAD_ERROR,
+ PAYLOAD_DATA
+ };
+ PayLoad m_Payload = PAYLOAD_UNKNOWN;
+ std::string m_ErrorMessage;
+ Json::Value m_Data;
+};
+
+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;
+
+ const std::string Type;
+ const std::string Cookie;
+ const Json::Value Data;
+
+private:
+ cmServerRequest(cmServer* server, const std::string& t, const std::string& c,
+ const Json::Value& d);
+
+ cmServer* m_Server;
+
+ friend class cmServer;
+};
+
+class cmServerProtocol
+{
+public:
+ virtual ~cmServerProtocol() {}
+
+ virtual std::pair<int, int> ProtocolVersion() const = 0;
+ virtual const cmServerResponse Process(const cmServerRequest& request) = 0;
+
+ bool Activate(const cmServerRequest& request, std::string* errorMessage);
+
+protected:
+ cmake* CMakeInstance() const;
+ // Implement protocol specific activation tasks here. Called from Activate().
+ virtual bool DoActivate(const cmServerRequest& request,
+ std::string* errorMessage);
+
+private:
+ std::unique_ptr<cmake> m_CMakeInstance;
+};
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 3b385ab..c09ea8b 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -23,6 +23,10 @@
#include "cm_auto_ptr.hxx"
#include "cmake.h"
+#if defined(HAVE_SERVER_MODE) && HAVE_SERVER_MODE
+#include "cmServer.h"
+#endif
+
#if defined(CMAKE_BUILD_WITH_CMAKE)
#include "cmDependsFortran.h" // For -E cmake_copy_f90_mod callback.
#endif
@@ -91,6 +95,7 @@ void CMakeCommandUsage(const char* program)
<< " remove_directory dir - remove a directory and its contents\n"
<< " rename oldname newname - rename a file or directory "
"(on one volume)\n"
+ << " server - start cmake in server mode\n"
<< " sleep <number>... - sleep for given number of seconds\n"
<< " tar [cxt][vf][zjJ] file.tar [file/dir1 file/dir2 ...]\n"
<< " - create or extract a tar or zip archive\n"
@@ -907,6 +912,19 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string>& args)
#endif
}
return 0;
+ } else if (args[1] == "server") {
+ if (args.size() > 2) {
+ cmSystemTools::Error("Too many arguments to start server mode");
+ return 1;
+ }
+#if defined(HAVE_SERVER_MODE) && HAVE_SERVER_MODE
+ cmServer server;
+ server.Serve();
+ return 0;
+#else
+ cmSystemTools::Error("CMake was not built with server mode enabled");
+ return 1;
+#endif
}
#if defined(CMAKE_BUILD_WITH_CMAKE)
diff --git a/Tests/RunCMake/CommandLine/E_server-arg-result.txt b/Tests/RunCMake/CommandLine/E_server-arg-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/E_server-arg-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CommandLine/E_server-arg-stderr.txt b/Tests/RunCMake/CommandLine/E_server-arg-stderr.txt
new file mode 100644
index 0000000..7877c01
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/E_server-arg-stderr.txt
@@ -0,0 +1 @@
+^CMake Error: Too many arguments to start server mode$
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index 6ae47a8..9f76ad9 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -12,6 +12,7 @@ run_cmake_command(E_capabilities ${CMAKE_COMMAND} -E capabilities)
run_cmake_command(E_capabilities-arg ${CMAKE_COMMAND} -E capabilities --extra-arg)
run_cmake_command(E_echo_append ${CMAKE_COMMAND} -E echo_append)
run_cmake_command(E_rename-no-arg ${CMAKE_COMMAND} -E rename)
+run_cmake_command(E_server-arg ${CMAKE_COMMAND} -E server --extra-arg)
run_cmake_command(E_touch_nocreate-no-arg ${CMAKE_COMMAND} -E touch_nocreate)
run_cmake_command(E_time ${CMAKE_COMMAND} -E time ${CMAKE_COMMAND} -E echo "hello world")