diff options
46 files changed, 16 insertions, 5293 deletions
diff --git a/Help/guide/ide-integration/index.rst b/Help/guide/ide-integration/index.rst index b1b9fc4..46ad4ef 100644 --- a/Help/guide/ide-integration/index.rst +++ b/Help/guide/ide-integration/index.rst @@ -79,8 +79,8 @@ as the include directories, compile definitions, etc. used to build the artifacts. Such information can be obtained by using the :manual:`File API <cmake-file-api(7)>`. The manual page for the File API contains more information about the API and how to invoke it. -:manual:`Server mode <cmake-server(7)>` is deprecated and should not be -used on CMake 3.14 or later. +:manual:`Server mode <cmake-server(7)>` was removed as of CMake 3.20 and +should not be used on CMake 3.14 or later. IDEs should avoid creating more build trees than necessary, and only create multiple build trees if the user wishes to switch to a different compiler, diff --git a/Help/manual/cmake-server.7.rst b/Help/manual/cmake-server.7.rst index 8f10b9f..6c8d0f4 100644 --- a/Help/manual/cmake-server.7.rst +++ b/Help/manual/cmake-server.7.rst @@ -3,742 +3,5 @@ cmake-server(7) *************** -.. only:: html - - .. contents:: - -.. deprecated:: 3.15 - - This will be removed from a future version of CMake. - Clients should use the :manual:`cmake-file-api(7)` instead. - -Introduction -============ - -:manual:`cmake(1)` is capable of providing semantic information about -CMake code it executes to generate a buildsystem. If executed with -the ``-E server`` command line options, it starts in a long running mode -and allows a client to request the available information via a JSON protocol. - -The protocol is designed to be useful to IDEs, refactoring tools, and -other tools which have a need to understand the buildsystem in entirety. - -A single :manual:`cmake-buildsystem(7)` may describe buildsystem contents -and build properties which differ based on -:manual:`generation-time context <cmake-generator-expressions(7)>` -including: - -* The Platform (eg, Windows, APPLE, Linux). -* The build configuration (eg, Debug, Release, Coverage). -* The Compiler (eg, MSVC, GCC, Clang) and compiler version. -* The language of the source files compiled. -* Available compile features (eg CXX variadic templates). -* CMake policies. - -The protocol aims to provide information to tooling to satisfy several -needs: - -#. Provide a complete and easily parsed source of all information relevant - to the tooling as it relates to the source code. There should be no need - for tooling to parse generated buildsystems to access include directories - or compile definitions for example. -#. Semantic information about the CMake buildsystem itself. -#. Provide a stable interface for reading the information in the CMake cache. -#. Information for determining when cmake needs to be re-run as a result of - file changes. - - -Operation -========= - -Start :manual:`cmake(1)` in the server command mode, supplying the path to -the build directory to process:: - - cmake -E server (--debug|--pipe=<NAMED_PIPE>) - -The server will communicate using stdin/stdout (with the ``--debug`` parameter) -or using a named pipe (with the ``--pipe=<NAMED_PIPE>`` parameter). Note -that "named pipe" refers to a local domain socket on Unix and to a named pipe -on Windows. - -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":1,"minor":0}],"type":"hello"} - ]== "CMake Server" ==] - -Messages sent to and from the process are wrapped in magic strings:: - - [== "CMake Server" ==[ - { - ... some JSON message ... - } - ]== "CMake Server" ==] - -The server is now ready to accept further requests via the named pipe -or 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 -============ - - -General Message Layout ----------------------- - -All messages need to have a "type" value, which identifies the type of -message that is passed back or forth. E.g. the initial message sent by the -server is of type "hello". Messages without a type will generate an response -of type "error". - -All requests sent to the server may contain a "cookie" value. This value -will he handed back unchanged in all responses triggered by the request. - -All responses will contain a value "inReplyTo", which may be empty in -case of parse errors, but will contain the type of the request message -in all other cases. - - -Type "reply" -^^^^^^^^^^^^ - -This type is used by the server to reply to requests. - -The message may -- depending on the type of the original request -- -contain values. - -Example:: - - [== "CMake Server" ==[ - {"cookie":"zimtstern","inReplyTo":"handshake","type":"reply"} - ]== "CMake Server" ==] - - -Type "error" -^^^^^^^^^^^^ - -This type is used to return an error condition to the client. It will -contain an "errorMessage". - -Example:: - - [== "CMake Server" ==[ - {"cookie":"","errorMessage":"Protocol version not supported.","inReplyTo":"handshake","type":"error"} - ]== "CMake Server" ==] - - -Type "progress" -^^^^^^^^^^^^^^^ - -When the server is busy for a long time, it is polite to send back replies of -type "progress" to the client. These will contain a "progressMessage" with a -string describing the action currently taking place as well as -"progressMinimum", "progressMaximum" and "progressCurrent" with integer values -describing the range of progress. - -Messages of type "progress" will be followed by more "progress" messages or with -a message of type "reply" or "error" that complete the request. - -"progress" messages may not be emitted after the "reply" or "error" message for -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" ==] - - -Type "signal" -^^^^^^^^^^^^^ - -The server can send signals when it detects changes in the system state. Signals -are of type "signal", have an empty "cookie" and "inReplyTo" field and always -have a "name" set to show which signal was sent. - - -Specific Signals ----------------- - -The cmake server may sent signals with the following names: - -"dirty" Signal -^^^^^^^^^^^^^^ - -The "dirty" signal is sent whenever the server determines that the configuration -of the project is no longer up-to-date. This happens when any of the files that have -an influence on the build system is changed. - -The "dirty" signal may look like this:: - - [== "CMake Server" ==[ - { - "cookie":"", - "inReplyTo":"", - "name":"dirty", - "type":"signal"} - ]== "CMake Server" ==] - - -"fileChange" Signal -^^^^^^^^^^^^^^^^^^^ - -The "fileChange" signal is sent whenever a watched file is changed. It contains -the "path" that has changed and a list of "properties" with the kind of change -that was detected. Possible changes are "change" and "rename". - -The "fileChange" signal looks like this:: - - [== "CMake Server" ==[ - { - "cookie":"", - "inReplyTo":"", - "name":"fileChange", - "path":"/absolute/CMakeLists.txt", - "properties":["change"], - "type":"signal"} - ]== "CMake Server" ==] - - -Specific Message Types ----------------------- - - -Type "hello" -^^^^^^^^^^^^ - -The initial message send by the cmake server on startup is of type "hello". -This is the only message ever sent by the server that is not of type "reply", -"progress" or "error". - -It will contain "supportedProtocolVersions" with an array of server protocol -versions supported by the cmake server. These are JSON objects with "major" and -"minor" keys containing non-negative integer values. Some versions may be marked -as experimental. These will contain the "isExperimental" key set to true. Enabling -these requires a special command line argument when starting the cmake server mode. - -Within a "major" version all "minor" versions are fully backwards compatible. -New "minor" versions may introduce functionality in such a way that existing -clients of the same "major" version will continue to work, provided they -ignore keys in the output that they do not know about. - -Example:: - - [== "CMake Server" ==[ - {"supportedProtocolVersions":[{"major":0,"minor":1}],"type":"hello"} - ]== "CMake Server" ==] - - -Type "handshake" -^^^^^^^^^^^^^^^^ - -The first request that the client may send to the server is of type "handshake". - -This request needs to pass one of the "supportedProtocolVersions" of the "hello" -type response received earlier back to the server in the "protocolVersion" field. -Giving the "major" version of the requested protocol version will make the server -use the latest minor version of that protocol. Use this if you do not explicitly -need to depend on a specific minor version. - -Protocol version 1.0 requires the following attributes to be set: - - * "sourceDirectory" with a path to the sources - * "buildDirectory" with a path to the build directory - * "generator" with the generator name - * "extraGenerator" (optional!) with the extra generator to be used - * "platform" with the generator platform (if supported by the generator) - * "toolset" with the generator toolset (if supported by the generator) - -Protocol version 1.2 makes all but the build directory optional, provided -there is a valid cache in the build directory that contains all the other -information already. - -Example:: - - [== "CMake Server" ==[ - {"cookie":"zimtstern","type":"handshake","protocolVersion":{"major":0}, - "sourceDirectory":"/home/code/cmake", "buildDirectory":"/tmp/testbuild", - "generator":"Ninja"} - ]== "CMake Server" ==] - -which will result in a response type "reply":: - - [== "CMake Server" ==[ - {"cookie":"zimtstern","inReplyTo":"handshake","type":"reply"} - ]== "CMake Server" ==] - -indicating that the server is ready for action. - - -Type "globalSettings" -^^^^^^^^^^^^^^^^^^^^^ - -This request can be sent after the initial handshake. It will return a -JSON structure with information on cmake state. - -Example:: - - [== "CMake Server" ==[ - {"type":"globalSettings"} - ]== "CMake Server" ==] - -which will result in a response type "reply":: - - [== "CMake Server" ==[ - { - "buildDirectory": "/tmp/test-build", - "capabilities": { - "generators": [ - { - "extraGenerators": [], - "name": "Watcom WMake", - "platformSupport": false, - "toolsetSupport": false - }, - <...> - ], - "serverMode": false, - "version": { - "isDirty": false, - "major": 3, - "minor": 6, - "patch": 20160830, - "string": "3.6.20160830-gd6abad", - "suffix": "gd6abad" - } - }, - "checkSystemVars": false, - "cookie": "", - "extraGenerator": "", - "generator": "Ninja", - "debugOutput": false, - "inReplyTo": "globalSettings", - "sourceDirectory": "/home/code/cmake", - "trace": false, - "traceExpand": false, - "type": "reply", - "warnUninitialized": false, - "warnUnused": false, - "warnUnusedCli": true - } - ]== "CMake Server" ==] - - -Type "setGlobalSettings" -^^^^^^^^^^^^^^^^^^^^^^^^ - -This request can be sent to change the global settings attributes. Unknown -attributes are going to be ignored. Read-only attributes reported by -"globalSettings" are all capabilities, buildDirectory, generator, -extraGenerator and sourceDirectory. Any attempt to set these will be ignored, -too. - -All other settings will be changed. - -The server will respond with an empty reply message or an error. - -Example:: - - [== "CMake Server" ==[ - {"type":"setGlobalSettings","debugOutput":true} - ]== "CMake Server" ==] - -CMake will reply to this with:: - - [== "CMake Server" ==[ - {"inReplyTo":"setGlobalSettings","type":"reply"} - ]== "CMake Server" ==] - - -Type "configure" -^^^^^^^^^^^^^^^^ - -This request will configure a project for build. - -To configure a build directory already containing cmake files, it is enough to -set "buildDirectory" via "setGlobalSettings". To create a fresh build directory -you also need to set "currentGenerator" and "sourceDirectory" via "setGlobalSettings" -in addition to "buildDirectory". - -You may a list of strings to "configure" via the "cacheArguments" key. These -strings will be interpreted similar to command line arguments related to -cache handling that are passed to the cmake command line client. - -Example:: - - [== "CMake Server" ==[ - {"type":"configure", "cacheArguments":["-Dsomething=else"]} - ]== "CMake Server" ==] - -CMake will reply like this (after reporting progress for some time):: - - [== "CMake Server" ==[ - {"cookie":"","inReplyTo":"configure","type":"reply"} - ]== "CMake Server" ==] - - -Type "compute" -^^^^^^^^^^^^^^ - -This request will generate build system files in the build directory and -is only available after a project was successfully "configure"d. - -Example:: - - [== "CMake Server" ==[ - {"type":"compute"} - ]== "CMake Server" ==] - -CMake will reply (after reporting progress information):: - - [== "CMake Server" ==[ - {"cookie":"","inReplyTo":"compute","type":"reply"} - ]== "CMake Server" ==] - - -Type "codemodel" -^^^^^^^^^^^^^^^^ - -The "codemodel" request can be used after a project was "compute"d successfully. - -It will list the complete project structure as it is known to cmake. - -The reply will contain a key "configurations", which will contain a list of -configuration objects. Configuration objects are used to destinquish between -different configurations the build directory might have enabled. While most -generators only support one configuration, others might support several. - -Each configuration object can have the following keys: - -"name" - contains the name of the configuration. The name may be empty. -"projects" - contains a list of project objects, one for each build project. - -Project objects define one (sub-)project defined in the cmake build system. - -Each project object can have the following keys: - -"name" - contains the (sub-)projects name. -"minimumCMakeVersion" - contains the minimum cmake version allowed for this project, null if the - project doesn't specify one. -"hasInstallRule" - true if the project contains any install rules, false otherwise. -"sourceDirectory" - contains the current source directory -"buildDirectory" - contains the current build directory. -"targets" - contains a list of build system target objects. - -Target objects define individual build targets for a certain configuration. - -Each target object can have the following keys: - -"name" - contains the name of the target. -"type" - defines the type of build of the target. Possible values are - "STATIC_LIBRARY", "MODULE_LIBRARY", "SHARED_LIBRARY", "OBJECT_LIBRARY", - "EXECUTABLE", "UTILITY" and "INTERFACE_LIBRARY". -"fullName" - contains the full name of the build result (incl. extensions, etc.). -"sourceDirectory" - contains the current source directory. -"buildDirectory" - contains the current build directory. -"isGeneratorProvided" - true if the target is auto-created by a generator, false otherwise -"hasInstallRule" - true if the target contains any install rules, false otherwise. -"installPaths" - full path to the destination directories defined by target install rules. -"artifacts" - with a list of build artifacts. The list is sorted with the most - important artifacts first (e.g. a .DLL file is listed before a - .PDB file on windows). -"linkerLanguage" - contains the language of the linker used to produce the artifact. -"linkLibraries" - with a list of libraries to link to. This value is encoded in the - system's native shell format. -"linkFlags" - with a list of flags to pass to the linker. This value is encoded in - the system's native shell format. -"linkLanguageFlags" - with the flags for a compiler using the linkerLanguage. This value is - encoded in the system's native shell format. -"frameworkPath" - with the framework path (on Apple computers). This value is encoded - in the system's native shell format. -"linkPath" - with the link path. This value is encoded in the system's native shell - format. -"sysroot" - with the sysroot path. -"fileGroups" - contains the source files making up the target. - -FileGroups are used to group sources using similar settings together. - -Each fileGroup object may contain the following keys: - -"language" - contains the programming language used by all files in the group. -"compileFlags" - with a string containing all the flags passed to the compiler - when building any of the files in this group. This value is encoded in - the system's native shell format. -"includePath" - with a list of include paths. Each include path is an object - containing a "path" with the actual include path and "isSystem" with a bool - value informing whether this is a normal include or a system include. This - value is encoded in the system's native shell format. -"defines" - with a list of defines in the form "SOMEVALUE" or "SOMEVALUE=42". This - value is encoded in the system's native shell format. -"sources" - with a list of source files. - -All file paths in the fileGroup are either absolute or relative to the -sourceDirectory of the target. - -Example:: - - [== "CMake Server" ==[ - {"type":"codemodel"} - ]== "CMake Server" ==] - -CMake will reply:: - - [== "CMake Server" ==[ - { - "configurations": [ - { - "name": "", - "projects": [ - { - "buildDirectory": "/tmp/build/Source/CursesDialog/form", - "name": "CMAKE_FORM", - "sourceDirectory": "/home/code/src/cmake/Source/CursesDialog/form", - "targets": [ - { - "artifacts": [ "/tmp/build/Source/CursesDialog/form/libcmForm.a" ], - "buildDirectory": "/tmp/build/Source/CursesDialog/form", - "fileGroups": [ - { - "compileFlags": " -std=gnu11", - "defines": [ "CURL_STATICLIB", "LIBARCHIVE_STATIC" ], - "includePath": [ { "path": "/tmp/build/Utilities" }, <...> ], - "isGenerated": false, - "language": "C", - "sources": [ "fld_arg.c", <...> ] - } - ], - "fullName": "libcmForm.a", - "linkerLanguage": "C", - "name": "cmForm", - "sourceDirectory": "/home/code/src/cmake/Source/CursesDialog/form", - "type": "STATIC_LIBRARY" - } - ] - }, - <...> - ] - } - ], - "cookie": "", - "inReplyTo": "codemodel", - "type": "reply" - } - ]== "CMake Server" ==] - - -Type "ctestInfo" -^^^^^^^^^^^^^^^^ - -The "ctestInfo" request can be used after a project was "compute"d successfully. - -It will list the complete project test structure as it is known to cmake. - -The reply will contain a key "configurations", which will contain a list of -configuration objects. Configuration objects are used to destinquish between -different configurations the build directory might have enabled. While most -generators only support one configuration, others might support several. - -Each configuration object can have the following keys: - -"name" - contains the name of the configuration. The name may be empty. -"projects" - contains a list of project objects, one for each build project. - -Project objects define one (sub-)project defined in the cmake build system. - -Each project object can have the following keys: - -"name" - contains the (sub-)projects name. -"ctestInfo" - contains a list of test objects. - -Each test object can have the following keys: - -"ctestName" - contains the name of the test. -"ctestCommand" - contains the test command. -"properties" - contains a list of test property objects. - -Each test property object can have the following keys: - -"key" - contains the test property key. -"value" - contains the test property value. - - -Type "cmakeInputs" -^^^^^^^^^^^^^^^^^^ - -The "cmakeInputs" requests will report files used by CMake as part -of the build system itself. - -This request is only available after a project was successfully -"configure"d. - -Example:: - - [== "CMake Server" ==[ - {"type":"cmakeInputs"} - ]== "CMake Server" ==] - -CMake will reply with the following information:: - - [== "CMake Server" ==[ - {"buildFiles": - [ - {"isCMake":true,"isTemporary":false,"sources":["/usr/lib/cmake/...", ... ]}, - {"isCMake":false,"isTemporary":false,"sources":["CMakeLists.txt", ...]}, - {"isCMake":false,"isTemporary":true,"sources":["/tmp/build/CMakeFiles/...", ...]} - ], - "cmakeRootDirectory":"/usr/lib/cmake", - "sourceDirectory":"/home/code/src/cmake", - "cookie":"", - "inReplyTo":"cmakeInputs", - "type":"reply" - } - ]== "CMake Server" ==] - -All file names are either relative to the top level source directory or -absolute. - -The list of files which "isCMake" set to true are part of the cmake installation. - -The list of files witch "isTemporary" set to true are part of the build directory -and will not survive the build directory getting cleaned out. - - -Type "cache" -^^^^^^^^^^^^ - -The "cache" request will list the cached configuration values. - -Example:: - - [== "CMake Server" ==[ - {"type":"cache"} - ]== "CMake Server" ==] - -CMake will respond with the following output:: - - [== "CMake Server" ==[ - { - "cookie":"","inReplyTo":"cache","type":"reply", - "cache": - [ - { - "key":"SOMEVALUE", - "properties": - { - "ADVANCED":"1", - "HELPSTRING":"This is not helpful" - } - "type":"STRING", - "value":"TEST"} - ] - } - ]== "CMake Server" ==] - -The output can be limited to a list of keys by passing an array of key names -to the "keys" optional field of the "cache" request. - - -Type "fileSystemWatchers" -^^^^^^^^^^^^^^^^^^^^^^^^^ - -The server can watch the filesystem for changes. The "fileSystemWatchers" -command will report on the files and directories watched. - -Example:: - - [== "CMake Server" ==[ - {"type":"fileSystemWatchers"} - ]== "CMake Server" ==] - -CMake will respond with the following output:: - - [== "CMake Server" ==[ - { - "cookie":"","inReplyTo":"fileSystemWatchers","type":"reply", - "watchedFiles": [ "/absolute/path" ], - "watchedDirectories": [ "/absolute" ] - } - ]== "CMake Server" ==] +The :manual:`cmake(1)` server mode has been removed since CMake 3.20. +Clients should use the :manual:`cmake-file-api(7)` instead. diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst index b8a1b42..356ca38 100644 --- a/Help/manual/cmake.1.rst +++ b/Help/manual/cmake.1.rst @@ -906,6 +906,7 @@ Available commands are: ``serverMode`` ``true`` if cmake supports server-mode and ``false`` otherwise. + Always false since CMake 3.20. ``cat <files>...`` Concatenate files and print on the standard output. diff --git a/Help/release/dev/remove-server-mode.rst b/Help/release/dev/remove-server-mode.rst new file mode 100644 index 0000000..7c986b1 --- /dev/null +++ b/Help/release/dev/remove-server-mode.rst @@ -0,0 +1,5 @@ +remove-server-mode +------------------ + +* The :manual:`cmake-server(7)` mode has been removed. + Clients should use the :manual:`cmake-file-api(7)` instead. diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 2946022..91dd4f1 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -1156,20 +1156,6 @@ add_executable(cmake cmakemain.cxx cmcmd.cxx cmcmd.h ${MANIFEST_FILE}) list(APPEND _tools cmake) target_link_libraries(cmake CMakeLib) -add_library(CMakeServerLib - cmConnection.h cmConnection.cxx - cmFileMonitor.cxx cmFileMonitor.h - cmJsonObjectDictionary.h - cmJsonObjects.h - cmJsonObjects.cxx - cmPipeConnection.cxx cmPipeConnection.h - cmServer.cxx cmServer.h - cmServerConnection.cxx cmServerConnection.h - cmServerProtocol.cxx cmServerProtocol.h - ) -target_link_libraries(CMakeServerLib CMakeLib) -target_link_libraries(cmake CMakeServerLib) - # Build CTest executable add_executable(ctest ctest.cxx ${MANIFEST_FILE}) list(APPEND _tools ctest) diff --git a/Source/cmConnection.cxx b/Source/cmConnection.cxx deleted file mode 100644 index e4d0cf1..0000000 --- a/Source/cmConnection.cxx +++ /dev/null @@ -1,173 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmConnection.h" - -#include <cassert> -#include <cstring> - -#include <cm3p/uv.h> - -#include "cmServer.h" - -struct write_req_t -{ - uv_write_t req; - uv_buf_t buf; -}; - -void cmEventBasedConnection::on_alloc_buffer(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf) -{ - (void)(handle); -#ifndef __clang_analyzer__ - char* rawBuffer = new char[suggested_size]; - *buf = uv_buf_init(rawBuffer, static_cast<unsigned int>(suggested_size)); -#else - (void)(suggested_size); - (void)(buf); -#endif /* __clang_analyzer__ */ -} - -void cmEventBasedConnection::on_read(uv_stream_t* stream, ssize_t nread, - const uv_buf_t* buf) -{ - auto conn = static_cast<cmEventBasedConnection*>(stream->data); - if (conn) { - if (nread >= 0) { - conn->ReadData(std::string(buf->base, buf->base + nread)); - } else { - conn->OnDisconnect(static_cast<int>(nread)); - } - } - - delete[](buf->base); -} - -void cmEventBasedConnection::on_close(uv_handle_t* /*handle*/) -{ -} - -void cmEventBasedConnection::on_write(uv_write_t* req, int status) -{ - (void)(status); - - // Free req and buffer - write_req_t* wr = reinterpret_cast<write_req_t*>(req); - delete[](wr->buf.base); - delete wr; -} - -void cmEventBasedConnection::on_new_connection(uv_stream_t* stream, int status) -{ - (void)(status); - auto conn = static_cast<cmEventBasedConnection*>(stream->data); - - if (conn) { - conn->Connect(stream); - } -} - -bool cmEventBasedConnection::IsOpen() const -{ - return this->WriteStream != nullptr; -} - -void cmEventBasedConnection::WriteData(const std::string& _data) -{ -#ifndef NDEBUG - auto curr_thread_id = uv_thread_self(); - assert(this->Server); - assert(uv_thread_equal(&curr_thread_id, &this->Server->ServeThreadId)); -#endif - -#ifndef __clang_analyzer__ - auto data = _data; - assert(this->WriteStream.get()); - if (BufferStrategy) { - data = BufferStrategy->BufferOutMessage(data); - } - - 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<unsigned int>(ds)); - memcpy(req->buf.base, data.c_str(), ds); - uv_write(reinterpret_cast<uv_write_t*>(req), this->WriteStream, &req->buf, 1, - on_write); -#else - (void)(_data); -#endif /* __clang_analyzer__ */ -} - -void cmEventBasedConnection::ReadData(const std::string& data) -{ - this->RawReadBuffer += data; - if (BufferStrategy) { - std::string packet = BufferStrategy->BufferMessage(this->RawReadBuffer); - while (!packet.empty()) { - ProcessRequest(packet); - packet = BufferStrategy->BufferMessage(this->RawReadBuffer); - } - } else { - ProcessRequest(this->RawReadBuffer); - this->RawReadBuffer.clear(); - } -} - -cmEventBasedConnection::cmEventBasedConnection( - cmConnectionBufferStrategy* bufferStrategy) - : BufferStrategy(bufferStrategy) -{ -} - -void cmEventBasedConnection::Connect(uv_stream_t* server) -{ - (void)server; - Server->OnConnected(nullptr); -} - -void cmEventBasedConnection::OnDisconnect(int onerror) -{ - (void)onerror; - this->OnConnectionShuttingDown(); - if (this->Server) { - this->Server->OnDisconnect(this); - } -} - -cmConnection::~cmConnection() = default; - -bool cmConnection::OnConnectionShuttingDown() -{ - this->Server = nullptr; - return true; -} - -void cmConnection::SetServer(cmServerBase* s) -{ - Server = s; -} - -void cmConnection::ProcessRequest(const std::string& request) -{ - Server->ProcessRequest(this, request); -} - -bool cmConnection::OnServeStart(std::string* errString) -{ - (void)errString; - return true; -} - -bool cmEventBasedConnection::OnConnectionShuttingDown() -{ - if (this->WriteStream.get()) { - this->WriteStream->data = nullptr; - } - - WriteStream.reset(); - - return true; -} diff --git a/Source/cmConnection.h b/Source/cmConnection.h deleted file mode 100644 index 5335a7f..0000000 --- a/Source/cmConnection.h +++ /dev/null @@ -1,137 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ - -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <cstddef> -#include <memory> -#include <string> - -#include <cm3p/uv.h> - -#include "cmUVHandlePtr.h" - -class cmServerBase; - -/*** - * Given a sequence of bytes with any kind of buffering, instances of this - * class arrange logical chunks according to whatever the use case is for - * the connection. - */ -class cmConnectionBufferStrategy -{ -public: - virtual ~cmConnectionBufferStrategy(); - - /*** - * Called whenever with an active raw buffer. If a logical chunk - * becomes available, that chunk is returned and that portion is - * removed from the rawBuffer - * - * @param rawBuffer in/out parameter. Receive buffer; the buffer strategy is - * free to manipulate this buffer anyway it needs to. - * - * @return Next chunk from the stream. Returns the empty string if a chunk - * isn't ready yet. Users of this interface should repeatedly call this - * function until an empty string is returned since its entirely possible - * multiple chunks come in a single raw buffer. - */ - virtual std::string BufferMessage(std::string& rawBuffer) = 0; - - /*** - * Called to properly buffer an outgoing message. - * - * @param rawBuffer Message to format in the correct way - * - * @return Formatted message - */ - virtual std::string BufferOutMessage(const std::string& rawBuffer) const - { - return rawBuffer; - }; - /*** - * Resets the internal state of the buffering - */ - virtual void clear(); - - // TODO: There should be a callback / flag set for errors -}; - -class cmConnection -{ -public: - cmConnection() = default; - - cmConnection(cmConnection const&) = delete; - cmConnection& operator=(cmConnection const&) = delete; - - virtual void WriteData(const std::string& data) = 0; - - virtual ~cmConnection(); - - virtual bool OnConnectionShuttingDown(); - - virtual bool IsOpen() const = 0; - - virtual void SetServer(cmServerBase* s); - - virtual void ProcessRequest(const std::string& request); - - virtual bool OnServeStart(std::string* pString); - -protected: - cmServerBase* Server = nullptr; -}; - -/*** - * Abstraction of a connection; ties in event callbacks from libuv and notifies - * the server when appropriate - */ -class cmEventBasedConnection : public cmConnection -{ - -public: - /*** - * @param bufferStrategy If no strategy is given, it will process the raw - * chunks as they come in. The connection - * owns the pointer given. - */ - cmEventBasedConnection(cmConnectionBufferStrategy* bufferStrategy = nullptr); - - virtual void Connect(uv_stream_t* server); - - virtual void ReadData(const std::string& data); - - bool IsOpen() const override; - - void WriteData(const std::string& data) override; - bool OnConnectionShuttingDown() override; - - virtual void OnDisconnect(int errorCode); - - static void on_close(uv_handle_t* handle); - - template <typename T> - static void on_close_delete(uv_handle_t* handle) - { - delete reinterpret_cast<T*>(handle); - } - -protected: - cm::uv_stream_ptr WriteStream; - - std::string RawReadBuffer; - - std::unique_ptr<cmConnectionBufferStrategy> BufferStrategy; - - static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); - - static void on_write(uv_write_t* req, int status); - - static void on_new_connection(uv_stream_t* stream, int status); - - static void on_alloc_buffer(uv_handle_t* handle, size_t suggested_size, - uv_buf_t* buf); -}; diff --git a/Source/cmFileMonitor.cxx b/Source/cmFileMonitor.cxx deleted file mode 100644 index 8cfdb2d..0000000 --- a/Source/cmFileMonitor.cxx +++ /dev/null @@ -1,383 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmFileMonitor.h" - -#include <cassert> -#include <cstddef> -#include <unordered_map> -#include <utility> - -#include <cm/memory> - -#include "cmsys/SystemTools.hxx" - -namespace { -void on_directory_change(uv_fs_event_t* handle, const char* filename, - int events, int status); -void on_fs_close(uv_handle_t* handle); -} // namespace - -class cmIBaseWatcher -{ -public: - virtual ~cmIBaseWatcher() = default; - - virtual void Trigger(const std::string& pathSegment, int events, - int status) const = 0; - virtual std::string Path() const = 0; - virtual uv_loop_t* Loop() const = 0; - - virtual void StartWatching() = 0; - virtual void StopWatching() = 0; - - virtual std::vector<std::string> WatchedFiles() const = 0; - virtual std::vector<std::string> WatchedDirectories() const = 0; -}; - -class cmVirtualDirectoryWatcher : public cmIBaseWatcher -{ -public: - ~cmVirtualDirectoryWatcher() override = default; - - cmIBaseWatcher* Find(const std::string& ps) - { - const auto i = this->Children.find(ps); - return (i == this->Children.end()) ? nullptr : i->second.get(); - } - - void Trigger(const std::string& pathSegment, int events, - int status) const final - { - if (pathSegment.empty()) { - for (auto const& child : this->Children) { - child.second->Trigger(std::string(), events, status); - } - } else { - const auto i = this->Children.find(pathSegment); - if (i != this->Children.end()) { - i->second->Trigger(std::string(), events, status); - } - } - } - - void StartWatching() override - { - for (auto const& child : this->Children) { - child.second->StartWatching(); - } - } - - void StopWatching() override - { - for (auto const& child : this->Children) { - child.second->StopWatching(); - } - } - - std::vector<std::string> WatchedFiles() const final - { - std::vector<std::string> result; - for (auto const& child : this->Children) { - for (std::string const& f : child.second->WatchedFiles()) { - result.push_back(f); - } - } - return result; - } - - std::vector<std::string> WatchedDirectories() const override - { - std::vector<std::string> result; - for (auto const& child : this->Children) { - for (std::string const& dir : child.second->WatchedDirectories()) { - result.push_back(dir); - } - } - return result; - } - - void Reset() { this->Children.clear(); } - - void AddChildWatcher(const std::string& ps, cmIBaseWatcher* watcher) - { - assert(!ps.empty()); - assert(this->Children.find(ps) == this->Children.end()); - assert(watcher); - - this->Children.emplace(ps, std::unique_ptr<cmIBaseWatcher>(watcher)); - } - -private: - std::unordered_map<std::string, std::unique_ptr<cmIBaseWatcher>> - Children; // owned! -}; - -// Root of all the different (on windows!) root directories: -class cmRootWatcher : public cmVirtualDirectoryWatcher -{ -public: - cmRootWatcher(uv_loop_t* loop) - : mLoop(loop) - { - assert(loop); - } - - std::string Path() const final - { - assert(false); - return std::string(); - } - uv_loop_t* Loop() const final { return this->mLoop; } - -private: - uv_loop_t* const mLoop; // no ownership! -}; - -// Real directories: -class cmRealDirectoryWatcher : public cmVirtualDirectoryWatcher -{ -public: - cmRealDirectoryWatcher(cmVirtualDirectoryWatcher* p, const std::string& ps) - : Parent(p) - , PathSegment(ps) - { - assert(p); - assert(!ps.empty()); - - p->AddChildWatcher(ps, this); - } - - void StartWatching() final - { - if (!this->Handle) { - this->Handle = new uv_fs_event_t; - - uv_fs_event_init(this->Loop(), this->Handle); - this->Handle->data = this; - uv_fs_event_start(this->Handle, &on_directory_change, Path().c_str(), 0); - } - cmVirtualDirectoryWatcher::StartWatching(); - } - - void StopWatching() final - { - if (this->Handle) { - uv_fs_event_stop(this->Handle); - if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(this->Handle))) { - uv_close(reinterpret_cast<uv_handle_t*>(this->Handle), &on_fs_close); - } - this->Handle = nullptr; - } - cmVirtualDirectoryWatcher::StopWatching(); - } - - uv_loop_t* Loop() const final { return this->Parent->Loop(); } - - std::vector<std::string> WatchedDirectories() const override - { - std::vector<std::string> result = { Path() }; - for (std::string const& dir : - cmVirtualDirectoryWatcher::WatchedDirectories()) { - result.push_back(dir); - } - return result; - } - -protected: - cmVirtualDirectoryWatcher* const Parent; - const std::string PathSegment; - -private: - uv_fs_event_t* Handle = nullptr; // owner! -}; - -// Root directories: -class cmRootDirectoryWatcher : public cmRealDirectoryWatcher -{ -public: - cmRootDirectoryWatcher(cmRootWatcher* p, const std::string& ps) - : cmRealDirectoryWatcher(p, ps) - { - } - - std::string Path() const final { return this->PathSegment; } -}; - -// Normal directories below root: -class cmDirectoryWatcher : public cmRealDirectoryWatcher -{ -public: - cmDirectoryWatcher(cmRealDirectoryWatcher* p, const std::string& ps) - : cmRealDirectoryWatcher(p, ps) - { - } - - std::string Path() const final - { - return this->Parent->Path() + this->PathSegment + "/"; - } -}; - -class cmFileWatcher : public cmIBaseWatcher -{ -public: - cmFileWatcher(cmRealDirectoryWatcher* p, const std::string& ps, - cmFileMonitor::Callback cb) - : Parent(p) - , PathSegment(ps) - , CbList({ std::move(cb) }) - { - assert(p); - assert(!ps.empty()); - p->AddChildWatcher(ps, this); - } - - void StartWatching() final {} - - void StopWatching() final {} - - void AppendCallback(cmFileMonitor::Callback const& cb) - { - this->CbList.push_back(cb); - } - - std::string Path() const final - { - return this->Parent->Path() + this->PathSegment; - } - - std::vector<std::string> WatchedDirectories() const final { return {}; } - - std::vector<std::string> WatchedFiles() const final - { - return { this->Path() }; - } - - void Trigger(const std::string& ps, int events, int status) const final - { - assert(ps.empty()); - assert(status == 0); - static_cast<void>(ps); - - const std::string path = this->Path(); - for (cmFileMonitor::Callback const& cb : this->CbList) { - cb(path, events, status); - } - } - - uv_loop_t* Loop() const final { return this->Parent->Loop(); } - -private: - cmRealDirectoryWatcher* Parent; - const std::string PathSegment; - std::vector<cmFileMonitor::Callback> CbList; -}; - -namespace { - -void on_directory_change(uv_fs_event_t* handle, const char* filename, - int events, int status) -{ - const cmIBaseWatcher* const watcher = - static_cast<const cmIBaseWatcher*>(handle->data); - const std::string pathSegment(filename ? filename : ""); - watcher->Trigger(pathSegment, events, status); -} - -void on_fs_close(uv_handle_t* handle) -{ - delete reinterpret_cast<uv_fs_event_t*>(handle); -} - -} // namespace - -cmFileMonitor::cmFileMonitor(uv_loop_t* l) - : Root(cm::make_unique<cmRootWatcher>(l)) -{ -} - -cmFileMonitor::~cmFileMonitor() = default; - -void cmFileMonitor::MonitorPaths(const std::vector<std::string>& paths, - Callback const& cb) -{ - for (std::string const& p : paths) { - std::vector<std::string> pathSegments; - cmsys::SystemTools::SplitPath(p, pathSegments, true); - const bool pathIsFile = !cmsys::SystemTools::FileIsDirectory(p); - - const size_t segmentCount = pathSegments.size(); - if (segmentCount < 2) { // Expect at least rootdir and filename - continue; - } - cmVirtualDirectoryWatcher* currentWatcher = this->Root.get(); - for (size_t i = 0; i < segmentCount; ++i) { - assert(currentWatcher); - - const bool fileSegment = (i == segmentCount - 1 && pathIsFile); - const bool rootSegment = (i == 0); - assert( - !(fileSegment && - rootSegment)); // Can not be both filename and root part of the path! - - const std::string& currentSegment = pathSegments[i]; - if (currentSegment.empty()) { - continue; - } - - cmIBaseWatcher* nextWatcher = currentWatcher->Find(currentSegment); - if (!nextWatcher) { - if (rootSegment) { // Root part - assert(currentWatcher == this->Root.get()); - nextWatcher = - new cmRootDirectoryWatcher(this->Root.get(), currentSegment); - assert(currentWatcher->Find(currentSegment) == nextWatcher); - } else if (fileSegment) { // File part - assert(currentWatcher != this->Root.get()); - nextWatcher = new cmFileWatcher( - dynamic_cast<cmRealDirectoryWatcher*>(currentWatcher), - currentSegment, cb); - assert(currentWatcher->Find(currentSegment) == nextWatcher); - } else { // Any normal directory in between - nextWatcher = new cmDirectoryWatcher( - dynamic_cast<cmRealDirectoryWatcher*>(currentWatcher), - currentSegment); - assert(currentWatcher->Find(currentSegment) == nextWatcher); - } - } else { - if (fileSegment) { - auto filePtr = dynamic_cast<cmFileWatcher*>(nextWatcher); - assert(filePtr); - filePtr->AppendCallback(cb); - continue; - } - } - currentWatcher = dynamic_cast<cmVirtualDirectoryWatcher*>(nextWatcher); - } - } - this->Root->StartWatching(); -} - -void cmFileMonitor::StopMonitoring() -{ - this->Root->StopWatching(); - this->Root->Reset(); -} - -std::vector<std::string> cmFileMonitor::WatchedFiles() const -{ - std::vector<std::string> result; - if (this->Root) { - result = this->Root->WatchedFiles(); - } - return result; -} - -std::vector<std::string> cmFileMonitor::WatchedDirectories() const -{ - std::vector<std::string> result; - if (this->Root) { - result = this->Root->WatchedDirectories(); - } - return result; -} diff --git a/Source/cmFileMonitor.h b/Source/cmFileMonitor.h deleted file mode 100644 index fc75b0c..0000000 --- a/Source/cmFileMonitor.h +++ /dev/null @@ -1,35 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <functional> -#include <memory> -#include <string> -#include <vector> - -#include <cm3p/uv.h> - -class cmRootWatcher; - -class cmFileMonitor -{ - -public: - cmFileMonitor(uv_loop_t* l); - ~cmFileMonitor(); - - cmFileMonitor(cmFileMonitor const&) = delete; - cmFileMonitor& operator=(cmFileMonitor const&) = delete; - - using Callback = std::function<void(const std::string&, int, int)>; - void MonitorPaths(const std::vector<std::string>& paths, Callback const& cb); - void StopMonitoring(); - - std::vector<std::string> WatchedFiles() const; - std::vector<std::string> WatchedDirectories() const; - -private: - std::unique_ptr<cmRootWatcher> Root; -}; diff --git a/Source/cmJsonObjectDictionary.h b/Source/cmJsonObjectDictionary.h deleted file mode 100644 index 8a2b529..0000000 --- a/Source/cmJsonObjectDictionary.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include <string> - -// Vocabulary: - -static const std::string kARTIFACTS_KEY = "artifacts"; -static const std::string kBUILD_DIRECTORY_KEY = "buildDirectory"; -static const std::string kCOMPILE_FLAGS_KEY = "compileFlags"; -static const std::string kCONFIGURATIONS_KEY = "configurations"; -static const std::string kDEFINES_KEY = "defines"; -static const std::string kFILE_GROUPS_KEY = "fileGroups"; -static const std::string kFRAMEWORK_PATH_KEY = "frameworkPath"; -static const std::string kFULL_NAME_KEY = "fullName"; -static const std::string kINCLUDE_PATH_KEY = "includePath"; -static const std::string kIS_CMAKE_KEY = "isCMake"; -static const std::string kIS_GENERATED_KEY = "isGenerated"; -static const std::string kIS_SYSTEM_KEY = "isSystem"; -static const std::string kIS_TEMPORARY_KEY = "isTemporary"; -static const std::string kKEY_KEY = "key"; -static const std::string kLANGUAGE_KEY = "language"; -static const std::string kLINKER_LANGUAGE_KEY = "linkerLanguage"; -static const std::string kLINK_FLAGS_KEY = "linkFlags"; -static const std::string kLINK_LANGUAGE_FLAGS_KEY = "linkLanguageFlags"; -static const std::string kLINK_LIBRARIES_KEY = "linkLibraries"; -static const std::string kLINK_PATH_KEY = "linkPath"; -static const std::string kNAME_KEY = "name"; -static const std::string kPATH_KEY = "path"; -static const std::string kPROJECTS_KEY = "projects"; -static const std::string kPROPERTIES_KEY = "properties"; -static const std::string kSOURCE_DIRECTORY_KEY = "sourceDirectory"; -static const std::string kSOURCES_KEY = "sources"; -static const std::string kSYSROOT_KEY = "sysroot"; -static const std::string kTARGETS_KEY = "targets"; -static const std::string kTYPE_KEY = "type"; -static const std::string kVALUE_KEY = "value"; -static const std::string kHAS_INSTALL_RULE = "hasInstallRule"; -static const std::string kINSTALL_PATHS = "installPaths"; -static const std::string kCTEST_NAME = "ctestName"; -static const std::string kCTEST_COMMAND = "ctestCommand"; -static const std::string kCTEST_INFO = "ctestInfo"; -static const std::string kMINIMUM_CMAKE_VERSION = "minimumCMakeVersion"; -static const std::string kIS_GENERATOR_PROVIDED_KEY = "isGeneratorProvided"; diff --git a/Source/cmJsonObjects.cxx b/Source/cmJsonObjects.cxx deleted file mode 100644 index 3a7ae0c..0000000 --- a/Source/cmJsonObjects.cxx +++ /dev/null @@ -1,692 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmJsonObjects.h" // IWYU pragma: keep - -#include <algorithm> -#include <cassert> -#include <cstddef> -#include <functional> -#include <limits> -#include <map> -#include <memory> -#include <set> -#include <string> -#include <unordered_map> -#include <utility> -#include <vector> - -#include <cmext/algorithm> - -#include "cmGeneratorExpression.h" -#include "cmGeneratorTarget.h" -#include "cmGlobalGenerator.h" -#include "cmInstallGenerator.h" -#include "cmInstallSubdirectoryGenerator.h" -#include "cmInstallTargetGenerator.h" -#include "cmJsonObjectDictionary.h" -#include "cmJsonObjects.h" -#include "cmLinkLineComputer.h" -#include "cmLocalGenerator.h" -#include "cmMakefile.h" -#include "cmProperty.h" -#include "cmPropertyMap.h" -#include "cmSourceFile.h" -#include "cmState.h" -#include "cmStateDirectory.h" -#include "cmStateSnapshot.h" -#include "cmStateTypes.h" -#include "cmStringAlgorithms.h" -#include "cmSystemTools.h" -#include "cmTarget.h" -#include "cmTest.h" -#include "cmake.h" - -namespace { - -std::vector<std::string> getConfigurations(const cmake* cm) -{ - std::vector<std::string> configurations; - const auto& makefiles = cm->GetGlobalGenerator()->GetMakefiles(); - if (makefiles.empty()) { - return configurations; - } - - return makefiles[0]->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); -} - -bool hasString(const Json::Value& v, const std::string& s) -{ - return !v.isNull() && - std::any_of(v.begin(), v.end(), - [s](const Json::Value& i) { return i.asString() == s; }); -} - -template <class T> -Json::Value fromStringList(const T& in) -{ - Json::Value result = Json::arrayValue; - for (std::string const& i : in) { - result.append(i); - } - return result; -} - -} // namespace - -void cmGetCMakeInputs(const cmGlobalGenerator* gg, - const std::string& sourceDir, - const std::string& buildDir, - std::vector<std::string>* internalFiles, - std::vector<std::string>* explicitFiles, - std::vector<std::string>* tmpFiles) -{ - const std::string cmakeRootDir = cmSystemTools::GetCMakeRoot() + '/'; - auto const& makefiles = gg->GetMakefiles(); - for (const auto& mf : makefiles) { - for (std::string const& lf : mf->GetListFiles()) { - - const std::string startOfFile = lf.substr(0, cmakeRootDir.size()); - const bool isInternal = (startOfFile == cmakeRootDir); - const bool isTemporary = - !isInternal && (cmHasPrefix(lf, buildDir + '/')); - - std::string toAdd = lf; - if (!sourceDir.empty()) { - const std::string& relative = - cmSystemTools::RelativePath(sourceDir, lf); - if (toAdd.size() > relative.size()) { - toAdd = relative; - } - } - - if (isInternal) { - if (internalFiles) { - internalFiles->push_back(std::move(toAdd)); - } - } else { - if (isTemporary) { - if (tmpFiles) { - tmpFiles->push_back(std::move(toAdd)); - } - } else { - if (explicitFiles) { - explicitFiles->push_back(std::move(toAdd)); - } - } - } - } - } -} - -Json::Value cmDumpCMakeInputs(const cmake* cm) -{ - const cmGlobalGenerator* gg = cm->GetGlobalGenerator(); - const std::string& buildDir = cm->GetHomeOutputDirectory(); - const std::string& sourceDir = cm->GetHomeDirectory(); - - std::vector<std::string> internalFiles; - std::vector<std::string> explicitFiles; - std::vector<std::string> tmpFiles; - cmGetCMakeInputs(gg, sourceDir, buildDir, &internalFiles, &explicitFiles, - &tmpFiles); - - Json::Value array = Json::arrayValue; - - Json::Value tmp = Json::objectValue; - tmp[kIS_CMAKE_KEY] = true; - tmp[kIS_TEMPORARY_KEY] = false; - tmp[kSOURCES_KEY] = fromStringList(internalFiles); - array.append(tmp); - - tmp = Json::objectValue; - tmp[kIS_CMAKE_KEY] = false; - tmp[kIS_TEMPORARY_KEY] = false; - tmp[kSOURCES_KEY] = fromStringList(explicitFiles); - array.append(tmp); - - tmp = Json::objectValue; - tmp[kIS_CMAKE_KEY] = false; - tmp[kIS_TEMPORARY_KEY] = true; - tmp[kSOURCES_KEY] = fromStringList(tmpFiles); - array.append(tmp); - - return array; -} - -class LanguageData -{ -public: - bool operator==(const LanguageData& other) const; - - void SetDefines(const std::set<std::string>& defines); - - bool IsGenerated = false; - std::string Language; - std::string Flags; - std::vector<std::string> Defines; - std::vector<std::pair<std::string, bool>> IncludePathList; -}; - -bool LanguageData::operator==(const LanguageData& other) const -{ - return Language == other.Language && Defines == other.Defines && - Flags == other.Flags && IncludePathList == other.IncludePathList && - IsGenerated == other.IsGenerated; -} - -void LanguageData::SetDefines(const std::set<std::string>& defines) -{ - std::vector<std::string> result; - result.reserve(defines.size()); - for (std::string const& i : defines) { - result.push_back(i); - } - std::sort(result.begin(), result.end()); - Defines = std::move(result); -} - -namespace std { - -template <> -struct hash<LanguageData> -{ - std::size_t operator()(const LanguageData& in) const - { - using std::hash; - size_t result = - hash<std::string>()(in.Language) ^ hash<std::string>()(in.Flags); - for (auto const& i : in.IncludePathList) { - result = result ^ - (hash<std::string>()(i.first) ^ - (i.second ? std::numeric_limits<size_t>::max() : 0)); - } - for (auto const& i : in.Defines) { - result = result ^ hash<std::string>()(i); - } - result = - result ^ (in.IsGenerated ? std::numeric_limits<size_t>::max() : 0); - return result; - } -}; - -} // namespace std - -static Json::Value DumpSourceFileGroup(const LanguageData& data, - const std::vector<std::string>& files, - const std::string& baseDir) -{ - Json::Value result = Json::objectValue; - - if (!data.Language.empty()) { - result[kLANGUAGE_KEY] = data.Language; - if (!data.Flags.empty()) { - result[kCOMPILE_FLAGS_KEY] = data.Flags; - } - if (!data.IncludePathList.empty()) { - Json::Value includes = Json::arrayValue; - for (auto const& i : data.IncludePathList) { - Json::Value tmp = Json::objectValue; - tmp[kPATH_KEY] = i.first; - if (i.second) { - tmp[kIS_SYSTEM_KEY] = i.second; - } - includes.append(tmp); - } - result[kINCLUDE_PATH_KEY] = includes; - } - if (!data.Defines.empty()) { - result[kDEFINES_KEY] = fromStringList(data.Defines); - } - } - - result[kIS_GENERATED_KEY] = data.IsGenerated; - - Json::Value sourcesValue = Json::arrayValue; - for (auto const& i : files) { - const std::string relPath = cmSystemTools::RelativePath(baseDir, i); - sourcesValue.append(relPath.size() < i.size() ? relPath : i); - } - - result[kSOURCES_KEY] = sourcesValue; - return result; -} - -static Json::Value DumpSourceFilesList( - cmGeneratorTarget* target, const std::string& config, - const std::map<std::string, LanguageData>& languageDataMap) -{ - // Collect sourcefile groups: - - std::vector<cmSourceFile*> files; - target->GetSourceFiles(files, config); - - std::unordered_map<LanguageData, std::vector<std::string>> fileGroups; - for (cmSourceFile* file : files) { - LanguageData fileData; - fileData.Language = file->GetOrDetermineLanguage(); - if (!fileData.Language.empty()) { - const LanguageData& ld = languageDataMap.at(fileData.Language); - cmLocalGenerator* lg = target->GetLocalGenerator(); - cmGeneratorExpressionInterpreter genexInterpreter(lg, config, target, - fileData.Language); - - std::string compileFlags = ld.Flags; - const std::string COMPILE_FLAGS("COMPILE_FLAGS"); - if (cmProp cflags = file->GetProperty(COMPILE_FLAGS)) { - lg->AppendFlags(compileFlags, - genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS)); - } - const std::string COMPILE_OPTIONS("COMPILE_OPTIONS"); - if (cmProp coptions = file->GetProperty(COMPILE_OPTIONS)) { - lg->AppendCompileOptions( - compileFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS)); - } - fileData.Flags = compileFlags; - - // Add include directories from source file properties. - std::vector<std::string> includes; - - const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); - if (cmProp cincludes = file->GetProperty(INCLUDE_DIRECTORIES)) { - const std::string& evaluatedIncludes = - genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES); - lg->AppendIncludeDirectories(includes, evaluatedIncludes, *file); - - for (const auto& include : includes) { - fileData.IncludePathList.emplace_back( - include, - target->IsSystemIncludeDirectory(include, config, - fileData.Language)); - } - } - - fileData.IncludePathList.insert(fileData.IncludePathList.end(), - ld.IncludePathList.begin(), - ld.IncludePathList.end()); - - const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); - std::set<std::string> defines; - if (cmProp defs = file->GetProperty(COMPILE_DEFINITIONS)) { - lg->AppendDefines( - defines, genexInterpreter.Evaluate(*defs, COMPILE_DEFINITIONS)); - } - - const std::string defPropName = - "COMPILE_DEFINITIONS_" + cmSystemTools::UpperCase(config); - if (cmProp config_defs = file->GetProperty(defPropName)) { - lg->AppendDefines( - defines, - genexInterpreter.Evaluate(*config_defs, COMPILE_DEFINITIONS)); - } - - defines.insert(ld.Defines.begin(), ld.Defines.end()); - - fileData.SetDefines(defines); - } - - fileData.IsGenerated = file->GetIsGenerated(); - std::vector<std::string>& groupFileList = fileGroups[fileData]; - groupFileList.push_back(file->ResolveFullPath()); - } - - const std::string& baseDir = target->Makefile->GetCurrentSourceDirectory(); - Json::Value result = Json::arrayValue; - for (auto const& it : fileGroups) { - Json::Value group = DumpSourceFileGroup(it.first, it.second, baseDir); - if (!group.isNull()) { - result.append(group); - } - } - - return result; -} - -static Json::Value DumpCTestInfo(cmLocalGenerator* lg, cmTest* testInfo, - const std::string& config) -{ - Json::Value result = Json::objectValue; - result[kCTEST_NAME] = testInfo->GetName(); - - // Concat command entries together. After the first should be the arguments - // for the command - std::string command; - for (auto const& cmd : testInfo->GetCommand()) { - command.append(cmd); - command.append(" "); - } - - // Remove any config specific variables from the output. - result[kCTEST_COMMAND] = - cmGeneratorExpression::Evaluate(command, lg, config); - - // Build up the list of properties that may have been specified - Json::Value properties = Json::arrayValue; - for (auto& prop : testInfo->GetProperties().GetList()) { - Json::Value entry = Json::objectValue; - entry[kKEY_KEY] = prop.first; - - // Remove config variables from the value too. - entry[kVALUE_KEY] = - cmGeneratorExpression::Evaluate(prop.second, lg, config); - properties.append(entry); - } - result[kPROPERTIES_KEY] = properties; - - return result; -} - -static void DumpMakefileTests(cmLocalGenerator* lg, const std::string& config, - Json::Value* result) -{ - auto mf = lg->GetMakefile(); - std::vector<cmTest*> tests; - mf->GetTests(config, tests); - for (auto test : tests) { - Json::Value tmp = DumpCTestInfo(lg, test, config); - if (!tmp.isNull()) { - result->append(tmp); - } - } -} - -static Json::Value DumpCTestProjectList(const cmake* cm, - std::string const& config) -{ - Json::Value result = Json::arrayValue; - - auto globalGen = cm->GetGlobalGenerator(); - - for (const auto& projectIt : globalGen->GetProjectMap()) { - Json::Value pObj = Json::objectValue; - pObj[kNAME_KEY] = projectIt.first; - - Json::Value tests = Json::arrayValue; - - // Gather tests for every generator - for (const auto& lg : projectIt.second) { - // Make sure they're generated. - lg->GenerateTestFiles(); - DumpMakefileTests(lg, config, &tests); - } - - pObj[kCTEST_INFO] = tests; - - result.append(pObj); - } - - return result; -} - -static Json::Value DumpCTestConfiguration(const cmake* cm, - const std::string& config) -{ - Json::Value result = Json::objectValue; - result[kNAME_KEY] = config; - - result[kPROJECTS_KEY] = DumpCTestProjectList(cm, config); - - return result; -} - -static Json::Value DumpCTestConfigurationsList(const cmake* cm) -{ - Json::Value result = Json::arrayValue; - - for (const std::string& c : getConfigurations(cm)) { - result.append(DumpCTestConfiguration(cm, c)); - } - - return result; -} - -Json::Value cmDumpCTestInfo(const cmake* cm) -{ - Json::Value result = Json::objectValue; - result[kCONFIGURATIONS_KEY] = DumpCTestConfigurationsList(cm); - return result; -} - -static Json::Value DumpTarget(cmGeneratorTarget* target, - const std::string& config) -{ - cmLocalGenerator* lg = target->GetLocalGenerator(); - - const cmStateEnums::TargetType type = target->GetType(); - const std::string typeName = cmState::GetTargetTypeName(type); - - Json::Value ttl = Json::arrayValue; - ttl.append("EXECUTABLE"); - ttl.append("STATIC_LIBRARY"); - ttl.append("SHARED_LIBRARY"); - ttl.append("MODULE_LIBRARY"); - ttl.append("OBJECT_LIBRARY"); - ttl.append("UTILITY"); - ttl.append("INTERFACE_LIBRARY"); - - if (!hasString(ttl, typeName) || target->IsImported()) { - return Json::Value(); - } - - Json::Value result = Json::objectValue; - result[kNAME_KEY] = target->GetName(); - result[kIS_GENERATOR_PROVIDED_KEY] = - target->Target->GetIsGeneratorProvided(); - result[kTYPE_KEY] = typeName; - result[kSOURCE_DIRECTORY_KEY] = lg->GetCurrentSourceDirectory(); - result[kBUILD_DIRECTORY_KEY] = lg->GetCurrentBinaryDirectory(); - - if (type == cmStateEnums::INTERFACE_LIBRARY) { - return result; - } - - result[kFULL_NAME_KEY] = target->GetFullName(config); - - if (target->Target->GetHaveInstallRule()) { - result[kHAS_INSTALL_RULE] = true; - - Json::Value installPaths = Json::arrayValue; - for (const auto& installGenerator : - target->Makefile->GetInstallGenerators()) { - auto installTargetGenerator = - dynamic_cast<cmInstallTargetGenerator*>(installGenerator.get()); - if (installTargetGenerator != nullptr && - installTargetGenerator->GetTarget()->Target == target->Target) { - auto dest = installTargetGenerator->GetDestination(config); - - std::string installPath; - if (!dest.empty() && cmSystemTools::FileIsFullPath(dest)) { - installPath = dest; - } else { - installPath = cmStrCat( - target->Makefile->GetSafeDefinition("CMAKE_INSTALL_PREFIX"), '/', - dest); - } - - installPaths.append(installPath); - } - } - - result[kINSTALL_PATHS] = installPaths; - } - - if (target->HaveWellDefinedOutputFiles()) { - Json::Value artifacts = Json::arrayValue; - artifacts.append( - target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact)); - if (target->HasImportLibrary(config)) { - artifacts.append( - target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact)); - } - if (target->IsDLLPlatform()) { - const cmGeneratorTarget::OutputInfo* output = - target->GetOutputInfo(config); - if (output && !output->PdbDir.empty()) { - artifacts.append(output->PdbDir + '/' + target->GetPDBName(config)); - } - } - result[kARTIFACTS_KEY] = artifacts; - - result[kLINKER_LANGUAGE_KEY] = target->GetLinkerLanguage(config); - - std::string linkLibs; - std::string linkFlags; - std::string linkLanguageFlags; - std::string frameworkPath; - std::string linkPath; - cmLinkLineComputer linkLineComputer(lg, - lg->GetStateSnapshot().GetDirectory()); - lg->GetTargetFlags(&linkLineComputer, config, linkLibs, linkLanguageFlags, - linkFlags, frameworkPath, linkPath, target); - - linkLibs = cmTrimWhitespace(linkLibs); - linkFlags = cmTrimWhitespace(linkFlags); - linkLanguageFlags = cmTrimWhitespace(linkLanguageFlags); - frameworkPath = cmTrimWhitespace(frameworkPath); - linkPath = cmTrimWhitespace(linkPath); - - if (!cmTrimWhitespace(linkLibs).empty()) { - result[kLINK_LIBRARIES_KEY] = linkLibs; - } - if (!cmTrimWhitespace(linkFlags).empty()) { - result[kLINK_FLAGS_KEY] = linkFlags; - } - if (!cmTrimWhitespace(linkLanguageFlags).empty()) { - result[kLINK_LANGUAGE_FLAGS_KEY] = linkLanguageFlags; - } - if (!frameworkPath.empty()) { - result[kFRAMEWORK_PATH_KEY] = frameworkPath; - } - if (!linkPath.empty()) { - result[kLINK_PATH_KEY] = linkPath; - } - const std::string sysroot = - lg->GetMakefile()->GetSafeDefinition("CMAKE_SYSROOT"); - if (!sysroot.empty()) { - result[kSYSROOT_KEY] = sysroot; - } - } - - std::set<std::string> languages; - target->GetLanguages(languages, config); - std::map<std::string, LanguageData> languageDataMap; - - for (std::string const& lang : languages) { - LanguageData& ld = languageDataMap[lang]; - ld.Language = lang; - lg->GetTargetCompileFlags(target, config, lang, ld.Flags); - std::set<std::string> defines; - lg->GetTargetDefines(target, config, lang, defines); - ld.SetDefines(defines); - std::vector<std::string> includePathList; - lg->GetIncludeDirectories(includePathList, target, lang, config); - for (std::string const& i : includePathList) { - ld.IncludePathList.emplace_back( - i, target->IsSystemIncludeDirectory(i, config, lang)); - } - } - - Json::Value sourceGroupsValue = - DumpSourceFilesList(target, config, languageDataMap); - if (!sourceGroupsValue.empty()) { - result[kFILE_GROUPS_KEY] = sourceGroupsValue; - } - - return result; -} - -static Json::Value DumpTargetsList( - const std::vector<cmLocalGenerator*>& generators, const std::string& config) -{ - Json::Value result = Json::arrayValue; - - std::vector<cmGeneratorTarget*> targetList; - for (auto const& lgIt : generators) { - cm::append(targetList, lgIt->GetGeneratorTargets()); - } - std::sort(targetList.begin(), targetList.end()); - - for (cmGeneratorTarget* target : targetList) { - Json::Value tmp = DumpTarget(target, config); - if (!tmp.isNull()) { - result.append(tmp); - } - } - - return result; -} - -static Json::Value DumpProjectList(const cmake* cm, std::string const& config) -{ - Json::Value result = Json::arrayValue; - - auto globalGen = cm->GetGlobalGenerator(); - - for (auto const& projectIt : globalGen->GetProjectMap()) { - Json::Value pObj = Json::objectValue; - pObj[kNAME_KEY] = projectIt.first; - - // All Projects must have at least one local generator - assert(!projectIt.second.empty()); - const cmLocalGenerator* lg = projectIt.second.at(0); - - // Project structure information: - const cmMakefile* mf = lg->GetMakefile(); - auto minVersion = mf->GetSafeDefinition("CMAKE_MINIMUM_REQUIRED_VERSION"); - pObj[kMINIMUM_CMAKE_VERSION] = minVersion; - pObj[kSOURCE_DIRECTORY_KEY] = mf->GetCurrentSourceDirectory(); - pObj[kBUILD_DIRECTORY_KEY] = mf->GetCurrentBinaryDirectory(); - pObj[kTARGETS_KEY] = DumpTargetsList(projectIt.second, config); - - // For a project-level install rule it might be defined in any of its - // associated generators. - bool hasInstallRule = false; - for (const auto generator : projectIt.second) { - for (const auto& installGen : - generator->GetMakefile()->GetInstallGenerators()) { - if (!dynamic_cast<cmInstallSubdirectoryGenerator*>(installGen.get())) { - hasInstallRule = true; - break; - } - } - - if (hasInstallRule) { - break; - } - } - - pObj[kHAS_INSTALL_RULE] = hasInstallRule; - - result.append(pObj); - } - - return result; -} - -static Json::Value DumpConfiguration(const cmake* cm, - const std::string& config) -{ - Json::Value result = Json::objectValue; - result[kNAME_KEY] = config; - - result[kPROJECTS_KEY] = DumpProjectList(cm, config); - - return result; -} - -static Json::Value DumpConfigurationsList(const cmake* cm) -{ - Json::Value result = Json::arrayValue; - - for (std::string const& c : getConfigurations(cm)) { - result.append(DumpConfiguration(cm, c)); - } - - return result; -} - -Json::Value cmDumpCodeModel(const cmake* cm) -{ - Json::Value result = Json::objectValue; - result[kCONFIGURATIONS_KEY] = DumpConfigurationsList(cm); - return result; -} diff --git a/Source/cmJsonObjects.h b/Source/cmJsonObjects.h deleted file mode 100644 index 80a4834..0000000 --- a/Source/cmJsonObjects.h +++ /dev/null @@ -1,24 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <string> -#include <vector> - -#include <cm3p/json/value.h> - -class cmake; -class cmGlobalGenerator; - -extern void cmGetCMakeInputs(const cmGlobalGenerator* gg, - const std::string& sourceDir, - const std::string& buildDir, - std::vector<std::string>* internalFiles, - std::vector<std::string>* explicitFiles, - std::vector<std::string>* tmpFiles); - -extern Json::Value cmDumpCodeModel(const cmake* cm); -extern Json::Value cmDumpCTestInfo(const cmake* cm); -extern Json::Value cmDumpCMakeInputs(const cmake* cm); diff --git a/Source/cmPipeConnection.cxx b/Source/cmPipeConnection.cxx deleted file mode 100644 index 1eede13..0000000 --- a/Source/cmPipeConnection.cxx +++ /dev/null @@ -1,71 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmPipeConnection.h" - -#include <utility> - -#include "cmServer.h" - -cmPipeConnection::cmPipeConnection(std::string name, - cmConnectionBufferStrategy* bufferStrategy) - : cmEventBasedConnection(bufferStrategy) - , PipeName(std::move(name)) -{ -} - -void cmPipeConnection::Connect(uv_stream_t* server) -{ - if (this->WriteStream.get()) { - // Accept and close all pipes but the first: - cm::uv_pipe_ptr rejectPipe; - - rejectPipe.init(*this->Server->GetLoop(), 0); - uv_accept(server, rejectPipe); - - return; - } - - cm::uv_pipe_ptr ClientPipe; - ClientPipe.init(*this->Server->GetLoop(), 0, - static_cast<cmEventBasedConnection*>(this)); - - if (uv_accept(server, ClientPipe) != 0) { - return; - } - - uv_read_start(ClientPipe, on_alloc_buffer, on_read); - WriteStream = std::move(ClientPipe); - Server->OnConnected(this); -} - -bool cmPipeConnection::OnServeStart(std::string* errorMessage) -{ - this->ServerPipe.init(*this->Server->GetLoop(), 0, - static_cast<cmEventBasedConnection*>(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; - } - - if ((r = uv_listen(this->ServerPipe, 1, on_new_connection)) != 0) { - *errorMessage = std::string("Internal Error listening on ") + - this->PipeName + ": " + uv_err_name(r); - return false; - } - - return cmConnection::OnServeStart(errorMessage); -} - -bool cmPipeConnection::OnConnectionShuttingDown() -{ - if (this->WriteStream.get()) { - this->WriteStream->data = nullptr; - } - - this->ServerPipe.reset(); - - return cmEventBasedConnection::OnConnectionShuttingDown(); -} diff --git a/Source/cmPipeConnection.h b/Source/cmPipeConnection.h deleted file mode 100644 index 1215716..0000000 --- a/Source/cmPipeConnection.h +++ /dev/null @@ -1,29 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <string> - -#include <cm3p/uv.h> - -#include "cmConnection.h" -#include "cmUVHandlePtr.h" - -class cmPipeConnection : public cmEventBasedConnection -{ -public: - cmPipeConnection(std::string name, - cmConnectionBufferStrategy* bufferStrategy = nullptr); - - bool OnServeStart(std::string* pString) override; - - bool OnConnectionShuttingDown() override; - - void Connect(uv_stream_t* server) override; - -private: - const std::string PipeName; - cm::uv_pipe_ptr ServerPipe; -}; diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx deleted file mode 100644 index 7f97406..0000000 --- a/Source/cmServer.cxx +++ /dev/null @@ -1,570 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmServer.h" - -#include <algorithm> -#include <cassert> -#include <csignal> -#include <cstdint> -#include <iostream> -#include <mutex> -#include <utility> - -#include <cm/memory> -#include <cm/shared_mutex> - -#include <cm3p/json/reader.h> -#include <cm3p/json/writer.h> - -#include "cmsys/FStream.hxx" - -#include "cmConnection.h" -#include "cmFileMonitor.h" -#include "cmJsonObjectDictionary.h" -#include "cmServerDictionary.h" -#include "cmServerProtocol.h" -#include "cmSystemTools.h" -#include "cmake.h" - -void on_signal(uv_signal_t* signal, int signum) -{ - auto conn = static_cast<cmServerBase*>(signal->data); - conn->OnSignal(signum); -} - -static void on_walk_to_shutdown(uv_handle_t* handle, void* arg) -{ - (void)arg; - assert(uv_is_closing(handle)); - if (!uv_is_closing(handle)) { - uv_close(handle, &cmEventBasedConnection::on_close); - } -} - -class cmServer::DebugInfo -{ -public: - DebugInfo() - : StartTime(uv_hrtime()) - { - } - - bool PrintStatistics = false; - - std::string OutputFile; - uint64_t StartTime; -}; - -cmServer::cmServer(cmConnection* conn, bool supportExperimental) - : cmServerBase(conn) - , SupportExperimental(supportExperimental) -{ - // Register supported protocols: - this->RegisterProtocol(cm::make_unique<cmServerProtocol1>()); -} - -cmServer::~cmServer() -{ - Close(); -} - -void cmServer::ProcessRequest(cmConnection* connection, - const std::string& input) -{ - Json::Reader reader; - Json::Value value; - if (!reader.parse(input, value)) { - this->WriteParseError(connection, "Failed to parse JSON input."); - return; - } - - std::unique_ptr<DebugInfo> debug; - Json::Value debugValue = value["debug"]; - if (!debugValue.isNull()) { - debug = cm::make_unique<DebugInfo>(); - debug->OutputFile = debugValue["dumpToFile"].asString(); - debug->PrintStatistics = debugValue["showStats"].asBool(); - } - - const cmServerRequest request(this, connection, value[kTYPE_KEY].asString(), - value[kCOOKIE_KEY].asString(), value); - - if (request.Type.empty()) { - cmServerResponse response(request); - response.SetError("No type given in request."); - this->WriteResponse(connection, response, nullptr); - return; - } - - cmSystemTools::SetMessageCallback( - [&request](const std::string& msg, const char* title) { - reportMessage(msg, title, request); - }); - - if (this->Protocol) { - this->Protocol->CMakeInstance()->SetProgressCallback( - [&request](const std::string& msg, float prog) { - reportProgress(msg, prog, request); - }); - this->WriteResponse(connection, this->Protocol->Process(request), - debug.get()); - } else { - this->WriteResponse(connection, this->SetProtocolVersion(request), - debug.get()); - } -} - -void cmServer::RegisterProtocol(std::unique_ptr<cmServerProtocol> protocol) -{ - if (protocol->IsExperimental() && !this->SupportExperimental) { - protocol.reset(); - return; - } - auto version = protocol->ProtocolVersion(); - assert(version.first >= 0); - assert(version.second >= 0); - auto it = std::find_if( - this->SupportedProtocols.begin(), this->SupportedProtocols.end(), - [version](const std::unique_ptr<cmServerProtocol>& p) { - return p->ProtocolVersion() == version; - }); - if (it == this->SupportedProtocols.end()) { - this->SupportedProtocols.push_back(std::move(protocol)); - } -} - -void cmServer::PrintHello(cmConnection* connection) const -{ - Json::Value hello = Json::objectValue; - hello[kTYPE_KEY] = "hello"; - - Json::Value& protocolVersions = hello[kSUPPORTED_PROTOCOL_VERSIONS] = - Json::arrayValue; - - for (auto const& proto : this->SupportedProtocols) { - auto version = proto->ProtocolVersion(); - Json::Value tmp = Json::objectValue; - tmp[kMAJOR_KEY] = version.first; - tmp[kMINOR_KEY] = version.second; - if (proto->IsExperimental()) { - tmp[kIS_EXPERIMENTAL_KEY] = true; - } - protocolVersions.append(tmp); - } - - this->WriteJsonObject(connection, hello, nullptr); -} - -void cmServer::reportProgress(const std::string& msg, float progress, - const cmServerRequest& request) -{ - if (progress < 0.0f || progress > 1.0f) { - request.ReportMessage(msg, ""); - } else { - request.ReportProgress(0, static_cast<int>(progress * 1000), 1000, msg); - } -} - -void cmServer::reportMessage(const std::string& msg, const char* title, - const cmServerRequest& request) -{ - std::string titleString; - if (title) { - titleString = title; - } - request.ReportMessage(msg, titleString); -} - -cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request) -{ - if (request.Type != kHANDSHAKE_TYPE) { - return request.ReportError("Waiting for type \"" + kHANDSHAKE_TYPE + - "\"."); - } - - Json::Value requestedProtocolVersion = request.Data[kPROTOCOL_VERSION_KEY]; - if (requestedProtocolVersion.isNull()) { - return request.ReportError("\"" + kPROTOCOL_VERSION_KEY + - "\" is required for \"" + kHANDSHAKE_TYPE + - "\"."); - } - - if (!requestedProtocolVersion.isObject()) { - return request.ReportError("\"" + kPROTOCOL_VERSION_KEY + - "\" must be a JSON object."); - } - - Json::Value majorValue = requestedProtocolVersion[kMAJOR_KEY]; - if (!majorValue.isInt()) { - return request.ReportError("\"" + kMAJOR_KEY + - "\" must be set and an integer."); - } - - Json::Value minorValue = requestedProtocolVersion[kMINOR_KEY]; - if (!minorValue.isNull() && !minorValue.isInt()) { - return request.ReportError("\"" + kMINOR_KEY + - "\" 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("\"" + kMAJOR_KEY + "\" must be >= 0."); - } - if (!minorValue.isNull() && minor < 0) { - return request.ReportError("\"" + kMINOR_KEY + - "\" must be >= 0 when set."); - } - - this->Protocol = - cmServer::FindMatchingProtocol(this->SupportedProtocols, major, minor); - if (!this->Protocol) { - return request.ReportError("Protocol version not supported."); - } - - std::string errorMessage; - if (!this->Protocol->Activate(this, request, &errorMessage)) { - this->Protocol = nullptr; - return request.ReportError("Failed to activate protocol version: " + - errorMessage); - } - return request.Reply(Json::objectValue); -} - -bool cmServer::Serve(std::string* errorMessage) -{ - if (this->SupportedProtocols.empty()) { - *errorMessage = - "No protocol versions defined. Maybe you need --experimental?"; - return false; - } - assert(!this->Protocol); - - return cmServerBase::Serve(errorMessage); -} - -cmFileMonitor* cmServer::FileMonitor() const -{ - return fileMonitor.get(); -} - -void cmServer::WriteJsonObject(const Json::Value& jsonValue, - const DebugInfo* debug) const -{ - cm::shared_lock<cm::shared_mutex> lock(ConnectionsMutex); - for (auto& connection : this->Connections) { - WriteJsonObject(connection.get(), jsonValue, debug); - } -} - -void cmServer::WriteJsonObject(cmConnection* connection, - const Json::Value& jsonValue, - const DebugInfo* debug) const -{ - Json::FastWriter writer; - - 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()) { - cmsys::ofstream myfile(debug->OutputFile.c_str()); - myfile << result; - } - } - - connection->WriteData(result); -} - -cmServerProtocol* cmServer::FindMatchingProtocol( - const std::vector<std::unique_ptr<cmServerProtocol>>& protocols, int major, - int minor) -{ - cmServerProtocol* bestMatch = nullptr; - for (const auto& protocol : protocols) { - auto version = protocol->ProtocolVersion(); - if (major != version.first) { - continue; - } - if (minor == version.second) { - return protocol.get(); - } - if (!bestMatch || bestMatch->ProtocolVersion().second < version.second) { - bestMatch = protocol.get(); - } - } - 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] = kPROGRESS_TYPE; - obj[kREPLY_TO_KEY] = request.Type; - obj[kCOOKIE_KEY] = request.Cookie; - obj[kPROGRESS_MESSAGE_KEY] = message; - obj[kPROGRESS_MINIMUM_KEY] = min; - obj[kPROGRESS_MAXIMUM_KEY] = max; - obj[kPROGRESS_CURRENT_KEY] = current; - - this->WriteJsonObject(request.Connection, 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[kMESSAGE_KEY] = message; - if (!title.empty()) { - obj[kTITLE_KEY] = title; - } - - WriteJsonObject(request.Connection, obj, nullptr); -} - -void cmServer::WriteParseError(cmConnection* connection, - const std::string& message) const -{ - Json::Value obj = Json::objectValue; - obj[kTYPE_KEY] = kERROR_TYPE; - obj[kERROR_MESSAGE_KEY] = message; - obj[kREPLY_TO_KEY] = ""; - obj[kCOOKIE_KEY] = ""; - - this->WriteJsonObject(connection, obj, nullptr); -} - -void cmServer::WriteSignal(const std::string& name, - const Json::Value& data) const -{ - assert(data.isObject()); - Json::Value obj = data; - obj[kTYPE_KEY] = kSIGNAL_TYPE; - obj[kREPLY_TO_KEY] = ""; - obj[kCOOKIE_KEY] = ""; - obj[kNAME_KEY] = name; - - WriteJsonObject(obj, nullptr); -} - -void cmServer::WriteResponse(cmConnection* connection, - 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() ? kERROR_TYPE : kREPLY_TYPE; - obj[kREPLY_TO_KEY] = response.Type; - if (response.IsError()) { - obj[kERROR_MESSAGE_KEY] = response.ErrorMessage(); - } - - this->WriteJsonObject(connection, obj, debug); -} - -void cmServer::OnConnected(cmConnection* connection) -{ - PrintHello(connection); -} - -void cmServer::OnServeStart() -{ - cmServerBase::OnServeStart(); - fileMonitor = std::make_shared<cmFileMonitor>(GetLoop()); -} - -void cmServer::StartShutDown() -{ - if (fileMonitor) { - fileMonitor->StopMonitoring(); - fileMonitor.reset(); - } - cmServerBase::StartShutDown(); -} - -static void __start_thread(void* arg) -{ - auto server = static_cast<cmServerBase*>(arg); - std::string error; - bool success = server->Serve(&error); - if (!success || !error.empty()) { - std::cerr << "Error during serve: " << error << std::endl; - } -} - -bool cmServerBase::StartServeThread() -{ - ServeThreadRunning = true; - uv_thread_create(&ServeThread, __start_thread, this); - return true; -} - -static void __shutdownThread(uv_async_t* arg) -{ - auto server = static_cast<cmServerBase*>(arg->data); - server->StartShutDown(); -} - -bool cmServerBase::Serve(std::string* errorMessage) -{ -#ifndef NDEBUG - uv_thread_t blank_thread_t = {}; - assert(uv_thread_equal(&blank_thread_t, &ServeThreadId)); - ServeThreadId = uv_thread_self(); -#endif - - errorMessage->clear(); - - ShutdownSignal.init(Loop, __shutdownThread, this); - - SIGINTHandler.init(Loop, this); - SIGHUPHandler.init(Loop, this); - - SIGINTHandler.start(&on_signal, SIGINT); - SIGHUPHandler.start(&on_signal, SIGHUP); - - OnServeStart(); - - { - cm::shared_lock<cm::shared_mutex> lock(ConnectionsMutex); - for (auto& connection : Connections) { - if (!connection->OnServeStart(errorMessage)) { - return false; - } - } - } - - if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) { - // It is important we don't ever let the event loop exit with open handles - // at best this is a memory leak, but it can also introduce race conditions - // which can hang the program. - assert(false && "Event loop stopped in unclean state."); - - *errorMessage = "Internal Error: Event loop stopped in unclean state."; - return false; - } - - return true; -} - -void cmServerBase::OnConnected(cmConnection*) -{ -} - -void cmServerBase::OnServeStart() -{ -} - -void cmServerBase::StartShutDown() -{ - ShutdownSignal.reset(); - SIGINTHandler.reset(); - SIGHUPHandler.reset(); - - { - std::unique_lock<cm::shared_mutex> lock(ConnectionsMutex); - for (auto& connection : Connections) { - connection->OnConnectionShuttingDown(); - } - Connections.clear(); - } - - uv_walk(&Loop, on_walk_to_shutdown, nullptr); -} - -bool cmServerBase::OnSignal(int signum) -{ - (void)signum; - StartShutDown(); - return true; -} - -cmServerBase::cmServerBase(cmConnection* connection) -{ - auto err = uv_loop_init(&Loop); - (void)err; - Loop.data = this; - assert(err == 0); - - AddNewConnection(connection); -} - -void cmServerBase::Close() -{ - if (Loop.data) { - if (ServeThreadRunning) { - this->ShutdownSignal.send(); - uv_thread_join(&ServeThread); - } - - uv_loop_close(&Loop); - Loop.data = nullptr; - } -} -cmServerBase::~cmServerBase() -{ - Close(); -} - -void cmServerBase::AddNewConnection(cmConnection* ownedConnection) -{ - { - std::unique_lock<cm::shared_mutex> lock(ConnectionsMutex); - Connections.emplace_back(ownedConnection); - } - ownedConnection->SetServer(this); -} - -uv_loop_t* cmServerBase::GetLoop() -{ - return &Loop; -} - -void cmServerBase::OnDisconnect(cmConnection* pConnection) -{ - auto pred = [pConnection](const std::unique_ptr<cmConnection>& m) { - return m.get() == pConnection; - }; - { - std::unique_lock<cm::shared_mutex> lock(ConnectionsMutex); - Connections.erase( - std::remove_if(Connections.begin(), Connections.end(), pred), - Connections.end()); - } - - if (Connections.empty()) { - this->ShutdownSignal.send(); - } -} diff --git a/Source/cmServer.h b/Source/cmServer.h deleted file mode 100644 index 9543329..0000000 --- a/Source/cmServer.h +++ /dev/null @@ -1,162 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <memory> -#include <string> -#include <vector> - -#include <cm/shared_mutex> - -#include <cm3p/json/value.h> -#include <cm3p/uv.h> - -#include "cmUVHandlePtr.h" - -class cmConnection; -class cmFileMonitor; -class cmServerProtocol; -class cmServerRequest; -class cmServerResponse; - -/*** - * This essentially hold and manages a libuv event queue and responds to - * messages - * on any of its connections. - */ -class cmServerBase -{ -public: - cmServerBase(cmConnection* connection); - virtual ~cmServerBase(); - - virtual void AddNewConnection(cmConnection* ownedConnection); - - /*** - * The main override responsible for tailoring behavior towards - * whatever the given server is supposed to do - * - * This should almost always be called by the given connections - * directly. - * - * @param connection The connection the request was received on - * @param request The actual request - */ - virtual void ProcessRequest(cmConnection* connection, - const std::string& request) = 0; - virtual void OnConnected(cmConnection* connection); - - /*** - * Start a dedicated thread. If this is used to start the server, it will - * join on the - * servers dtor. - */ - virtual bool StartServeThread(); - virtual bool Serve(std::string* errorMessage); - - virtual void OnServeStart(); - virtual void StartShutDown(); - - virtual bool OnSignal(int signum); - uv_loop_t* GetLoop(); - void Close(); - void OnDisconnect(cmConnection* pConnection); - -protected: - mutable cm::shared_mutex ConnectionsMutex; - std::vector<std::unique_ptr<cmConnection>> Connections; - - bool ServeThreadRunning = false; - uv_thread_t ServeThread; - cm::uv_async_ptr ShutdownSignal; -#ifndef NDEBUG -public: - // When the server starts it will mark down it's current thread ID, - // which is useful in other contexts to just assert that operations - // are performed on that same thread. - uv_thread_t ServeThreadId = {}; - -protected: -#endif - - uv_loop_t Loop; - - cm::uv_signal_ptr SIGINTHandler; - cm::uv_signal_ptr SIGHUPHandler; -}; - -class cmServer : public cmServerBase -{ -public: - class DebugInfo; - - cmServer(cmConnection* conn, bool supportExperimental); - ~cmServer() override; - - cmServer(cmServer const&) = delete; - cmServer& operator=(cmServer const&) = delete; - - bool Serve(std::string* errorMessage) override; - - cmFileMonitor* FileMonitor() const; - -private: - void RegisterProtocol(std::unique_ptr<cmServerProtocol> protocol); - - // Callbacks from cmServerConnection: - - void ProcessRequest(cmConnection* connection, - const std::string& request) override; - std::shared_ptr<cmFileMonitor> fileMonitor; - -public: - void OnServeStart() override; - - void StartShutDown() override; - -public: - void OnConnected(cmConnection* connection) override; - -private: - static void reportProgress(const std::string& msg, float progress, - const cmServerRequest& request); - static void reportMessage(const std::string& msg, const char* title, - const cmServerRequest& request); - - // Handle requests: - cmServerResponse SetProtocolVersion(const cmServerRequest& request); - - void PrintHello(cmConnection* connection) const; - - // 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(cmConnection* connection, - const cmServerResponse& response, - const DebugInfo* debug) const; - void WriteParseError(cmConnection* connection, - const std::string& message) const; - void WriteSignal(const std::string& name, const Json::Value& obj) const; - - void WriteJsonObject(Json::Value const& jsonValue, - const DebugInfo* debug) const; - - void WriteJsonObject(cmConnection* connection, Json::Value const& jsonValue, - const DebugInfo* debug) const; - - static cmServerProtocol* FindMatchingProtocol( - const std::vector<std::unique_ptr<cmServerProtocol>>& protocols, int major, - int minor); - - const bool SupportExperimental; - - cmServerProtocol* Protocol = nullptr; - std::vector<std::unique_ptr<cmServerProtocol>> SupportedProtocols; - - friend class cmServerProtocol; - friend class cmServerRequest; -}; diff --git a/Source/cmServerConnection.cxx b/Source/cmServerConnection.cxx deleted file mode 100644 index b4f41a0..0000000 --- a/Source/cmServerConnection.cxx +++ /dev/null @@ -1,165 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmConfigure.h" - -#include "cmServerConnection.h" - -#include <cm3p/uv.h> - -#include "cmServer.h" -#include "cmServerDictionary.h" - -#ifdef _WIN32 -# include "io.h" -#else -# include <unistd.h> -#endif -#include <cassert> -#include <utility> - -cmStdIoConnection::cmStdIoConnection( - cmConnectionBufferStrategy* bufferStrategy) - : cmEventBasedConnection(bufferStrategy) -{ -} - -cm::uv_stream_ptr cmStdIoConnection::SetupStream(int file_id) -{ - switch (uv_guess_handle(file_id)) { - case UV_TTY: { - cm::uv_tty_ptr tty; - tty.init(*this->Server->GetLoop(), file_id, file_id == 0, - static_cast<cmEventBasedConnection*>(this)); - uv_tty_set_mode(tty, UV_TTY_MODE_NORMAL); - return { std::move(tty) }; - } - case UV_FILE: - if (file_id == 0) { - return nullptr; - } - // Intentional fallthrough; stdin can _not_ be treated as a named - // pipe, however stdout can be. - CM_FALLTHROUGH; - case UV_NAMED_PIPE: { - cm::uv_pipe_ptr pipe; - pipe.init(*this->Server->GetLoop(), 0, - static_cast<cmEventBasedConnection*>(this)); - uv_pipe_open(pipe, file_id); - return { std::move(pipe) }; - } - default: - assert(false && "Unable to determine stream type"); - return nullptr; - } -} - -void cmStdIoConnection::SetServer(cmServerBase* s) -{ - cmConnection::SetServer(s); - if (!s) { - return; - } - - this->ReadStream = SetupStream(0); - this->WriteStream = SetupStream(1); -} - -void shutdown_connection(uv_prepare_t* prepare) -{ - cmStdIoConnection* connection = - static_cast<cmStdIoConnection*>(prepare->data); - - if (!uv_is_closing(reinterpret_cast<uv_handle_t*>(prepare))) { - uv_close(reinterpret_cast<uv_handle_t*>(prepare), - &cmEventBasedConnection::on_close_delete<uv_prepare_t>); - } - connection->OnDisconnect(0); -} - -bool cmStdIoConnection::OnServeStart(std::string* pString) -{ - Server->OnConnected(this); - if (this->ReadStream.get()) { - uv_read_start(this->ReadStream, on_alloc_buffer, on_read); - } else if (uv_guess_handle(0) == UV_FILE) { - char buffer[1024]; - while (auto len = read(0, buffer, sizeof(buffer))) { - ReadData(std::string(buffer, buffer + len)); - } - - // We can't start the disconnect from here, add a prepare hook to do that - // for us - auto prepare = new uv_prepare_t(); - prepare->data = this; - uv_prepare_init(Server->GetLoop(), prepare); - uv_prepare_start(prepare, shutdown_connection); - } - return cmConnection::OnServeStart(pString); -} - -bool cmStdIoConnection::OnConnectionShuttingDown() -{ - if (ReadStream.get()) { - uv_read_stop(ReadStream); - ReadStream->data = nullptr; - } - - this->ReadStream.reset(); - - cmEventBasedConnection::OnConnectionShuttingDown(); - - return true; -} - -cmServerPipeConnection::cmServerPipeConnection(const std::string& name) - : cmPipeConnection(name, new cmServerBufferStrategy) -{ -} - -cmServerStdIoConnection::cmServerStdIoConnection() - : cmStdIoConnection(new cmServerBufferStrategy) -{ -} - -cmConnectionBufferStrategy::~cmConnectionBufferStrategy() = default; - -void cmConnectionBufferStrategy::clear() -{ -} - -std::string cmServerBufferStrategy::BufferOutMessage( - const std::string& rawBuffer) const -{ - return std::string("\n") + kSTART_MAGIC + std::string("\n") + rawBuffer + - kEND_MAGIC + std::string("\n"); -} - -std::string cmServerBufferStrategy::BufferMessage(std::string& RawReadBuffer) -{ - for (;;) { - auto needle = RawReadBuffer.find('\n'); - - if (needle == std::string::npos) { - return ""; - } - std::string line = RawReadBuffer.substr(0, needle); - const auto ls = line.size(); - if (ls > 1 && line.at(ls - 1) == '\r') { - line.erase(ls - 1, 1); - } - RawReadBuffer.erase(RawReadBuffer.begin(), - RawReadBuffer.begin() + static_cast<long>(needle) + 1); - if (line == kSTART_MAGIC) { - RequestBuffer.clear(); - continue; - } - if (line == kEND_MAGIC) { - std::string rtn; - rtn.swap(this->RequestBuffer); - return rtn; - } - - this->RequestBuffer += line; - this->RequestBuffer += "\n"; - } -} diff --git a/Source/cmServerConnection.h b/Source/cmServerConnection.h deleted file mode 100644 index a70edb4..0000000 --- a/Source/cmServerConnection.h +++ /dev/null @@ -1,67 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <string> - -#include "cmConnection.h" -#include "cmPipeConnection.h" -#include "cmUVHandlePtr.h" - -class cmServerBase; - -/*** - * This connection buffer strategy accepts messages in the form of - * [== "CMake Server" ==[ -{ - ... some JSON message ... -} -]== "CMake Server" ==] - * and only passes on the core json; it discards the envelope. - */ -class cmServerBufferStrategy : public cmConnectionBufferStrategy -{ -public: - std::string BufferMessage(std::string& rawBuffer) override; - std::string BufferOutMessage(const std::string& rawBuffer) const override; - -private: - std::string RequestBuffer; -}; - -/*** - * Generic connection over std io interfaces -- tty - */ -class cmStdIoConnection : public cmEventBasedConnection -{ -public: - cmStdIoConnection(cmConnectionBufferStrategy* bufferStrategy); - - void SetServer(cmServerBase* s) override; - - bool OnConnectionShuttingDown() override; - - bool OnServeStart(std::string* pString) override; - -private: - cm::uv_stream_ptr SetupStream(int file_id); - cm::uv_stream_ptr ReadStream; -}; - -/*** - * These specific connections use the cmake server - * buffering strategy. - */ -class cmServerStdIoConnection : public cmStdIoConnection -{ -public: - cmServerStdIoConnection(); -}; - -class cmServerPipeConnection : public cmPipeConnection -{ -public: - cmServerPipeConnection(const std::string& name); -}; diff --git a/Source/cmServerDictionary.h b/Source/cmServerDictionary.h deleted file mode 100644 index 961e4b7..0000000 --- a/Source/cmServerDictionary.h +++ /dev/null @@ -1,67 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include <string> - -// Vocabulary: - -static const std::string kDIRTY_SIGNAL = "dirty"; -static const std::string kFILE_CHANGE_SIGNAL = "fileChange"; - -static const std::string kCACHE_TYPE = "cache"; -static const std::string kCMAKE_INPUTS_TYPE = "cmakeInputs"; -static const std::string kCODE_MODEL_TYPE = "codemodel"; -static const std::string kCOMPUTE_TYPE = "compute"; -static const std::string kCONFIGURE_TYPE = "configure"; -static const std::string kERROR_TYPE = "error"; -static const std::string kFILESYSTEM_WATCHERS_TYPE = "fileSystemWatchers"; -static const std::string kGLOBAL_SETTINGS_TYPE = "globalSettings"; -static const std::string kHANDSHAKE_TYPE = "handshake"; -static const std::string kMESSAGE_TYPE = "message"; -static const std::string kPROGRESS_TYPE = "progress"; -static const std::string kREPLY_TYPE = "reply"; -static const std::string kSET_GLOBAL_SETTINGS_TYPE = "setGlobalSettings"; -static const std::string kSIGNAL_TYPE = "signal"; -static const std::string kCTEST_INFO_TYPE = "ctestInfo"; - -static const std::string kBUILD_FILES_KEY = "buildFiles"; -static const std::string kCACHE_ARGUMENTS_KEY = "cacheArguments"; -static const std::string kCACHE_KEY = "cache"; -static const std::string kCAPABILITIES_KEY = "capabilities"; -static const std::string kCHECK_SYSTEM_VARS_KEY = "checkSystemVars"; -static const std::string kCMAKE_ROOT_DIRECTORY_KEY = "cmakeRootDirectory"; -static const std::string kCOOKIE_KEY = "cookie"; -static const std::string kDEBUG_OUTPUT_KEY = "debugOutput"; -static const std::string kERROR_MESSAGE_KEY = "errorMessage"; -static const std::string kEXTRA_GENERATOR_KEY = "extraGenerator"; -static const std::string kGENERATOR_KEY = "generator"; -static const std::string kIS_EXPERIMENTAL_KEY = "isExperimental"; -static const std::string kKEYS_KEY = "keys"; -static const std::string kMAJOR_KEY = "major"; -static const std::string kMESSAGE_KEY = "message"; -static const std::string kMINOR_KEY = "minor"; -static const std::string kPLATFORM_KEY = "platform"; -static const std::string kPROGRESS_CURRENT_KEY = "progressCurrent"; -static const std::string kPROGRESS_MAXIMUM_KEY = "progressMaximum"; -static const std::string kPROGRESS_MESSAGE_KEY = "progressMessage"; -static const std::string kPROGRESS_MINIMUM_KEY = "progressMinimum"; -static const std::string kPROTOCOL_VERSION_KEY = "protocolVersion"; -static const std::string kREPLY_TO_KEY = "inReplyTo"; -static const std::string kSUPPORTED_PROTOCOL_VERSIONS = - "supportedProtocolVersions"; -static const std::string kTITLE_KEY = "title"; -static const std::string kTOOLSET_KEY = "toolset"; -static const std::string kTRACE_EXPAND_KEY = "traceExpand"; -static const std::string kTRACE_KEY = "trace"; -static const std::string kWARN_UNINITIALIZED_KEY = "warnUninitialized"; -static const std::string kWARN_UNUSED_CLI_KEY = "warnUnusedCli"; -static const std::string kWARN_UNUSED_KEY = "warnUnused"; -static const std::string kWATCHED_DIRECTORIES_KEY = "watchedDirectories"; -static const std::string kWATCHED_FILES_KEY = "watchedFiles"; - -static const std::string kSTART_MAGIC = "[== \"CMake Server\" ==["; -static const std::string kEND_MAGIC = "]== \"CMake Server\" ==]"; - -static const std::string kRENAME_PROPERTY_VALUE = "rename"; -static const std::string kCHANGE_PROPERTY_VALUE = "change"; diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx deleted file mode 100644 index e586fd9..0000000 --- a/Source/cmServerProtocol.cxx +++ /dev/null @@ -1,760 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmServerProtocol.h" - -#include <algorithm> -#include <cassert> -#include <functional> -#include <string> -#include <utility> -#include <vector> - -#include <cm/memory> -#include <cmext/algorithm> - -#include <cm3p/uv.h> - -#include "cmExternalMakefileProjectGenerator.h" -#include "cmFileMonitor.h" -#include "cmGlobalGenerator.h" -#include "cmJsonObjectDictionary.h" -#include "cmJsonObjects.h" -#include "cmMessageType.h" -#include "cmProperty.h" -#include "cmServer.h" -#include "cmServerDictionary.h" -#include "cmState.h" -#include "cmSystemTools.h" -#include "cmake.h" - -// Get rid of some windows macros: -#undef max - -namespace { - -std::vector<std::string> toStringList(const Json::Value& in) -{ - std::vector<std::string> result; - for (auto const& it : in) { - result.push_back(it.asString()); - } - return result; -} - -} // namespace - -cmServerRequest::cmServerRequest(cmServer* server, cmConnection* connection, - std::string t, std::string c, Json::Value d) - : Type(std::move(t)) - , Cookie(std::move(c)) - , Data(std::move(d)) - , Connection(connection) - , 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); -} - -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); - 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; - } - return std::string(); -} - -Json::Value cmServerResponse::Data() const -{ - assert(this->m_Payload != PAYLOAD_UNKNOWN); - return this->m_Data; -} - -bool cmServerProtocol::Activate(cmServer* server, - const cmServerRequest& request, - std::string* errorMessage) -{ - assert(server); - this->m_Server = server; - this->m_CMakeInstance = - cm::make_unique<cmake>(cmake::RoleProject, cmState::Project); - this->m_WarnUnused = false; - const bool result = this->DoActivate(request, errorMessage); - if (!result) { - this->m_CMakeInstance = nullptr; - } - return result; -} - -cmFileMonitor* cmServerProtocol::FileMonitor() const -{ - return this->m_Server ? this->m_Server->FileMonitor() : nullptr; -} - -void cmServerProtocol::SendSignal(const std::string& name, - const Json::Value& data) const -{ - if (this->m_Server) { - this->m_Server->WriteSignal(name, data); - } -} - -cmake* cmServerProtocol::CMakeInstance() const -{ - return this->m_CMakeInstance.get(); -} - -bool cmServerProtocol::DoActivate(const cmServerRequest& /*request*/, - std::string* /*errorMessage*/) -{ - return true; -} - -std::pair<int, int> cmServerProtocol1::ProtocolVersion() const -{ - return { 1, 2 }; -} - -static void setErrorMessage(std::string* errorMessage, const std::string& text) -{ - if (errorMessage) { - *errorMessage = text; - } -} - -static bool getOrTestHomeDirectory(cmState* state, std::string& value, - std::string* errorMessage) -{ - const std::string cachedValue = - *state->GetCacheEntryValue("CMAKE_HOME_DIRECTORY"); - if (value.empty()) { - value = cachedValue; - return true; - } - const std::string suffix = "/CMakeLists.txt"; - const std::string cachedValueCML = cachedValue + suffix; - const std::string valueCML = value + suffix; - if (!cmSystemTools::SameFile(valueCML, cachedValueCML)) { - setErrorMessage(errorMessage, - std::string("\"CMAKE_HOME_DIRECTORY\" is set but " - "incompatible with configured " - "source directory value.")); - return false; - } - return true; -} - -static bool getOrTestValue(cmState* state, const std::string& key, - std::string& value, - const std::string& keyDescription, - std::string* errorMessage) -{ - const std::string cachedValue = state->GetSafeCacheEntryValue(key); - if (value.empty()) { - value = cachedValue; - } - if (!cachedValue.empty() && cachedValue != value) { - setErrorMessage(errorMessage, - std::string("\"") + key + - "\" is set but incompatible with configured " + - keyDescription + " value."); - return false; - } - return true; -} - -bool cmServerProtocol1::DoActivate(const cmServerRequest& request, - std::string* errorMessage) -{ - std::string sourceDirectory = request.Data[kSOURCE_DIRECTORY_KEY].asString(); - std::string buildDirectory = request.Data[kBUILD_DIRECTORY_KEY].asString(); - std::string generator = request.Data[kGENERATOR_KEY].asString(); - std::string extraGenerator = request.Data[kEXTRA_GENERATOR_KEY].asString(); - std::string toolset = request.Data[kTOOLSET_KEY].asString(); - std::string platform = request.Data[kPLATFORM_KEY].asString(); - - // normalize source and build directory - if (!sourceDirectory.empty()) { - sourceDirectory = cmSystemTools::CollapseFullPath(sourceDirectory); - cmSystemTools::ConvertToUnixSlashes(sourceDirectory); - } - if (!buildDirectory.empty()) { - buildDirectory = cmSystemTools::CollapseFullPath(buildDirectory); - cmSystemTools::ConvertToUnixSlashes(buildDirectory); - } - - if (buildDirectory.empty()) { - setErrorMessage(errorMessage, - std::string("\"") + kBUILD_DIRECTORY_KEY + - "\" is missing."); - return false; - } - - cmake* cm = CMakeInstance(); - if (cmSystemTools::PathExists(buildDirectory)) { - if (!cmSystemTools::FileIsDirectory(buildDirectory)) { - setErrorMessage(errorMessage, - std::string("\"") + kBUILD_DIRECTORY_KEY + - "\" exists but is not a directory."); - return false; - } - - const std::string cachePath = cmake::FindCacheFile(buildDirectory); - if (cm->LoadCache(cachePath)) { - cmState* state = cm->GetState(); - - // Check generator: - if (!getOrTestValue(state, "CMAKE_GENERATOR", generator, "generator", - errorMessage)) { - return false; - } - - // check extra generator: - if (!getOrTestValue(state, "CMAKE_EXTRA_GENERATOR", extraGenerator, - "extra generator", errorMessage)) { - return false; - } - - // check sourcedir: - if (!getOrTestHomeDirectory(state, sourceDirectory, errorMessage)) { - return false; - } - - // check toolset: - if (!getOrTestValue(state, "CMAKE_GENERATOR_TOOLSET", toolset, "toolset", - errorMessage)) { - return false; - } - - // check platform: - if (!getOrTestValue(state, "CMAKE_GENERATOR_PLATFORM", platform, - "platform", errorMessage)) { - return false; - } - } - } - - if (sourceDirectory.empty()) { - setErrorMessage(errorMessage, - std::string("\"") + kSOURCE_DIRECTORY_KEY + - "\" is unset but required."); - return false; - } - if (!cmSystemTools::FileIsDirectory(sourceDirectory)) { - setErrorMessage(errorMessage, - std::string("\"") + kSOURCE_DIRECTORY_KEY + - "\" is not a directory."); - return false; - } - if (generator.empty()) { - setErrorMessage(errorMessage, - std::string("\"") + kGENERATOR_KEY + - "\" is unset but required."); - return false; - } - - std::vector<cmake::GeneratorInfo> generators; - cm->GetRegisteredGenerators(generators); - auto baseIt = std::find_if(generators.begin(), generators.end(), - [&generator](const cmake::GeneratorInfo& info) { - return info.name == generator; - }); - if (baseIt == generators.end()) { - setErrorMessage(errorMessage, - std::string("Generator \"") + generator + - "\" not supported."); - return false; - } - auto extraIt = std::find_if( - generators.begin(), generators.end(), - [&generator, &extraGenerator](const cmake::GeneratorInfo& info) { - return info.baseName == generator && info.extraName == extraGenerator; - }); - if (extraIt == generators.end()) { - setErrorMessage(errorMessage, - std::string("The combination of generator \"" + generator + - "\" and extra generator \"" + extraGenerator + - "\" is not supported.")); - return false; - } - if (!extraIt->supportsToolset && !toolset.empty()) { - setErrorMessage(errorMessage, - std::string("Toolset was provided but is not supported by " - "the requested generator.")); - return false; - } - if (!extraIt->supportsPlatform && !platform.empty()) { - setErrorMessage(errorMessage, - std::string("Platform was provided but is not supported " - "by the requested generator.")); - return false; - } - - this->GeneratorInfo = - GeneratorInformation(generator, extraGenerator, toolset, platform, - sourceDirectory, buildDirectory); - - this->m_State = STATE_ACTIVE; - return true; -} - -void cmServerProtocol1::HandleCMakeFileChanges(const std::string& path, - int event, int status) -{ - assert(status == 0); - static_cast<void>(status); - - if (!m_isDirty) { - m_isDirty = true; - SendSignal(kDIRTY_SIGNAL, Json::objectValue); - } - Json::Value obj = Json::objectValue; - obj[kPATH_KEY] = path; - Json::Value properties = Json::arrayValue; - if (event & UV_RENAME) { - properties.append(kRENAME_PROPERTY_VALUE); - } - if (event & UV_CHANGE) { - properties.append(kCHANGE_PROPERTY_VALUE); - } - - obj[kPROPERTIES_KEY] = properties; - SendSignal(kFILE_CHANGE_SIGNAL, obj); -} - -cmServerResponse cmServerProtocol1::Process(const cmServerRequest& request) -{ - assert(this->m_State >= STATE_ACTIVE); - - if (request.Type == kCACHE_TYPE) { - return this->ProcessCache(request); - } - if (request.Type == kCMAKE_INPUTS_TYPE) { - return this->ProcessCMakeInputs(request); - } - if (request.Type == kCODE_MODEL_TYPE) { - return this->ProcessCodeModel(request); - } - if (request.Type == kCOMPUTE_TYPE) { - return this->ProcessCompute(request); - } - if (request.Type == kCONFIGURE_TYPE) { - return this->ProcessConfigure(request); - } - if (request.Type == kFILESYSTEM_WATCHERS_TYPE) { - return this->ProcessFileSystemWatchers(request); - } - if (request.Type == kGLOBAL_SETTINGS_TYPE) { - return this->ProcessGlobalSettings(request); - } - if (request.Type == kSET_GLOBAL_SETTINGS_TYPE) { - return this->ProcessSetGlobalSettings(request); - } - if (request.Type == kCTEST_INFO_TYPE) { - return this->ProcessCTests(request); - } - - return request.ReportError("Unknown command!"); -} - -bool cmServerProtocol1::IsExperimental() const -{ - return true; -} - -cmServerResponse cmServerProtocol1::ProcessCache( - const cmServerRequest& request) -{ - cmState* state = this->CMakeInstance()->GetState(); - - Json::Value result = Json::objectValue; - - std::vector<std::string> allKeys = state->GetCacheEntryKeys(); - - Json::Value list = Json::arrayValue; - std::vector<std::string> keys = toStringList(request.Data[kKEYS_KEY]); - if (keys.empty()) { - keys = allKeys; - } else { - for (auto const& i : keys) { - if (!cm::contains(allKeys, i)) { - return request.ReportError("Key \"" + i + "\" not found in cache."); - } - } - } - std::sort(keys.begin(), keys.end()); - for (auto const& key : keys) { - Json::Value entry = Json::objectValue; - entry[kKEY_KEY] = key; - entry[kTYPE_KEY] = - cmState::CacheEntryTypeToString(state->GetCacheEntryType(key)); - entry[kVALUE_KEY] = *state->GetCacheEntryValue(key); - - Json::Value props = Json::objectValue; - bool haveProperties = false; - for (auto const& prop : state->GetCacheEntryPropertyList(key)) { - haveProperties = true; - props[prop] = *state->GetCacheEntryProperty(key, prop); - } - if (haveProperties) { - entry[kPROPERTIES_KEY] = props; - } - - list.append(entry); - } - - result[kCACHE_KEY] = list; - return request.Reply(result); -} - -cmServerResponse cmServerProtocol1::ProcessCMakeInputs( - const cmServerRequest& request) -{ - if (this->m_State < STATE_CONFIGURED) { - return request.ReportError("This instance was not yet configured."); - } - - const cmake* cm = this->CMakeInstance(); - const std::string cmakeRootDir = cmSystemTools::GetCMakeRoot(); - const std::string& sourceDir = cm->GetHomeDirectory(); - - Json::Value result = Json::objectValue; - result[kSOURCE_DIRECTORY_KEY] = sourceDir; - result[kCMAKE_ROOT_DIRECTORY_KEY] = cmakeRootDir; - result[kBUILD_FILES_KEY] = cmDumpCMakeInputs(cm); - return request.Reply(result); -} - -cmServerResponse cmServerProtocol1::ProcessCodeModel( - const cmServerRequest& request) -{ - if (this->m_State != STATE_COMPUTED) { - return request.ReportError("No build system was generated yet."); - } - - return request.Reply(cmDumpCodeModel(this->CMakeInstance())); -} - -cmServerResponse cmServerProtocol1::ProcessCompute( - const cmServerRequest& request) -{ - if (this->m_State > STATE_CONFIGURED) { - return request.ReportError("This build system was already generated."); - } - if (this->m_State < STATE_CONFIGURED) { - return request.ReportError("This project was not configured yet."); - } - - cmake* cm = this->CMakeInstance(); - int ret = cm->Generate(); - - if (ret < 0) { - return request.ReportError("Failed to compute build system."); - } - m_State = STATE_COMPUTED; - return request.Reply(Json::Value()); -} - -cmServerResponse cmServerProtocol1::ProcessConfigure( - const cmServerRequest& request) -{ - if (this->m_State == STATE_INACTIVE) { - return request.ReportError("This instance is inactive."); - } - - FileMonitor()->StopMonitoring(); - - std::string errorMessage; - cmake* cm = this->CMakeInstance(); - this->GeneratorInfo.SetupGenerator(cm, &errorMessage); - if (!errorMessage.empty()) { - return request.ReportError(errorMessage); - } - - // Make sure the types of cacheArguments matches (if given): - std::vector<std::string> cacheArgs = { "unused" }; - bool cacheArgumentsError = false; - const Json::Value passedArgs = request.Data[kCACHE_ARGUMENTS_KEY]; - if (!passedArgs.isNull()) { - if (passedArgs.isString()) { - cacheArgs.push_back(passedArgs.asString()); - } else if (passedArgs.isArray()) { - for (auto const& arg : passedArgs) { - if (!arg.isString()) { - cacheArgumentsError = true; - break; - } - cacheArgs.push_back(arg.asString()); - } - } else { - cacheArgumentsError = true; - } - } - if (cacheArgumentsError) { - request.ReportError( - "cacheArguments must be unset, a string or an array of strings."); - } - - std::string sourceDir = cm->GetHomeDirectory(); - const std::string buildDir = cm->GetHomeOutputDirectory(); - - cmGlobalGenerator* gg = cm->GetGlobalGenerator(); - - if (buildDir.empty()) { - return request.ReportError("No build directory set via Handshake."); - } - - if (cm->LoadCache(buildDir)) { - // build directory has been set up before - cmProp cachedSourceDir = - cm->GetState()->GetInitializedCacheValue("CMAKE_HOME_DIRECTORY"); - if (!cachedSourceDir) { - return request.ReportError("No CMAKE_HOME_DIRECTORY found in cache."); - } - if (sourceDir.empty()) { - sourceDir = *cachedSourceDir; - cm->SetHomeDirectory(sourceDir); - } - - cmProp cachedGenerator = - cm->GetState()->GetInitializedCacheValue("CMAKE_GENERATOR"); - if (cachedGenerator) { - if (gg && gg->GetName() != *cachedGenerator) { - return request.ReportError("Configured generator does not match with " - "CMAKE_GENERATOR found in cache."); - } - } - } else { - // build directory has not been set up before - if (sourceDir.empty()) { - return request.ReportError("No sourceDirectory set via " - "setGlobalSettings and no cache found in " - "buildDirectory."); - } - } - - cmSystemTools::ResetErrorOccuredFlag(); // Reset error state - - if (cm->AddCMakePaths() != 1) { - return request.ReportError("Failed to set CMake paths."); - } - - if (!cm->SetCacheArgs(cacheArgs)) { - return request.ReportError("cacheArguments could not be set."); - } - - int ret = cm->Configure(); - cm->IssueMessage( - MessageType::DEPRECATION_WARNING, - "The 'cmake-server(7)' is deprecated. " - "Please port clients to use the 'cmake-file-api(7)' instead."); - if (ret < 0) { - return request.ReportError("Configuration failed."); - } - - std::vector<std::string> toWatchList; - cmGetCMakeInputs(gg, std::string(), buildDir, nullptr, &toWatchList, - nullptr); - - FileMonitor()->MonitorPaths(toWatchList, - [this](const std::string& p, int e, int s) { - this->HandleCMakeFileChanges(p, e, s); - }); - - m_State = STATE_CONFIGURED; - m_isDirty = false; - return request.Reply(Json::Value()); -} - -cmServerResponse cmServerProtocol1::ProcessGlobalSettings( - const cmServerRequest& request) -{ - cmake* cm = this->CMakeInstance(); - Json::Value obj = Json::objectValue; - - // Capabilities information: - obj[kCAPABILITIES_KEY] = cm->ReportCapabilitiesJson(); - - obj[kDEBUG_OUTPUT_KEY] = cm->GetDebugOutput(); - obj[kTRACE_KEY] = cm->GetTrace(); - obj[kTRACE_EXPAND_KEY] = cm->GetTraceExpand(); - obj[kWARN_UNINITIALIZED_KEY] = cm->GetWarnUninitialized(); - obj[kWARN_UNUSED_KEY] = m_WarnUnused; - obj[kWARN_UNUSED_CLI_KEY] = cm->GetWarnUnusedCli(); - obj[kCHECK_SYSTEM_VARS_KEY] = cm->GetCheckSystemVars(); - - obj[kSOURCE_DIRECTORY_KEY] = this->GeneratorInfo.SourceDirectory; - obj[kBUILD_DIRECTORY_KEY] = this->GeneratorInfo.BuildDirectory; - - // Currently used generator: - obj[kGENERATOR_KEY] = this->GeneratorInfo.GeneratorName; - obj[kEXTRA_GENERATOR_KEY] = this->GeneratorInfo.ExtraGeneratorName; - - return request.Reply(obj); -} - -static void setBool(const cmServerRequest& request, const std::string& key, - std::function<void(bool)> const& setter) -{ - if (request.Data[key].isNull()) { - return; - } - setter(request.Data[key].asBool()); -} - -cmServerResponse cmServerProtocol1::ProcessSetGlobalSettings( - const cmServerRequest& request) -{ - const std::vector<std::string> boolValues = { - kDEBUG_OUTPUT_KEY, kTRACE_KEY, kTRACE_EXPAND_KEY, - kWARN_UNINITIALIZED_KEY, kWARN_UNUSED_KEY, kWARN_UNUSED_CLI_KEY, - kCHECK_SYSTEM_VARS_KEY - }; - for (std::string const& i : boolValues) { - if (!request.Data[i].isNull() && !request.Data[i].isBool()) { - return request.ReportError("\"" + i + - "\" must be unset or a bool value."); - } - } - - cmake* cm = this->CMakeInstance(); - - setBool(request, kDEBUG_OUTPUT_KEY, - [cm](bool e) { cm->SetDebugOutputOn(e); }); - setBool(request, kTRACE_KEY, [cm](bool e) { cm->SetTrace(e); }); - setBool(request, kTRACE_EXPAND_KEY, [cm](bool e) { cm->SetTraceExpand(e); }); - setBool(request, kWARN_UNINITIALIZED_KEY, - [cm](bool e) { cm->SetWarnUninitialized(e); }); - setBool(request, kWARN_UNUSED_KEY, [this](bool e) { m_WarnUnused = e; }); - setBool(request, kWARN_UNUSED_CLI_KEY, - [cm](bool e) { cm->SetWarnUnusedCli(e); }); - setBool(request, kCHECK_SYSTEM_VARS_KEY, - [cm](bool e) { cm->SetCheckSystemVars(e); }); - - return request.Reply(Json::Value()); -} - -cmServerResponse cmServerProtocol1::ProcessFileSystemWatchers( - const cmServerRequest& request) -{ - const cmFileMonitor* const fm = FileMonitor(); - Json::Value result = Json::objectValue; - Json::Value files = Json::arrayValue; - for (auto const& f : fm->WatchedFiles()) { - files.append(f); - } - Json::Value directories = Json::arrayValue; - for (auto const& d : fm->WatchedDirectories()) { - directories.append(d); - } - result[kWATCHED_FILES_KEY] = files; - result[kWATCHED_DIRECTORIES_KEY] = directories; - - return request.Reply(result); -} - -cmServerResponse cmServerProtocol1::ProcessCTests( - const cmServerRequest& request) -{ - if (this->m_State < STATE_COMPUTED) { - return request.ReportError("This instance was not yet computed."); - } - - return request.Reply(cmDumpCTestInfo(this->CMakeInstance())); -} - -cmServerProtocol1::GeneratorInformation::GeneratorInformation( - std::string generatorName, std::string extraGeneratorName, - std::string toolset, std::string platform, std::string sourceDirectory, - std::string buildDirectory) - : GeneratorName(std::move(generatorName)) - , ExtraGeneratorName(std::move(extraGeneratorName)) - , Toolset(std::move(toolset)) - , Platform(std::move(platform)) - , SourceDirectory(std::move(sourceDirectory)) - , BuildDirectory(std::move(buildDirectory)) -{ -} - -void cmServerProtocol1::GeneratorInformation::SetupGenerator( - cmake* cm, std::string* errorMessage) -{ - const std::string fullGeneratorName = - cmExternalMakefileProjectGenerator::CreateFullGeneratorName( - GeneratorName, ExtraGeneratorName); - - cm->SetHomeDirectory(SourceDirectory); - cm->SetHomeOutputDirectory(BuildDirectory); - - auto gg = cm->CreateGlobalGenerator(fullGeneratorName); - if (!gg) { - setErrorMessage( - errorMessage, - std::string("Could not set up the requested combination of \"") + - kGENERATOR_KEY + "\" and \"" + kEXTRA_GENERATOR_KEY + "\""); - return; - } - - cm->SetGlobalGenerator(std::move(gg)); - - cm->SetGeneratorToolset(Toolset); - cm->SetGeneratorPlatform(Platform); -} diff --git a/Source/cmServerProtocol.h b/Source/cmServerProtocol.h deleted file mode 100644 index 6009e23..0000000 --- a/Source/cmServerProtocol.h +++ /dev/null @@ -1,162 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#pragma once - -#include "cmConfigure.h" // IWYU pragma: keep - -#include <memory> -#include <string> -#include <utility> - -#include <cm3p/json/value.h> - -#include "cmake.h" - -class cmConnection; -class cmFileMonitor; -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: - 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; - cmConnection* Connection; - -private: - cmServerRequest(cmServer* server, cmConnection* connection, std::string t, - std::string c, 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; -}; - -class cmServerProtocol -{ -public: - cmServerProtocol() = default; - virtual ~cmServerProtocol() = default; - - cmServerProtocol(cmServerProtocol const&) = delete; - cmServerProtocol& operator=(cmServerProtocol const&) = delete; - - virtual std::pair<int, int> ProtocolVersion() const = 0; - virtual bool IsExperimental() const = 0; - virtual cmServerResponse Process(const cmServerRequest& request) = 0; - - bool Activate(cmServer* server, const cmServerRequest& request, - std::string* errorMessage); - - cmFileMonitor* FileMonitor() const; - void SendSignal(const std::string& name, const Json::Value& data) const; - -protected: - cmake* CMakeInstance() const; - // Implement protocol specific activation tasks here. Called from Activate(). - virtual bool DoActivate(const cmServerRequest& request, - std::string* errorMessage); - bool m_WarnUnused = false; // storage for legacy option - -private: - std::unique_ptr<cmake> m_CMakeInstance; - cmServer* m_Server = nullptr; // not owned! - - friend class cmServer; -}; - -class cmServerProtocol1 : public cmServerProtocol -{ -public: - std::pair<int, int> ProtocolVersion() const override; - bool IsExperimental() const override; - cmServerResponse Process(const cmServerRequest& request) override; - -private: - bool DoActivate(const cmServerRequest& request, - std::string* errorMessage) override; - - void HandleCMakeFileChanges(const std::string& path, int event, int status); - - // Handle requests: - cmServerResponse ProcessCache(const cmServerRequest& request); - cmServerResponse ProcessCMakeInputs(const cmServerRequest& request); - cmServerResponse ProcessCodeModel(const cmServerRequest& request); - cmServerResponse ProcessCompute(const cmServerRequest& request); - cmServerResponse ProcessConfigure(const cmServerRequest& request); - cmServerResponse ProcessGlobalSettings(const cmServerRequest& request); - cmServerResponse ProcessSetGlobalSettings(const cmServerRequest& request); - cmServerResponse ProcessFileSystemWatchers(const cmServerRequest& request); - cmServerResponse ProcessCTests(const cmServerRequest& request); - - enum State - { - STATE_INACTIVE, - STATE_ACTIVE, - STATE_CONFIGURED, - STATE_COMPUTED - }; - State m_State = STATE_INACTIVE; - - bool m_isDirty = false; - - struct GeneratorInformation - { - public: - GeneratorInformation() = default; - GeneratorInformation(std::string generatorName, - std::string extraGeneratorName, std::string toolset, - std::string platform, std::string sourceDirectory, - std::string buildDirectory); - - void SetupGenerator(cmake* cm, std::string* errorMessage); - - std::string GeneratorName; - std::string ExtraGeneratorName; - std::string Toolset; - std::string Platform; - - std::string SourceDirectory; - std::string BuildDirectory; - }; - - GeneratorInformation GeneratorInfo; -}; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 2a14b03..720a567 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -263,7 +263,7 @@ Json::Value cmake::ReportCapabilitiesJson() const } obj["generators"] = generators; obj["fileApi"] = cmFileAPI::ReportCapabilities(); - obj["serverMode"] = true; + obj["serverMode"] = false; return obj; } diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 0bdc6fa..61ff470 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -28,8 +28,6 @@ #if !defined(CMAKE_BOOTSTRAP) # include "cmDependsFortran.h" // For -E cmake_copy_f90_mod callback. # include "cmFileTime.h" -# include "cmServer.h" -# include "cmServerConnection.h" # include "bindexplib.h" #endif @@ -59,8 +57,6 @@ #include "cmsys/Process.h" #include "cmsys/Terminal.h" -class cmConnection; - int cmcmd_cmake_ninja_depends(std::vector<std::string>::const_iterator argBeg, std::vector<std::string>::const_iterator argEnd); int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg, @@ -119,7 +115,6 @@ void CMakeCommandUsage(const char* program) "(on one volume)\n" << " rm [-rRf] <file/dir>... - remove files or directories, use -f to " "force it, r or R to remove directories and their contents recursively\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" @@ -1351,47 +1346,8 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args) } if (args[1] == "server") { - const std::string pipePrefix = "--pipe="; - bool supportExperimental = false; - bool isDebug = false; - std::string pipe; - - for (auto const& arg : cmMakeRange(args).advance(2)) { - if (arg == "--experimental") { - supportExperimental = true; - } else if (arg == "--debug") { - pipe.clear(); - isDebug = true; - } else if (cmHasPrefix(arg, pipePrefix)) { - isDebug = false; - pipe = arg.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(CMAKE_BOOTSTRAP) - cmConnection* conn; - if (isDebug) { - conn = new cmServerStdIoConnection; - } else { - conn = new cmServerPipeConnection(pipe); - } - cmServer server(conn, supportExperimental); - std::string errorMessage; - if (server.Serve(&errorMessage)) { - return 0; - } - cmSystemTools::Error(errorMessage); -#else - static_cast<void>(supportExperimental); - static_cast<void>(isDebug); - cmSystemTools::Error("CMake was not built with server mode enabled"); -#endif + cmSystemTools::Error( + "CMake server mode has been removed in favor of the file-api."); return 1; } diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index f4be4b6..328ab7f 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -240,8 +240,6 @@ if(BUILD_TESTING) if(NOT CMake_TEST_EXTERNAL_CMAKE) add_subdirectory(CMakeLib) - - add_subdirectory(CMakeServerLib) endif() add_subdirectory(CMakeOnly) add_subdirectory(RunCMake) @@ -2911,13 +2909,6 @@ if(BUILD_TESTING) ADD_TEST_MACRO(CMakeCommands.link_directories) ADD_TEST_MACRO(CMakeCommands.target_link_directories) - # The cmake server-mode test requires python for a simple client. - find_package(PythonInterp QUIET) - if(PYTHON_EXECUTABLE) - set(Server_BUILD_OPTIONS -DPYTHON_EXECUTABLE:FILEPATH=${PYTHON_EXECUTABLE}) - ADD_TEST_MACRO(Server Server) - endif() - configure_file( "${CMake_SOURCE_DIR}/Tests/CTestTestCrash/test.cmake.in" "${CMake_BINARY_DIR}/Tests/CTestTestCrash/test.cmake" diff --git a/Tests/CMakeServerLib/CMakeLists.txt b/Tests/CMakeServerLib/CMakeLists.txt deleted file mode 100644 index 2c23c2d..0000000 --- a/Tests/CMakeServerLib/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -include_directories( - ${CMAKE_CURRENT_BINARY_DIR} - ${CMake_BINARY_DIR}/Source - ${CMake_SOURCE_DIR}/Source - ) - -set(CMakeServerLib_TESTS - testServerBuffering.cpp - ) - -create_test_sourcelist(CMakeLib_TEST_SRCS CMakeServerLibTests.cxx ${CMakeServerLib_TESTS}) -add_executable(CMakeServerLibTests ${CMakeLib_TEST_SRCS}) -target_link_libraries(CMakeServerLibTests CMakeLib CMakeServerLib) - -SET_PROPERTY(TARGET CMakeServerLibTests PROPERTY C_CLANG_TIDY "") -SET_PROPERTY(TARGET CMakeServerLibTests PROPERTY CXX_CLANG_TIDY "") - -foreach(testfile ${CMakeServerLib_TESTS}) - get_filename_component(test "${testfile}" NAME_WE) - add_test(CMakeServerLib.${test} CMakeServerLibTests ${test} ${${test}_ARGS}) -endforeach() diff --git a/Tests/CMakeServerLib/testServerBuffering.cpp b/Tests/CMakeServerLib/testServerBuffering.cpp deleted file mode 100644 index 6f22940..0000000 --- a/Tests/CMakeServerLib/testServerBuffering.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include <iostream> -#include <memory> -#include <string> -#include <vector> - -#include "cmConnection.h" -#include "cmServerConnection.h" - -void print_error(const std::vector<std::string>& input, - const std::vector<std::string>& output) -{ - std::cerr << "Responses don't equal input messages input." << std::endl; - std::cerr << "Responses: " << std::endl; - - for (auto& msg : output) { - std::cerr << "'" << msg << "'" << std::endl; - } - - std::cerr << "Input messages" << std::endl; - for (auto& msg : input) { - std::cerr << "'" << msg << "'" << std::endl; - } -} - -std::string trim_newline(const std::string& _buffer) -{ - auto buffer = _buffer; - while (!buffer.empty() && (buffer.back() == '\n' || buffer.back() == '\r')) { - buffer.pop_back(); - } - return buffer; -} - -int testServerBuffering(int, char** const) -{ - std::vector<std::string> messages = { - "{ \"test\": 10}", "{ \"test\": { \"test2\": false} }", - "{ \"test\": [1, 2, 3] }", - "{ \"a\": { \"1\": {}, \n\n\n \"2\":[] \t\t\t\t}}" - }; - - std::string fullMessage; - for (auto& msg : messages) { - fullMessage += "[== \"CMake Server\" ==[\n"; - fullMessage += msg; - fullMessage += "\n]== \"CMake Server\" ==]\n"; - } - - // The buffering strategy should cope with any fragmentation, including - // just getting the characters one at a time. - auto bufferingStrategy = - std::unique_ptr<cmConnectionBufferStrategy>(new cmServerBufferStrategy); - std::vector<std::string> response; - std::string rawBuffer; - for (auto& messageChar : fullMessage) { - rawBuffer += messageChar; - std::string packet = bufferingStrategy->BufferMessage(rawBuffer); - do { - if (!packet.empty() && packet != "\r\n") { - response.push_back(trim_newline(packet)); - } - packet = bufferingStrategy->BufferMessage(rawBuffer); - } while (!packet.empty()); - } - - if (response != messages) { - print_error(messages, response); - return 1; - } - - // We should also be able to deal with getting a bunch at once - response.clear(); - std::string packet = bufferingStrategy->BufferMessage(fullMessage); - do { - if (!packet.empty() && packet != "\r\n") { - response.push_back(trim_newline(packet)); - } - packet = bufferingStrategy->BufferMessage(fullMessage); - } while (!packet.empty()); - - if (response != messages) { - print_error(messages, response); - return 1; - } - - return 0; -} diff --git a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt index e24e131..a8b6584 100644 --- a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt +++ b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt @@ -1 +1 @@ -^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":2}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":true,"version":{.*}}$ +^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":2}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"version":{.*}}$ diff --git a/Tests/RunCMake/CommandLine/E_server-arg-stderr.txt b/Tests/RunCMake/CommandLine/E_server-arg-stderr.txt deleted file mode 100644 index 4dcbab9..0000000 --- a/Tests/RunCMake/CommandLine/E_server-arg-stderr.txt +++ /dev/null @@ -1 +0,0 @@ -^CMake Error: Unknown argument for server mode$ diff --git a/Tests/RunCMake/CommandLine/E_server-pipe-result.txt b/Tests/RunCMake/CommandLine/E_server-pipe-result.txt deleted file mode 100644 index 0cfbf08..0000000 --- a/Tests/RunCMake/CommandLine/E_server-pipe-result.txt +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/Tests/RunCMake/CommandLine/E_server-pipe-stderr.txt b/Tests/RunCMake/CommandLine/E_server-pipe-stderr.txt deleted file mode 100644 index 7193ba6..0000000 --- a/Tests/RunCMake/CommandLine/E_server-pipe-stderr.txt +++ /dev/null @@ -1 +0,0 @@ -^CMake Error: No pipe given after --pipe=$ diff --git a/Tests/RunCMake/CommandLine/E_server-arg-result.txt b/Tests/RunCMake/CommandLine/E_server-result.txt index d00491f..d00491f 100644 --- a/Tests/RunCMake/CommandLine/E_server-arg-result.txt +++ b/Tests/RunCMake/CommandLine/E_server-result.txt diff --git a/Tests/RunCMake/CommandLine/E_server-stderr.txt b/Tests/RunCMake/CommandLine/E_server-stderr.txt new file mode 100644 index 0000000..0cd0e56 --- /dev/null +++ b/Tests/RunCMake/CommandLine/E_server-stderr.txt @@ -0,0 +1 @@ +^CMake Error: CMake server mode has been removed in favor of the file-api\.$ diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake index 2a5d5d3..49a10c1 100644 --- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake +++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake @@ -25,8 +25,7 @@ run_cmake_command(E_compare_files-ignore-eol-nonexistent ${CMAKE_COMMAND} -E com run_cmake_command(E_compare_files-invalid-arguments ${CMAKE_COMMAND} -E compare_files file1.txt file2.txt file3.txt) 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_server ${CMAKE_COMMAND} -E server) run_cmake_command(E_true ${CMAKE_COMMAND} -E true) run_cmake_command(E_true-extraargs ${CMAKE_COMMAND} -E true ignored) run_cmake_command(E_false ${CMAKE_COMMAND} -E false) diff --git a/Tests/Server/CMakeLists.txt b/Tests/Server/CMakeLists.txt deleted file mode 100644 index 8321edb..0000000 --- a/Tests/Server/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -cmake_minimum_required(VERSION 3.4) -project(Server CXX) - -find_package(Python REQUIRED) - -macro(do_test bsname file type) - execute_process(COMMAND ${Python_EXECUTABLE} - -B # no .pyc files - "${CMAKE_SOURCE_DIR}/${type}-test.py" - "${CMAKE_COMMAND}" - "${CMAKE_SOURCE_DIR}/${file}" - "${CMAKE_SOURCE_DIR}" - "${CMAKE_BINARY_DIR}" - "${CMAKE_GENERATOR}" - RESULT_VARIABLE test_result - ) - - if (NOT test_result EQUAL 0) - message(SEND_ERROR "TEST FAILED: ${test_result}") - endif() -endmacro() - -do_test("test_cache" "tc_cache.json" "server") -do_test("test_handshake" "tc_handshake.json" "server") -do_test("test_globalSettings" "tc_globalSettings.json" "server") -do_test("test_buildsystem1" "tc_buildsystem1.json" "server") - -add_executable(Server empty.cpp) diff --git a/Tests/Server/buildsystem1/CMakeLists.txt b/Tests/Server/buildsystem1/CMakeLists.txt deleted file mode 100644 index d690472..0000000 --- a/Tests/Server/buildsystem1/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -cmake_minimum_required(VERSION 3.4) - -project(buildsystem2) - -set(var1 123) - -set(var2 345) - -add_executable(main main.cpp) - -add_executable(m_other main.cpp) - -add_library(foo foo.cpp) - -function(f1) -endfunction() - -set(var3 345) - -add_library(someImportedLib UNKNOWN IMPORTED) - -add_subdirectory(subdir) diff --git a/Tests/Server/buildsystem1/foo.cpp b/Tests/Server/buildsystem1/foo.cpp deleted file mode 100644 index 7f39d71..0000000 --- a/Tests/Server/buildsystem1/foo.cpp +++ /dev/null @@ -1,5 +0,0 @@ - -int foo() -{ - return 0; -} diff --git a/Tests/Server/buildsystem1/main.cpp b/Tests/Server/buildsystem1/main.cpp deleted file mode 100644 index 766b775..0000000 --- a/Tests/Server/buildsystem1/main.cpp +++ /dev/null @@ -1,5 +0,0 @@ - -int main() -{ - return 0; -} diff --git a/Tests/Server/buildsystem1/subdir/CMakeLists.txt b/Tests/Server/buildsystem1/subdir/CMakeLists.txt deleted file mode 100644 index 9157312..0000000 --- a/Tests/Server/buildsystem1/subdir/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(bar4 something) - -set(bar5 more) - -add_executable(ooo empty.cpp) diff --git a/Tests/Server/buildsystem1/subdir/empty.cpp b/Tests/Server/buildsystem1/subdir/empty.cpp deleted file mode 100644 index 7f39d71..0000000 --- a/Tests/Server/buildsystem1/subdir/empty.cpp +++ /dev/null @@ -1,5 +0,0 @@ - -int foo() -{ - return 0; -} diff --git a/Tests/Server/cmakelib.py b/Tests/Server/cmakelib.py deleted file mode 100644 index 546ae4c..0000000 --- a/Tests/Server/cmakelib.py +++ /dev/null @@ -1,380 +0,0 @@ -from __future__ import print_function -import sys, subprocess, json, os, select, shutil, time, socket - -termwidth = 150 - -print_communication = True - -def ordered(obj): - if isinstance(obj, dict): - return sorted((k, ordered(v)) for k, v in obj.items()) - if isinstance(obj, list): - return sorted(ordered(x) for x in obj) - else: - return obj - -def col_print(title, array): - print() - print() - print(title) - - indentwidth = 4 - indent = " " * indentwidth - - if not array: - print(indent + "<None>") - return - - padwidth = 2 - - maxitemwidth = len(max(array, key=len)) - - numCols = max(1, int((termwidth - indentwidth + padwidth) / (maxitemwidth + padwidth))) - - numRows = len(array) // numCols + 1 - - pad = " " * padwidth - - for index in range(numRows): - print(indent + pad.join(item.ljust(maxitemwidth) for item in array[index::numRows])) - -filterPacket = lambda x: x - -STDIN = 0 -PIPE = 1 - -communicationMethods = [STDIN] - -if hasattr(socket, 'AF_UNIX'): - communicationMethods.append(PIPE) - -def defaultExitWithError(proc): - data = "" - try: - while select.select([proc.outPipe], [], [], 3.)[0]: - data = data + proc.outPipe.read(1) - if len(data): - print("Rest of raw buffer from server:") - printServer(data) - except: - pass - proc.outPipe.close() - proc.inPipe.close() - proc.kill() - sys.exit(1) - -exitWithError = lambda proc: defaultExitWithError(proc) - -serverTag = "SERVER" - -def printServer(*args): - print(serverTag + ">", *args) - print() - sys.stdout.flush() - -def printClient(*args): - print("CLIENT>", *args) - print() - sys.stdout.flush() - -def waitForRawMessage(cmakeCommand): - stdoutdata = "" - payload = "" - while not cmakeCommand.poll(): - stdoutdataLine = cmakeCommand.outPipe.readline() - if stdoutdataLine: - stdoutdata += stdoutdataLine.decode('utf-8') - else: - break - begin = stdoutdata.find('[== "CMake Server" ==[\n') - end = stdoutdata.find(']== "CMake Server" ==]') - - if begin != -1 and end != -1: - begin += len('[== "CMake Server" ==[\n') - payload = stdoutdata[begin:end] - jsonPayload = json.loads(payload) - filteredPayload = filterPacket(jsonPayload) - if print_communication and filteredPayload: - printServer(filteredPayload) - if filteredPayload is not None or jsonPayload is None: - return jsonPayload - stdoutdata = stdoutdata[(end+len(']== "CMake Server" ==]')):] - -# Python2 has no problem writing the output of encodes directly, -# but Python3 returns only 'int's for encode and so must be turned -# into bytes. We use the existence of 'to_bytes' on an int to -# determine which behavior is appropriate. It might be more clear -# to do this in the code which uses the flag, but introducing -# this lookup cost at every byte sent isn't ideal. -has_to_bytes = "to_bytes" in dir(10) - -def writeRawData(cmakeCommand, content): - writeRawData.counter += 1 - payload = """ -[== "CMake Server" ==[ -%s -]== "CMake Server" ==] -""" % content - - rn = ( writeRawData.counter % 2 ) == 0 - - if rn: - payload = payload.replace('\n', '\r\n') - - if print_communication: - printClient(content, "(Use \\r\\n:", rn, ")") - - # To stress test how cmake deals with fragmentation in the - # communication channel, we send only one byte at a time. - # Certain communication methods / platforms might still buffer - # it all into one message since its so close together, but in - # general this will catch places where we assume full buffers - # come in all at once. - encoded_payload = payload.encode('utf-8') - - # Python version 3+ can't write ints directly; but 'to_bytes' - # for int was only added in python 3.2. If this is a 3+ version - # of python without that conversion function; just write the whole - # thing out at once. - if sys.version_info[0] > 2 and not has_to_bytes: - cmakeCommand.write(encoded_payload) - else: - for c in encoded_payload: - if has_to_bytes: - c = c.to_bytes(1, byteorder='big') - cmakeCommand.write(c) - -writeRawData.counter = 0 - -def writePayload(cmakeCommand, obj): - writeRawData(cmakeCommand, json.dumps(obj)) - -def getPipeName(): - return "/tmp/server-test-socket" - -def attachPipe(cmakeCommand, pipeName): - time.sleep(1) - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.connect(pipeName) - global serverTag - serverTag = "SERVER(PIPE)" - cmakeCommand.outPipe = sock.makefile() - cmakeCommand.inPipe = sock - cmakeCommand.write = cmakeCommand.inPipe.sendall - -def writeAndFlush(pipe, val): - pipe.write(val) - pipe.flush() - -def initServerProc(cmakeCommand, comm): - if comm == PIPE: - pipeName = getPipeName() - cmakeCommand = subprocess.Popen([cmakeCommand, "-E", "server", "--experimental", "--pipe=" + pipeName]) - attachPipe(cmakeCommand, pipeName) - else: - cmakeCommand = subprocess.Popen([cmakeCommand, "-E", "server", "--experimental", "--debug"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) - cmakeCommand.outPipe = cmakeCommand.stdout - cmakeCommand.inPipe = cmakeCommand.stdin - cmakeCommand.write = lambda val: writeAndFlush(cmakeCommand.inPipe, val) - - packet = waitForRawMessage(cmakeCommand) - if packet == None: - print("Not in server mode") - sys.exit(2) - - if packet['type'] != 'hello': - print("No hello message") - sys.exit(3) - - return cmakeCommand - -def exitProc(cmakeCommand): - # Tell the server to exit. - cmakeCommand.stdin.close() - cmakeCommand.stdout.close() - - # Wait for the server to exit. - # If this version of python supports it, terminate the server after a timeout. - try: - cmakeCommand.wait(timeout=5) - except TypeError: - cmakeCommand.wait() - except: - cmakeCommand.terminate() - raise - -def waitForMessage(cmakeCommand, expected): - data = ordered(expected) - packet = ordered(waitForRawMessage(cmakeCommand)) - - if packet != data: - print ("Received unexpected message; test failed") - exitWithError(cmakeCommand) - return packet - -def waitForReply(cmakeCommand, originalType, cookie, skipProgress): - gotResult = False - while True: - packet = waitForRawMessage(cmakeCommand) - t = packet['type'] - if packet['cookie'] != cookie or packet['inReplyTo'] != originalType: - print("cookie or inReplyTo mismatch") - sys.exit(4) - if t == 'message' or t == 'progress': - if skipProgress: - continue - if t == 'reply': - break - print("Unrecognized message", packet) - sys.exit(5) - - return packet - -def waitForError(cmakeCommand, originalType, cookie, message): - packet = waitForRawMessage(cmakeCommand) - if packet['cookie'] != cookie or packet['type'] != 'error' or packet['inReplyTo'] != originalType or packet['errorMessage'] != message: - sys.exit(6) - -def waitForProgress(cmakeCommand, originalType, cookie, current, message): - packet = waitForRawMessage(cmakeCommand) - if packet['cookie'] != cookie or packet['type'] != 'progress' or packet['inReplyTo'] != originalType or packet['progressCurrent'] != current or packet['progressMessage'] != message: - sys.exit(7) - -def handshake(cmakeCommand, major, minor, source, build, generator, extraGenerator): - version = { 'major': major } - if minor >= 0: - version['minor'] = minor - - writePayload(cmakeCommand, { 'type': 'handshake', 'protocolVersion': version, - 'cookie': 'TEST_HANDSHAKE', 'sourceDirectory': source, 'buildDirectory': build, - 'generator': generator, 'extraGenerator': extraGenerator }) - waitForReply(cmakeCommand, 'handshake', 'TEST_HANDSHAKE', False) - -def validateGlobalSettings(cmakeCommand, cmakeCommandPath, data): - packet = waitForReply(cmakeCommand, 'globalSettings', '', False) - - capabilities = packet['capabilities'] - - # validate version: - cmakeoutput = subprocess.check_output([ cmakeCommandPath, "--version" ], universal_newlines=True) - cmakeVersion = cmakeoutput.splitlines()[0][14:] - - version = capabilities['version'] - versionString = version['string'] - vs = str(version['major']) + '.' + str(version['minor']) + '.' + str(version['patch']) - if (versionString != vs and not versionString.startswith(vs + '-')): - sys.exit(8) - if (versionString != cmakeVersion): - sys.exit(9) - - # validate generators: - generatorObjects = capabilities['generators'] - - cmakeoutput = subprocess.check_output([ cmakeCommandPath, "--help" ], universal_newlines=True) - index = cmakeoutput.index('\nGenerators\n\n') - cmakeGenerators = [] - for line in cmakeoutput[index + 12:].splitlines(): - if not line: - continue - if line[0] == '*': # default generator marker - line = ' ' + line[1:] - if not line.startswith(' '): - continue - if line.startswith(' '): - continue - equalPos = line.find('=') - tmp = '' - if (equalPos > 0): - tmp = line[2:equalPos].strip() - else: - tmp = line.strip() - if tmp.endswith(" [arch]"): - tmp = tmp[0:len(tmp) - 7] - if (len(tmp) > 0) and (" - " not in tmp): - cmakeGenerators.append(tmp) - - generators = [] - for genObj in generatorObjects: - generators.append(genObj['name']) - - generators.sort() - cmakeGenerators.sort() - - for gen in cmakeGenerators: - if (not gen in generators): - sys.exit(10) - - gen = packet['generator'] - if (gen != '' and not (gen in generators)): - sys.exit(11) - - for i in data: - print("Validating", i) - if (packet[i] != data[i]): - sys.exit(12) - -def validateCache(cmakeCommand, data): - packet = waitForReply(cmakeCommand, 'cache', '', False) - - cache = packet['cache'] - - if (data['isEmpty']): - if (cache != []): - print('Expected empty cache, but got data.\n') - sys.exit(1) - return; - - if (cache == []): - print('Expected cache contents, but got none.\n') - sys.exit(1) - - hadHomeDir = False - for value in cache: - if (value['key'] == 'CMAKE_HOME_DIRECTORY'): - hadHomeDir = True - - if (not hadHomeDir): - print('No CMAKE_HOME_DIRECTORY found in cache.') - sys.exit(1) - -def handleBasicMessage(proc, obj, debug): - if 'sendRaw' in obj: - data = obj['sendRaw'] - if debug: print("Sending raw:", data) - writeRawData(proc, data) - return True - elif 'send' in obj: - data = obj['send'] - if debug: print("Sending:", json.dumps(data)) - writePayload(proc, data) - return True - elif 'recv' in obj: - data = obj['recv'] - if debug: print("Waiting for:", json.dumps(data)) - waitForMessage(proc, data) - return True - elif 'message' in obj: - print("MESSAGE:", obj["message"]) - sys.stdout.flush() - return True - return False - -def shutdownProc(proc): - # Tell the server to exit. - proc.inPipe.close() - proc.outPipe.close() - - # Wait for the server to exit. - # If this version of python supports it, terminate the server after a timeout. - try: - proc.wait(timeout=5) - except TypeError: - proc.wait() - except: - proc.terminate() - raise - - print('cmake-server exited: %d' % proc.returncode) - sys.exit(proc.returncode) diff --git a/Tests/Server/empty.cpp b/Tests/Server/empty.cpp deleted file mode 100644 index 766b775..0000000 --- a/Tests/Server/empty.cpp +++ /dev/null @@ -1,5 +0,0 @@ - -int main() -{ - return 0; -} diff --git a/Tests/Server/server-test.py b/Tests/Server/server-test.py deleted file mode 100644 index 701c6e9..0000000 --- a/Tests/Server/server-test.py +++ /dev/null @@ -1,105 +0,0 @@ -from __future__ import print_function -import sys, cmakelib, json, os, shutil - -debug = True - -cmakeCommand = sys.argv[1] -testFile = sys.argv[2] -sourceDir = sys.argv[3] -buildDir = sys.argv[4] + "/" + os.path.splitext(os.path.basename(testFile))[0] -cmakeGenerator = sys.argv[5] - -print("Server Test:", testFile, - "\n-- SourceDir:", sourceDir, - "\n-- BuildDir:", buildDir, - "\n-- Generator:", cmakeGenerator) - -if os.path.exists(buildDir): - shutil.rmtree(buildDir) - -cmakelib.filterBase = sourceDir - -with open(testFile) as f: - testData = json.loads(f.read()) - -for communicationMethod in cmakelib.communicationMethods: - proc = cmakelib.initServerProc(cmakeCommand, communicationMethod) - if proc is None: - continue - - for obj in testData: - if cmakelib.handleBasicMessage(proc, obj, debug): - pass - elif 'reply' in obj: - data = obj['reply'] - if debug: print("Waiting for reply:", json.dumps(data)) - originalType = "" - cookie = "" - skipProgress = False; - if 'cookie' in data: cookie = data['cookie'] - if 'type' in data: originalType = data['type'] - if 'skipProgress' in data: skipProgress = data['skipProgress'] - cmakelib.waitForReply(proc, originalType, cookie, skipProgress) - elif 'error' in obj: - data = obj['error'] - if debug: print("Waiting for error:", json.dumps(data)) - originalType = "" - cookie = "" - message = "" - if 'cookie' in data: cookie = data['cookie'] - if 'type' in data: originalType = data['type'] - if 'message' in data: message = data['message'] - cmakelib.waitForError(proc, originalType, cookie, message) - elif 'progress' in obj: - data = obj['progress'] - if debug: print("Waiting for progress:", json.dumps(data)) - originalType = '' - cookie = "" - current = 0 - message = "" - if 'cookie' in data: cookie = data['cookie'] - if 'type' in data: originalType = data['type'] - if 'current' in data: current = data['current'] - if 'message' in data: message = data['message'] - cmakelib.waitForProgress(proc, originalType, cookie, current, message) - elif 'handshake' in obj: - data = obj['handshake'] - if debug: print("Doing handshake:", json.dumps(data)) - major = -1 - minor = -1 - generator = cmakeGenerator - extraGenerator = '' - sourceDirectory = sourceDir - buildDirectory = buildDir - if 'major' in data: major = data['major'] - if 'minor' in data: minor = data['minor'] - if 'buildDirectory' in data: buildDirectory = data['buildDirectory'] - if 'sourceDirectory' in data: sourceDirectory = data['sourceDirectory'] - if 'generator' in data: generator = data['generator'] - if 'extraGenerator' in data: extraGenerator = data['extraGenerator'] - - if not os.path.isabs(buildDirectory): - buildDirectory = buildDir + "/" + buildDirectory - if sourceDirectory != '' and not os.path.isabs(sourceDirectory): - sourceDirectory = sourceDir + "/" + sourceDirectory - cmakelib.handshake(proc, major, minor, sourceDirectory, buildDirectory, - generator, extraGenerator) - elif 'validateGlobalSettings' in obj: - data = obj['validateGlobalSettings'] - if not 'buildDirectory' in data: data['buildDirectory'] = buildDir - if not 'sourceDirectory' in data: data['sourceDirectory'] = sourceDir - if not 'generator' in data: data['generator'] = cmakeGenerator - if not 'extraGenerator' in data: data['extraGenerator'] = '' - cmakelib.validateGlobalSettings(proc, cmakeCommand, data) - elif 'validateCache' in obj: - data = obj['validateCache'] - if not 'isEmpty' in data: data['isEmpty'] = false - cmakelib.validateCache(proc, data) - elif 'reconnect' in obj: - cmakelib.exitProc(proc) - proc = cmakelib.initServerProc(cmakeCommand, communicationMethod) - else: - print("Unknown command:", json.dumps(obj)) - sys.exit(2) - cmakelib.shutdownProc(proc) - print("Completed") diff --git a/Tests/Server/tc_buildsystem1.json b/Tests/Server/tc_buildsystem1.json deleted file mode 100644 index 08831b7..0000000 --- a/Tests/Server/tc_buildsystem1.json +++ /dev/null @@ -1,27 +0,0 @@ -[ -{ "message": "Testing globalSettings" }, - -{ "handshake": {"major": 1, "sourceDirectory":"buildsystem1","buildDirectory":"buildsystem1"} }, - -{ "message": "Configure:" }, -{ "send": { "type": "configure", "cookie":"CONFIG" } }, -{ "reply": { "type": "configure", "cookie":"CONFIG", "skipProgress":true } }, - -{ "message": "Compute:" }, -{ "send": { "type": "compute", "cookie":"COMPUTE" } }, -{ "reply": { "type": "compute", "cookie":"COMPUTE", "skipProgress":true } }, - -{ "message": "Codemodel:" }, -{ "send": { "type": "codemodel", "cookie":"CODEMODEL" } }, -{ "reply": { "type": "codemodel", "cookie":"CODEMODEL" } }, - -{ "message": "CMake Inputs:"}, -{ "send": { "type": "cmakeInputs", "cookie":"INPUTS" } }, -{ "reply": { "type": "cmakeInputs", "cookie":"INPUTS" } }, - -{ "message": "Cache:"}, -{ "send": { "type": "cache", "cookie":"CACHE" } }, -{ "reply": { "type": "cache", "cookie":"CACHE" } }, - -{ "message": "Everything ok." } -] diff --git a/Tests/Server/tc_cache.json b/Tests/Server/tc_cache.json deleted file mode 100644 index 74af6d9..0000000 --- a/Tests/Server/tc_cache.json +++ /dev/null @@ -1,24 +0,0 @@ -[ -{ "message": "Testing cache" }, - -{ "message": "Cache after first handshake is empty:" }, -{ "handshake": {"major": 1, "sourceDirectory": "buildsystem1", "buildDirectory": "buildsystem1"} }, -{ "send": { "type": "cache" } }, -{ "validateCache": { "isEmpty": true } }, - -{ "message": "Cache after configure is populated:" }, -{ "send": { "type": "configure" } }, -{ "reply": { "type": "configure", "skipProgress":true } }, -{ "send": { "type": "cache" } }, -{ "validateCache": { "isEmpty": false } }, - -{ "message": "Handshake for existing cache requires buildDirectory only:" }, -{ "reconnect": {} }, -{ "handshake": {"major": 1, "sourceDirectory": "", "buildDirectory": "buildsystem1"} }, - -{ "message": "Cache after reconnect is again populated:" }, -{ "send": { "type": "cache" } }, -{ "validateCache": { "isEmpty": false } }, - -{ "message": "Everything ok." } -] diff --git a/Tests/Server/tc_globalSettings.json b/Tests/Server/tc_globalSettings.json deleted file mode 100644 index d72fb41..0000000 --- a/Tests/Server/tc_globalSettings.json +++ /dev/null @@ -1,140 +0,0 @@ -[ -{ "message": "Testing globalSettings" }, - -{ "handshake": {"major": 1} }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - - - -{ "message": "Change settings:" }, - -{ "send": { "type": "setGlobalSettings", "warnUnused": true } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "warnUnused": false } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "debugOutput": true } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": true, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "debugOutput": false } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "warnUninitialized": true } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": true, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "warnUninitialized": false } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "traceExpand": true } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": true, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "traceExpand": false } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - - - -{ "send": { "type": "setGlobalSettings", "trace": true } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": true, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "trace": false } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "warnUnusedCli": false } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": false, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "warnUnusedCli": true } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "checkSystemVars": true } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": true } }, - -{ "send": { "type": "setGlobalSettings", "checkSystemVars": false } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": false, "debugOutput": false, "warnUninitialized": false, "traceExpand": false, "trace": false, "warnUnusedCli": true, "checkSystemVars": false } }, - -{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, - -{ "message": "Ignore unknown/readonly" }, - -{ "send": { "type": "setGlobalSettings", "unknownKey": "unknownValue", "extraGenerator": "XXX", "generator": "YYY", "sourceDirectory": "/tmp/source", "buildDirectory": "/tmp/build" } }, -{ "reply": { "type": "setGlobalSettings" } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, - -{ "message": "Error paths:" }, - -{ "send": { "type": "setGlobalSettings", "debugOutput": true, "warnUnused": 1 } }, -{ "error": { "type": "setGlobalSettings", "message": "\"warnUnused\" must be unset or a bool value." } }, - -{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": 1 } }, -{ "error": { "type": "setGlobalSettings", "message": "\"debugOutput\" must be unset or a bool value." } }, - -{ "send": { "type": "setGlobalSettings", "warnUninitialized": 1, "warnUnused": true, "debugOutput": true } }, -{ "error": { "type": "setGlobalSettings", "message": "\"warnUninitialized\" must be unset or a bool value." } }, - -{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "traceExpand": 1 } }, -{ "error": { "type": "setGlobalSettings", "message": "\"traceExpand\" must be unset or a bool value." } }, - -{ "send": { "type": "setGlobalSettings", "debugOutput": true, "trace": 1, "warnUnused": true } }, -{ "error": { "type": "setGlobalSettings", "message": "\"trace\" must be unset or a bool value." } }, - -{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "warnUnusedCli": 1.0 } }, -{ "error": { "type": "setGlobalSettings", "message": "\"warnUnusedCli\" must be unset or a bool value." } }, - -{ "send": { "type": "setGlobalSettings", "warnUnused": true, "debugOutput": true, "checkSystemVars": "some string" } }, -{ "error": { "type": "setGlobalSettings", "message": "\"checkSystemVars\" must be unset or a bool value." } }, - -{ "send": { "type": "globalSettings"} }, -{ "validateGlobalSettings": { "warnUnused": true, "debugOutput": true, "warnUninitialized": true, "traceExpand": true, "trace": true, "warnUnusedCli": false, "checkSystemVars": true } }, - -{ "message": "Everything ok." } -] diff --git a/Tests/Server/tc_handshake.json b/Tests/Server/tc_handshake.json deleted file mode 100644 index 4bb7fa7..0000000 --- a/Tests/Server/tc_handshake.json +++ /dev/null @@ -1,75 +0,0 @@ -[ -{ "message": "Testing basic message handling:" }, - -{ "sendRaw": "Sometext"}, -{ "recv": {"cookie":"","errorMessage":"Failed to parse JSON input.","inReplyTo":"","type":"error"} }, - -{ "message": "Testing invalid json input"}, -{ "send": { "test": "sometext" } }, -{ "recv": {"cookie":"","errorMessage":"No type given in request.","inReplyTo":"","type":"error"} }, - -{ "send": {"test": "sometext","cookie":"monster"} }, -{ "recv": {"cookie":"monster","errorMessage":"No type given in request.","inReplyTo":"","type":"error"} }, - -{ "message": "Testing commands before handshake" }, -{ "send": {"type": "cache","cookie":"monster"} }, -{ "recv": {"cookie":"monster","errorMessage":"Waiting for type \"handshake\".","inReplyTo":"cache","type":"error"} }, - -{ "message": "Testing handshake" }, -{ "send": {"type": "sometype","cookie":"monster2"} }, -{ "recv": {"cookie":"monster2","errorMessage":"Waiting for type \"handshake\".","inReplyTo":"sometype","type":"error"} }, - -{ "send": {"type": "handshake"} }, -{ "recv": {"cookie":"","errorMessage":"\"protocolVersion\" is required for \"handshake\".","inReplyTo":"handshake","type":"error"} }, - -{ "send": {"type": "handshake","foo":"bar"} }, -{ "recv": {"cookie":"","errorMessage":"\"protocolVersion\" is required for \"handshake\".","inReplyTo":"handshake","type":"error"} }, - -{ "send": {"type": "handshake","protocolVersion":"bar"} }, -{ "recv": {"cookie":"","errorMessage":"\"protocolVersion\" must be a JSON object.","inReplyTo":"handshake","type":"error"} }, - -{ "send": {"type": "handshake","protocolVersion":{}} }, -{ "recv": {"cookie":"","errorMessage":"\"major\" must be set and an integer.","inReplyTo":"handshake","type":"error"} }, - -{ "send": {"type": "handshake","protocolVersion":{"major":"foo"}} }, -{ "recv": {"cookie":"","errorMessage":"\"major\" must be set and an integer.","inReplyTo":"handshake","type":"error"} }, - -{ "send": {"type": "handshake","protocolVersion":{"major":1, "minor":"foo"}} }, -{ "recv": {"cookie":"","errorMessage":"\"minor\" must be unset or an integer.","inReplyTo":"handshake","type":"error"} }, - -{ "send": {"type": "handshake","protocolVersion":{"major":-1, "minor":-1}} }, -{ "recv": {"cookie":"","errorMessage":"\"major\" must be >= 0.","inReplyTo":"handshake","type":"error"} }, - -{ "send": {"type": "handshake","protocolVersion":{"major":10, "minor":-1}} }, -{ "recv": {"cookie":"","errorMessage":"\"minor\" must be >= 0 when set.","inReplyTo":"handshake","type":"error"} }, - -{ "send": {"type": "handshake","protocolVersion":{"major":10000}} }, -{ "recv": {"cookie":"","errorMessage":"Protocol version not supported.","inReplyTo":"handshake","type":"error"} }, - -{ "send": {"type": "handshake","protocolVersion":{"major":1, "minor":10000}} }, -{ "recv": {"cookie":"","errorMessage":"Protocol version not supported.","inReplyTo":"handshake","type":"error"} }, - -{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1}} }, -{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: \"buildDirectory\" is missing."} }, - -{ "message": "Testing protocol version specific options (1.0):" }, -{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":"/tmp/src"} }, -{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: \"buildDirectory\" is missing."} }, - -{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":"/tmp/src","buildDirectory":"/tmp/build"} }, -{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: \"sourceDirectory\" is not a directory."} }, - -{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":".","buildDirectory":"/tmp/build","extraGenerator":"CodeBlocks"} }, -{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: \"generator\" is unset but required."} }, - -{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":".","buildDirectory":"/tmp/build","generator":"XXXX","extraGenerator":"CodeBlocks"} }, -{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: Generator \"XXXX\" not supported."} }, - -{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":".","buildDirectory":"/tmp/build","generator":"Ninja","extraGenerator":"XXXX"} }, -{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"error","errorMessage":"Failed to activate protocol version: The combination of generator \"Ninja\" and extra generator \"XXXX\" is not supported."} }, - -{ "send": {"cookie":"zimtstern","type": "handshake","protocolVersion":{"major":1},"sourceDirectory":".","buildDirectory":"/tmp/build","generator":"Ninja","extraGenerator":"CodeBlocks"} }, -{ "recv": {"cookie":"zimtstern","inReplyTo":"handshake","type":"reply"} }, - -{ "message": "Everything ok." } -] |