cmServer.cxx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmServer.h"
  4. #include "cmServerConnection.h"
  5. #include "cmServerDictionary.h"
  6. #include "cmServerProtocol.h"
  7. #include "cmSystemTools.h"
  8. #include "cm_jsoncpp_reader.h"
  9. #include "cm_jsoncpp_writer.h"
  10. #include "cmake.h"
  11. #include "cmsys/FStream.hxx"
  12. #include <algorithm>
  13. #include <cassert>
  14. #include <cstdint>
  15. #include <utility>
  16. class cmServer::DebugInfo
  17. {
  18. public:
  19. DebugInfo()
  20. : StartTime(uv_hrtime())
  21. {
  22. }
  23. bool PrintStatistics = false;
  24. std::string OutputFile;
  25. uint64_t StartTime;
  26. };
  27. cmServer::cmServer(cmServerConnection* conn, bool supportExperimental)
  28. : Connection(conn)
  29. , SupportExperimental(supportExperimental)
  30. {
  31. this->Connection->SetServer(this);
  32. // Register supported protocols:
  33. this->RegisterProtocol(new cmServerProtocol1);
  34. }
  35. cmServer::~cmServer()
  36. {
  37. if (!this->Protocol) { // Server was never fully started!
  38. return;
  39. }
  40. for (cmServerProtocol* p : this->SupportedProtocols) {
  41. delete p;
  42. }
  43. delete this->Connection;
  44. }
  45. void cmServer::PopOne()
  46. {
  47. if (this->Queue.empty()) {
  48. return;
  49. }
  50. Json::Reader reader;
  51. Json::Value value;
  52. const std::string input = this->Queue.front();
  53. this->Queue.erase(this->Queue.begin());
  54. if (!reader.parse(input, value)) {
  55. this->WriteParseError("Failed to parse JSON input.");
  56. return;
  57. }
  58. std::unique_ptr<DebugInfo> debug;
  59. Json::Value debugValue = value["debug"];
  60. if (!debugValue.isNull()) {
  61. debug = std::make_unique<DebugInfo>();
  62. debug->OutputFile = debugValue["dumpToFile"].asString();
  63. debug->PrintStatistics = debugValue["showStats"].asBool();
  64. }
  65. const cmServerRequest request(this, value[kTYPE_KEY].asString(),
  66. value[kCOOKIE_KEY].asString(), value);
  67. if (request.Type == "") {
  68. cmServerResponse response(request);
  69. response.SetError("No type given in request.");
  70. this->WriteResponse(response, nullptr);
  71. return;
  72. }
  73. cmSystemTools::SetMessageCallback(reportMessage,
  74. const_cast<cmServerRequest*>(&request));
  75. if (this->Protocol) {
  76. this->Protocol->CMakeInstance()->SetProgressCallback(
  77. reportProgress, const_cast<cmServerRequest*>(&request));
  78. this->WriteResponse(this->Protocol->Process(request), debug.get());
  79. } else {
  80. this->WriteResponse(this->SetProtocolVersion(request), debug.get());
  81. }
  82. }
  83. void cmServer::RegisterProtocol(cmServerProtocol* protocol)
  84. {
  85. if (protocol->IsExperimental() && !this->SupportExperimental) {
  86. return;
  87. }
  88. auto version = protocol->ProtocolVersion();
  89. assert(version.first >= 0);
  90. assert(version.second >= 0);
  91. auto it = std::find_if(this->SupportedProtocols.begin(),
  92. this->SupportedProtocols.end(),
  93. [version](cmServerProtocol* p) {
  94. return p->ProtocolVersion() == version;
  95. });
  96. if (it == this->SupportedProtocols.end()) {
  97. this->SupportedProtocols.push_back(protocol);
  98. }
  99. }
  100. void cmServer::PrintHello() const
  101. {
  102. Json::Value hello = Json::objectValue;
  103. hello[kTYPE_KEY] = "hello";
  104. Json::Value& protocolVersions = hello[kSUPPORTED_PROTOCOL_VERSIONS] =
  105. Json::arrayValue;
  106. for (auto const& proto : this->SupportedProtocols) {
  107. auto version = proto->ProtocolVersion();
  108. Json::Value tmp = Json::objectValue;
  109. tmp[kMAJOR_KEY] = version.first;
  110. tmp[kMINOR_KEY] = version.second;
  111. if (proto->IsExperimental()) {
  112. tmp[kIS_EXPERIMENTAL_KEY] = true;
  113. }
  114. protocolVersions.append(tmp);
  115. }
  116. this->WriteJsonObject(hello, nullptr);
  117. }
  118. void cmServer::QueueRequest(const std::string& request)
  119. {
  120. this->Queue.push_back(request);
  121. this->PopOne();
  122. }
  123. void cmServer::reportProgress(const char* msg, float progress, void* data)
  124. {
  125. const cmServerRequest* request = static_cast<const cmServerRequest*>(data);
  126. assert(request);
  127. if (progress < 0.0f || progress > 1.0f) {
  128. request->ReportMessage(msg, "");
  129. } else {
  130. request->ReportProgress(0, static_cast<int>(progress * 1000), 1000, msg);
  131. }
  132. }
  133. void cmServer::reportMessage(const char* msg, const char* title,
  134. bool& /* cancel */, void* data)
  135. {
  136. const cmServerRequest* request = static_cast<const cmServerRequest*>(data);
  137. assert(request);
  138. assert(msg);
  139. std::string titleString;
  140. if (title) {
  141. titleString = title;
  142. }
  143. request->ReportMessage(std::string(msg), titleString);
  144. }
  145. cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request)
  146. {
  147. if (request.Type != kHANDSHAKE_TYPE) {
  148. return request.ReportError("Waiting for type \"" + kHANDSHAKE_TYPE +
  149. "\".");
  150. }
  151. Json::Value requestedProtocolVersion = request.Data[kPROTOCOL_VERSION_KEY];
  152. if (requestedProtocolVersion.isNull()) {
  153. return request.ReportError("\"" + kPROTOCOL_VERSION_KEY +
  154. "\" is required for \"" + kHANDSHAKE_TYPE +
  155. "\".");
  156. }
  157. if (!requestedProtocolVersion.isObject()) {
  158. return request.ReportError("\"" + kPROTOCOL_VERSION_KEY +
  159. "\" must be a JSON object.");
  160. }
  161. Json::Value majorValue = requestedProtocolVersion[kMAJOR_KEY];
  162. if (!majorValue.isInt()) {
  163. return request.ReportError("\"" + kMAJOR_KEY +
  164. "\" must be set and an integer.");
  165. }
  166. Json::Value minorValue = requestedProtocolVersion[kMINOR_KEY];
  167. if (!minorValue.isNull() && !minorValue.isInt()) {
  168. return request.ReportError("\"" + kMINOR_KEY +
  169. "\" must be unset or an integer.");
  170. }
  171. const int major = majorValue.asInt();
  172. const int minor = minorValue.isNull() ? -1 : minorValue.asInt();
  173. if (major < 0) {
  174. return request.ReportError("\"" + kMAJOR_KEY + "\" must be >= 0.");
  175. }
  176. if (!minorValue.isNull() && minor < 0) {
  177. return request.ReportError("\"" + kMINOR_KEY +
  178. "\" must be >= 0 when set.");
  179. }
  180. this->Protocol =
  181. this->FindMatchingProtocol(this->SupportedProtocols, major, minor);
  182. if (!this->Protocol) {
  183. return request.ReportError("Protocol version not supported.");
  184. }
  185. std::string errorMessage;
  186. if (!this->Protocol->Activate(this, request, &errorMessage)) {
  187. this->Protocol = CM_NULLPTR;
  188. return request.ReportError("Failed to activate protocol version: " +
  189. errorMessage);
  190. }
  191. return request.Reply(Json::objectValue);
  192. }
  193. bool cmServer::Serve(std::string* errorMessage)
  194. {
  195. if (this->SupportedProtocols.empty()) {
  196. *errorMessage =
  197. "No protocol versions defined. Maybe you need --experimental?";
  198. return false;
  199. }
  200. assert(!this->Protocol);
  201. return Connection->ProcessEvents(errorMessage);
  202. }
  203. cmFileMonitor* cmServer::FileMonitor() const
  204. {
  205. return Connection->FileMonitor();
  206. }
  207. void cmServer::WriteJsonObject(const Json::Value& jsonValue,
  208. const DebugInfo* debug) const
  209. {
  210. Json::FastWriter writer;
  211. auto beforeJson = uv_hrtime();
  212. std::string result = writer.write(jsonValue);
  213. if (debug) {
  214. Json::Value copy = jsonValue;
  215. if (debug->PrintStatistics) {
  216. Json::Value stats = Json::objectValue;
  217. auto endTime = uv_hrtime();
  218. stats["jsonSerialization"] = double(endTime - beforeJson) / 1000000.0;
  219. stats["totalTime"] = double(endTime - debug->StartTime) / 1000000.0;
  220. stats["size"] = static_cast<int>(result.size());
  221. if (!debug->OutputFile.empty()) {
  222. stats["dumpFile"] = debug->OutputFile;
  223. }
  224. copy["zzzDebug"] = stats;
  225. result = writer.write(copy); // Update result to include debug info
  226. }
  227. if (!debug->OutputFile.empty()) {
  228. cmsys::ofstream myfile(debug->OutputFile.c_str());
  229. myfile << result;
  230. }
  231. }
  232. Connection->WriteData(std::string("\n") + kSTART_MAGIC + std::string("\n") +
  233. result + kEND_MAGIC + std::string("\n"));
  234. }
  235. cmServerProtocol* cmServer::FindMatchingProtocol(
  236. const std::vector<cmServerProtocol*>& protocols, int major, int minor)
  237. {
  238. cmServerProtocol* bestMatch = nullptr;
  239. for (auto protocol : protocols) {
  240. auto version = protocol->ProtocolVersion();
  241. if (major != version.first) {
  242. continue;
  243. }
  244. if (minor == version.second) {
  245. return protocol;
  246. }
  247. if (!bestMatch || bestMatch->ProtocolVersion().second < version.second) {
  248. bestMatch = protocol;
  249. }
  250. }
  251. return minor < 0 ? bestMatch : nullptr;
  252. }
  253. void cmServer::WriteProgress(const cmServerRequest& request, int min,
  254. int current, int max,
  255. const std::string& message) const
  256. {
  257. assert(min <= current && current <= max);
  258. assert(message.length() != 0);
  259. Json::Value obj = Json::objectValue;
  260. obj[kTYPE_KEY] = kPROGRESS_TYPE;
  261. obj[kREPLY_TO_KEY] = request.Type;
  262. obj[kCOOKIE_KEY] = request.Cookie;
  263. obj[kPROGRESS_MESSAGE_KEY] = message;
  264. obj[kPROGRESS_MINIMUM_KEY] = min;
  265. obj[kPROGRESS_MAXIMUM_KEY] = max;
  266. obj[kPROGRESS_CURRENT_KEY] = current;
  267. this->WriteJsonObject(obj, nullptr);
  268. }
  269. void cmServer::WriteMessage(const cmServerRequest& request,
  270. const std::string& message,
  271. const std::string& title) const
  272. {
  273. if (message.empty()) {
  274. return;
  275. }
  276. Json::Value obj = Json::objectValue;
  277. obj[kTYPE_KEY] = kMESSAGE_TYPE;
  278. obj[kREPLY_TO_KEY] = request.Type;
  279. obj[kCOOKIE_KEY] = request.Cookie;
  280. obj[kMESSAGE_KEY] = message;
  281. if (!title.empty()) {
  282. obj[kTITLE_KEY] = title;
  283. }
  284. WriteJsonObject(obj, nullptr);
  285. }
  286. void cmServer::WriteParseError(const std::string& message) const
  287. {
  288. Json::Value obj = Json::objectValue;
  289. obj[kTYPE_KEY] = kERROR_TYPE;
  290. obj[kERROR_MESSAGE_KEY] = message;
  291. obj[kREPLY_TO_KEY] = "";
  292. obj[kCOOKIE_KEY] = "";
  293. this->WriteJsonObject(obj, nullptr);
  294. }
  295. void cmServer::WriteSignal(const std::string& name,
  296. const Json::Value& data) const
  297. {
  298. assert(data.isObject());
  299. Json::Value obj = data;
  300. obj[kTYPE_KEY] = kSIGNAL_TYPE;
  301. obj[kREPLY_TO_KEY] = "";
  302. obj[kCOOKIE_KEY] = "";
  303. obj[kNAME_KEY] = name;
  304. WriteJsonObject(obj, nullptr);
  305. }
  306. void cmServer::WriteResponse(const cmServerResponse& response,
  307. const DebugInfo* debug) const
  308. {
  309. assert(response.IsComplete());
  310. Json::Value obj = response.Data();
  311. obj[kCOOKIE_KEY] = response.Cookie;
  312. obj[kTYPE_KEY] = response.IsError() ? kERROR_TYPE : kREPLY_TYPE;
  313. obj[kREPLY_TO_KEY] = response.Type;
  314. if (response.IsError()) {
  315. obj[kERROR_MESSAGE_KEY] = response.ErrorMessage();
  316. }
  317. this->WriteJsonObject(obj, debug);
  318. }