diff --git a/Help/manual/cmake-server.7.rst b/Help/manual/cmake-server.7.rst index 75aa0ee17..00ffcd130 100644 --- a/Help/manual/cmake-server.7.rst +++ b/Help/manual/cmake-server.7.rst @@ -49,12 +49,16 @@ Operation Start :manual:`cmake(1)` in the server command mode, supplying the path to the build directory to process:: - cmake -E server + cmake -E server (--debug|--pipe ) -The server will start up and reply with an hello message on stdout:: +The server will communicate using stdin/stdout (with the ``--debug`` parameter) +or using a named pipe (with the ``--pipe `` parameter). + +When connecting to the server (via named pipe or by starting it in ``--debug`` +mode), the server will reply with a hello message:: [== CMake Server ==[ - {"supportedProtocolVersions":[{"major":0,"minor":1}],"type":"hello"} + {"supportedProtocolVersions":[{"major":1,"minor":0}],"type":"hello"} ]== CMake Server ==] Messages sent to and from the process are wrapped in magic strings:: @@ -65,7 +69,8 @@ Messages sent to and from the process are wrapped in magic strings:: } ]== CMake Server ==] -The server is now ready to accept further requests via stdin. +The server is now ready to accept further requests via the named pipe +or stdin. Debugging diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index a2dead644..5e136e4f5 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -789,6 +789,7 @@ target_link_libraries(cmake CMakeLib) if(CMake_HAVE_SERVER_MODE) add_library(CMakeServerLib cmServer.cxx cmServer.h + cmServerConnection.cxx cmServerConnection.h cmServerProtocol.cxx cmServerProtocol.h ) target_link_libraries(CMakeServerLib CMakeLib) diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index be001a776..56cd7ba53 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -13,6 +13,7 @@ #include "cmServer.h" +#include "cmServerConnection.h" #include "cmServerProtocol.h" #include "cmSystemTools.h" #include "cmVersionMacros.h" @@ -40,57 +41,6 @@ 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 -{ - 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(malloc(suggested_size)), - static_cast(suggested_size)); -} - -void free_write_req(uv_write_t* req) -{ - write_req_t* wr = reinterpret_cast(req); - free(wr->buf.base); - free(wr); -} - -void on_stdout_write(uv_write_t* req, int status) -{ - (void)status; - auto server = reinterpret_cast(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(malloc(sizeof(write_req_t))); - req->req.data = dest->data; - req->buf = uv_buf_init(static_cast(malloc(content.size())), - static_cast(content.size())); - memcpy(req->buf.base, content.c_str(), content.size()); - uv_write(reinterpret_cast(req), static_cast(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(stream->data); - std::string result = std::string(buf->base, buf->base + nread); - server->handleData(result); - } - - if (buf->base) - free(buf->base); -} - class cmServer::DebugInfo { public: @@ -105,9 +55,11 @@ public: uint64_t StartTime; }; -cmServer::cmServer(bool supportExperimental) - : SupportExperimental(supportExperimental) +cmServer::cmServer(cmServerConnection* conn, bool supportExperimental) + : Connection(conn) + , SupportExperimental(supportExperimental) { + this->Connection->SetServer(this); // Register supported protocols: this->RegisterProtocol(new cmServerProtocol1_0); } @@ -118,18 +70,15 @@ cmServer::~cmServer() return; } - uv_close(reinterpret_cast(this->InputStream), NULL); - uv_close(reinterpret_cast(this->OutputStream), NULL); - uv_loop_close(this->Loop); - for (cmServerProtocol* p : this->SupportedProtocols) { delete p; } + + delete this->Connection; } void cmServer::PopOne() { - this->Writing = false; if (this->Queue.empty()) { return; } @@ -173,39 +122,6 @@ void cmServer::PopOne() } } -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 == kSTART_MAGIC) { - this->JsonData.clear(); - continue; - } - if (line == kEND_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) { if (protocol->IsExperimental() && !this->SupportExperimental) { @@ -245,6 +161,12 @@ void cmServer::PrintHello() const this->WriteJsonObject(hello, nullptr); } +void cmServer::QueueRequest(const std::string& request) +{ + this->Queue.push_back(request); + this->PopOne(); +} + void cmServer::reportProgress(const char* msg, float progress, void* data) { const cmServerRequest* request = static_cast(data); @@ -312,43 +234,16 @@ cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request) return request.Reply(Json::objectValue); } -bool cmServer::Serve() +bool cmServer::Serve(std::string* errorMessage) { if (this->SupportedProtocols.empty()) { + *errorMessage = + "No protocol versions defined. Maybe you need --experimental?"; return false; } 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(&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(&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(&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(&this->Output.pipe); - } - - this->PrintHello(); - - uv_read_start(this->InputStream, alloc_buffer, read_stdin); - - uv_run(this->Loop, UV_RUN_DEFAULT); - return true; + return Connection->ProcessEvents(errorMessage); } void cmServer::WriteJsonObject(const Json::Value& jsonValue, @@ -385,10 +280,8 @@ void cmServer::WriteJsonObject(const Json::Value& jsonValue, } } - this->Writing = true; - write_data(this->OutputStream, std::string("\n") + kSTART_MAGIC + - std::string("\n") + result + kEND_MAGIC + std::string("\n"), - on_stdout_write); + Connection->WriteData(std::string("\n") + kSTART_MAGIC + std::string("\n") + + result + kEND_MAGIC + std::string("\n")); } cmServerProtocol* cmServer::FindMatchingProtocol( diff --git a/Source/cmServer.h b/Source/cmServer.h index 38a11bbca..dde5333ca 100644 --- a/Source/cmServer.h +++ b/Source/cmServer.h @@ -24,6 +24,7 @@ #include #include +class cmServerConnection; class cmServerProtocol; class cmServerRequest; class cmServerResponse; @@ -33,18 +34,18 @@ class cmServer public: class DebugInfo; - cmServer(bool supportExperimental); + cmServer(cmServerConnection* conn, bool supportExperimental); ~cmServer(); - bool Serve(); - - // for callbacks: - void PopOne(); - void handleData(std::string const& data); + bool Serve(std::string* errorMessage); private: void RegisterProtocol(cmServerProtocol* protocol); + // Callbacks from cmServerConnection: + void PopOne(); + void QueueRequest(const std::string& request); + static void reportProgress(const char* msg, float progress, void* data); static void reportMessage(const char* msg, const char* title, bool& cancel, void* data); @@ -69,6 +70,7 @@ private: static cmServerProtocol* FindMatchingProtocol( const std::vector& protocols, int major, int minor); + cmServerConnection* Connection = nullptr; const bool SupportExperimental; cmServerProtocol* Protocol = nullptr; @@ -94,4 +96,5 @@ private: mutable bool Writing = false; friend class cmServerRequest; + friend class cmServerConnection; }; diff --git a/Source/cmServerConnection.cxx b/Source/cmServerConnection.cxx new file mode 100644 index 000000000..398e250ec --- /dev/null +++ b/Source/cmServerConnection.cxx @@ -0,0 +1,307 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2015 Stephen Kelly + Copyright 2016 Tobias Hunger + + 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 "cmServerConnection.h" + +#include + +#include + +namespace { + +static const std::string kSTART_MAGIC = "[== CMake Server ==["; +static const std::string kEND_MAGIC = "]== CMake Server ==]"; + +struct write_req_t +{ + uv_write_t req; + uv_buf_t buf; +}; + +void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) +{ + (void)(handle); + char* rawBuffer = new char[suggested_size]; + *buf = uv_buf_init(rawBuffer, static_cast(suggested_size)); +} + +void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) +{ + auto conn = reinterpret_cast(stream->data); + if (nread >= 0) { + conn->ReadData(std::string(buf->base, buf->base + nread)); + } else { + conn->HandleEof(); + } + + delete[](buf->base); +} + +void on_write(uv_write_t* req, int status) +{ + (void)(status); + auto conn = reinterpret_cast(req->data); + + // Free req and buffer + write_req_t* wr = reinterpret_cast(req); + delete[](wr->buf.base); + delete wr; + + conn->ProcessNextRequest(); +} + +void on_new_connection(uv_stream_t* stream, int status) +{ + (void)(status); + auto conn = reinterpret_cast(stream->data); + conn->Connect(stream); +} + +} // namespace + +class LoopGuard +{ +public: + LoopGuard(cmServerConnection* connection) + : Connection(connection) + { + Connection->mLoop = uv_default_loop(); + } + + ~LoopGuard() + { + uv_loop_close(Connection->mLoop); + Connection->mLoop = nullptr; + } + +private: + cmServerConnection* Connection; +}; + +cmServerConnection::cmServerConnection() +{ +} + +cmServerConnection::~cmServerConnection() +{ +} + +void cmServerConnection::SetServer(cmServer* s) +{ + this->Server = s; +} + +bool cmServerConnection::ProcessEvents(std::string* errorMessage) +{ + assert(this->Server); + errorMessage->clear(); + + this->RawReadBuffer.clear(); + this->RequestBuffer.clear(); + + LoopGuard guard(this); + (void)(guard); + if (!this->mLoop) { + *errorMessage = "Internal Error: Failed to create event loop."; + return false; + } + + if (!DoSetup(errorMessage)) { + return false; + } + + if (uv_run(this->mLoop, UV_RUN_DEFAULT) != 0) { + *errorMessage = "Internal Error: Event loop stopped in unclean state."; + return false; + } + + // These need to be cleaned up by now: + assert(!this->ReadStream); + assert(!this->WriteStream); + + this->RawReadBuffer.clear(); + this->RequestBuffer.clear(); + + return true; +} + +void cmServerConnection::ReadData(const std::string& data) +{ + this->RawReadBuffer += data; + + for (;;) { + auto needle = this->RawReadBuffer.find('\n'); + + if (needle == std::string::npos) { + return; + } + std::string line = this->RawReadBuffer.substr(0, needle); + const auto ls = line.size(); + if (ls > 1 && line.at(ls - 1) == '\r') + line.erase(ls - 1, 1); + this->RawReadBuffer.erase(this->RawReadBuffer.begin(), + this->RawReadBuffer.begin() + + static_cast(needle) + 1); + if (line == kSTART_MAGIC) { + this->RequestBuffer.clear(); + continue; + } + if (line == kEND_MAGIC) { + this->Server->QueueRequest(this->RequestBuffer); + this->RequestBuffer.clear(); + } else { + this->RequestBuffer += line; + this->RequestBuffer += "\n"; + } + } +} + +void cmServerConnection::HandleEof() +{ + this->TearDown(); +} + +void cmServerConnection::WriteData(const std::string& data) +{ + assert(this->WriteStream); + + auto ds = data.size(); + + write_req_t* req = new write_req_t; + req->req.data = this; + req->buf = uv_buf_init(new char[ds], static_cast(ds)); + memcpy(req->buf.base, data.c_str(), ds); + + uv_write(reinterpret_cast(req), + static_cast(this->WriteStream), &req->buf, 1, + on_write); +} + +void cmServerConnection::ProcessNextRequest() +{ + Server->PopOne(); +} + +void cmServerConnection::SendGreetings() +{ + Server->PrintHello(); +} + +bool cmServerStdIoConnection::DoSetup(std::string* errorMessage) +{ + (void)(errorMessage); + + 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); + Input.tty.data = this; + this->ReadStream = reinterpret_cast(&this->Input.tty); + + uv_tty_init(this->Loop(), &this->Output.tty, 1, 0); + uv_tty_set_mode(&this->Output.tty, UV_TTY_MODE_NORMAL); + Output.tty.data = this; + this->WriteStream = reinterpret_cast(&this->Output.tty); + } else { + uv_pipe_init(this->Loop(), &this->Input.pipe, 0); + uv_pipe_open(&this->Input.pipe, 0); + Input.pipe.data = this; + this->ReadStream = reinterpret_cast(&this->Input.pipe); + + uv_pipe_init(this->Loop(), &this->Output.pipe, 0); + uv_pipe_open(&this->Output.pipe, 1); + Output.pipe.data = this; + this->WriteStream = reinterpret_cast(&this->Output.pipe); + } + + SendGreetings(); + uv_read_start(this->ReadStream, on_alloc_buffer, on_read); + + return true; +} + +void cmServerStdIoConnection::TearDown() +{ + uv_close(reinterpret_cast(this->ReadStream), nullptr); + this->ReadStream = nullptr; + uv_close(reinterpret_cast(this->WriteStream), nullptr); + this->WriteStream = nullptr; +} + +cmServerPipeConnection::cmServerPipeConnection(const std::string& name) + : PipeName(name) +{ + this->ServerPipe.data = nullptr; + this->ClientPipe.data = nullptr; +} + +bool cmServerPipeConnection::DoSetup(std::string* errorMessage) +{ + uv_pipe_init(this->Loop(), &this->ServerPipe, 0); + this->ServerPipe.data = this; + + int r; + if ((r = uv_pipe_bind(&this->ServerPipe, this->PipeName.c_str())) != 0) { + *errorMessage = std::string("Internal Error with ") + this->PipeName + + ": " + uv_err_name(r); + return false; + } + auto serverStream = reinterpret_cast(&this->ServerPipe); + serverStream->data = this; + if ((r = uv_listen(serverStream, 1, on_new_connection)) != 0) { + *errorMessage = std::string("Internal Error with ") + this->PipeName + + ": " + uv_err_name(r); + return false; + } + + return true; +} + +void cmServerPipeConnection::TearDown() +{ + if (this->WriteStream->data) { + uv_close(reinterpret_cast(this->WriteStream), nullptr); + this->WriteStream->data = nullptr; + } + uv_close(reinterpret_cast(&this->ServerPipe), nullptr); + + this->WriteStream = nullptr; + this->ReadStream = nullptr; +} + +void cmServerPipeConnection::Connect(uv_stream_t* server) +{ + if (this->ClientPipe.data == this) { + // Accept and close all pipes but the first: + uv_pipe_t rejectPipe; + + uv_pipe_init(this->Loop(), &rejectPipe, 0); + auto rejecter = reinterpret_cast(&rejectPipe); + uv_accept(server, rejecter); + uv_close(reinterpret_cast(rejecter), nullptr); + return; + } + + uv_pipe_init(this->Loop(), &this->ClientPipe, 0); + this->ClientPipe.data = this; + auto client = reinterpret_cast(&this->ClientPipe); + if (uv_accept(server, client) != 0) { + uv_close(reinterpret_cast(client), nullptr); + return; + } + this->ReadStream = client; + this->WriteStream = client; + + uv_read_start(this->ReadStream, on_alloc_buffer, on_read); + + this->SendGreetings(); +} diff --git a/Source/cmServerConnection.h b/Source/cmServerConnection.h new file mode 100644 index 000000000..fa86e7191 --- /dev/null +++ b/Source/cmServerConnection.h @@ -0,0 +1,97 @@ +/*============================================================================ + CMake - Cross Platform Makefile Generator + Copyright 2015 Stephen Kelly + Copyright 2016 Tobias Hunger + + 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 +#include + +#if defined(CMAKE_BUILD_WITH_CMAKE) +#include "cm_uv.h" +#endif + +class cmServer; +class LoopGuard; + +class cmServerConnection +{ +public: + cmServerConnection(); + virtual ~cmServerConnection(); + + void SetServer(cmServer* s); + + bool ProcessEvents(std::string* errorMessage); + + void ReadData(const std::string& data); + void HandleEof(); + void WriteData(const std::string& data); + void ProcessNextRequest(); + + virtual void Connect(uv_stream_t* server) { (void)(server); } + +protected: + virtual bool DoSetup(std::string* errorMessage) = 0; + virtual void TearDown() = 0; + + void SendGreetings(); + + uv_loop_t* Loop() const { return mLoop; } + +protected: + std::string RawReadBuffer; + std::string RequestBuffer; + + uv_stream_t* ReadStream = nullptr; + uv_stream_t* WriteStream = nullptr; + +private: + uv_loop_t* mLoop = nullptr; + cmServer* Server = nullptr; + + friend class LoopGuard; +}; + +class cmServerStdIoConnection : public cmServerConnection +{ +public: + bool DoSetup(std::string* errorMessage) override; + + void TearDown() override; + +private: + typedef union + { + uv_tty_t tty; + uv_pipe_t pipe; + } InOutUnion; + + InOutUnion Input; + InOutUnion Output; +}; + +class cmServerPipeConnection : public cmServerConnection +{ +public: + cmServerPipeConnection(const std::string& name); + bool DoSetup(std::string* errorMessage) override; + + void TearDown() override; + + void Connect(uv_stream_t* server) override; + +private: + const std::string PipeName; + uv_pipe_t ServerPipe; + uv_pipe_t ClientPipe; +}; diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 38f00e61f..9daed4b44 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -25,6 +25,7 @@ #if defined(HAVE_SERVER_MODE) && HAVE_SERVER_MODE #include "cmServer.h" +#include "cmServerConnection.h" #endif #if defined(CMAKE_BUILD_WITH_CMAKE) @@ -913,32 +914,49 @@ int cmcmd::ExecuteCMakeCommand(std::vector& args) } return 0; } else if (args[1] == "server") { - if (args.size() > 3) { - cmSystemTools::Error("Too many arguments to start server mode"); - return 1; - } + const std::string pipePrefix = "--pipe="; bool supportExperimental = false; - if (args.size() == 3) { - if (args[2] == "--experimental") { + bool isDebug = false; + std::string pipe; + + for (size_t i = 2; i < args.size(); ++i) { + const std::string& a = args[i]; + + if (a == "--experimental") { supportExperimental = true; + } else if (a == "--debug") { + pipe.clear(); + isDebug = true; + } else if (a.substr(0, pipePrefix.size()) == pipePrefix) { + isDebug = false; + pipe = a.substr(pipePrefix.size()); + if (pipe.empty()) { + cmSystemTools::Error("No pipe given after --pipe="); + return 2; + } } else { cmSystemTools::Error("Unknown argument for server mode"); return 1; } } #if defined(HAVE_SERVER_MODE) && HAVE_SERVER_MODE - cmServer server(supportExperimental); - if (server.Serve()) { + cmServerConnection* conn; + if (isDebug) { + conn = new cmServerStdIoConnection; + } else { + conn = new cmServerPipeConnection(pipe); + } + cmServer server(conn, supportExperimental); + std::string errorMessage; + if (server.Serve(&errorMessage)) { return 0; } else { - cmSystemTools::Error( - "CMake server could not find any supported protocol. " - "Try with \"--experimental\" to enable " - "experimental support."); + cmSystemTools::Error(errorMessage.c_str()); return 1; } #else static_cast(supportExperimental); + static_cast(isDebug); cmSystemTools::Error("CMake was not built with server mode enabled"); return 1; #endif diff --git a/Tests/RunCMake/CommandLine/E_server-pipe-result.txt b/Tests/RunCMake/CommandLine/E_server-pipe-result.txt new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_server-pipe-result.txt @@ -0,0 +1 @@ +2 diff --git a/Tests/RunCMake/CommandLine/E_server-pipe-stderr.txt b/Tests/RunCMake/CommandLine/E_server-pipe-stderr.txt new file mode 100644 index 000000000..7193ba63b --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_server-pipe-stderr.txt @@ -0,0 +1 @@ +^CMake Error: No pipe given after --pipe=$ diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index 9f76ad910..0c4f71c89 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake @@ -13,6 +13,7 @@ run_cmake_command(E_capabilities-arg ${CMAKE_COMMAND} -E capabilities --extra-ar 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_server-pipe ${CMAKE_COMMAND} -E server --pipe=) 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") diff --git a/Tests/Server/cmakelib.py b/Tests/Server/cmakelib.py index e89b1f06e..0f9807898 100644 --- a/Tests/Server/cmakelib.py +++ b/Tests/Server/cmakelib.py @@ -79,7 +79,7 @@ def writePayload(cmakeCommand, obj): writeRawData(cmakeCommand, json.dumps(obj)) def initProc(cmakeCommand): - cmakeCommand = subprocess.Popen([cmakeCommand, "-E", "server", "--experimental"], + cmakeCommand = subprocess.Popen([cmakeCommand, "-E", "server", "--experimental", "--debug"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)