Merge branch 'release-3.29'
[kiteware-cmake.git] / Source / cmFileAPI.cxx
blobd4a717586df3869c5bdca444a3a43484b4c43f56
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 "cmFileAPI.h"
5 #include <algorithm>
6 #include <cassert>
7 #include <chrono>
8 #include <ctime>
9 #include <iomanip>
10 #include <sstream>
11 #include <utility>
13 #include "cmsys/Directory.hxx"
14 #include "cmsys/FStream.hxx"
16 #include "cmCryptoHash.h"
17 #include "cmFileAPICMakeFiles.h"
18 #include "cmFileAPICache.h"
19 #include "cmFileAPICodemodel.h"
20 #include "cmFileAPIConfigureLog.h"
21 #include "cmFileAPIToolchains.h"
22 #include "cmGlobalGenerator.h"
23 #include "cmStringAlgorithms.h"
24 #include "cmSystemTools.h"
25 #include "cmTimestamp.h"
26 #include "cmake.h"
28 cmFileAPI::cmFileAPI(cmake* cm)
29 : CMakeInstance(cm)
31 this->APIv1 =
32 this->CMakeInstance->GetHomeOutputDirectory() + "/.cmake/api/v1";
34 Json::CharReaderBuilder rbuilder;
35 rbuilder["collectComments"] = false;
36 rbuilder["failIfExtra"] = true;
37 rbuilder["rejectDupKeys"] = false;
38 rbuilder["strictRoot"] = true;
39 this->JsonReader =
40 std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
42 Json::StreamWriterBuilder wbuilder;
43 wbuilder["indentation"] = "\t";
44 this->JsonWriter =
45 std::unique_ptr<Json::StreamWriter>(wbuilder.newStreamWriter());
48 void cmFileAPI::ReadQueries()
50 std::string const query_dir = this->APIv1 + "/query";
51 this->QueryExists = cmSystemTools::FileIsDirectory(query_dir);
52 if (!this->QueryExists) {
53 return;
56 // Load queries at the top level.
57 std::vector<std::string> queries = cmFileAPI::LoadDir(query_dir);
59 // Read the queries and save for later.
60 for (std::string& query : queries) {
61 if (cmHasLiteralPrefix(query, "client-")) {
62 this->ReadClient(query);
63 } else if (!cmFileAPI::ReadQuery(query, this->TopQuery.Known)) {
64 this->TopQuery.Unknown.push_back(std::move(query));
69 std::vector<unsigned long> cmFileAPI::GetConfigureLogVersions()
71 std::vector<unsigned long> versions;
72 auto getConfigureLogVersions = [&versions](Query const& q) {
73 for (Object const& o : q.Known) {
74 if (o.Kind == ObjectKind::ConfigureLog) {
75 versions.emplace_back(o.Version);
79 getConfigureLogVersions(this->TopQuery);
80 for (auto const& client : this->ClientQueries) {
81 getConfigureLogVersions(client.second.DirQuery);
83 std::sort(versions.begin(), versions.end());
84 versions.erase(std::unique(versions.begin(), versions.end()),
85 versions.end());
86 return versions;
89 void cmFileAPI::WriteReplies()
91 if (this->QueryExists) {
92 cmSystemTools::MakeDirectory(this->APIv1 + "/reply");
93 this->WriteJsonFile(this->BuildReplyIndex(), "index", ComputeSuffixTime);
96 this->RemoveOldReplyFiles();
99 std::vector<std::string> cmFileAPI::LoadDir(std::string const& dir)
101 std::vector<std::string> files;
102 cmsys::Directory d;
103 d.Load(dir);
104 for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) {
105 std::string f = d.GetFile(i);
106 if (f != "." && f != "..") {
107 files.push_back(std::move(f));
110 std::sort(files.begin(), files.end());
111 return files;
114 void cmFileAPI::RemoveOldReplyFiles()
116 std::string const reply_dir = this->APIv1 + "/reply";
117 std::vector<std::string> files = this->LoadDir(reply_dir);
118 for (std::string const& f : files) {
119 if (this->ReplyFiles.find(f) == this->ReplyFiles.end()) {
120 std::string file = cmStrCat(reply_dir, "/", f);
121 cmSystemTools::RemoveFile(file);
126 bool cmFileAPI::ReadJsonFile(std::string const& file, Json::Value& value,
127 std::string& error)
129 std::vector<char> content;
131 cmsys::ifstream fin;
132 if (!cmSystemTools::FileIsDirectory(file)) {
133 fin.open(file.c_str(), std::ios::binary);
135 auto finEnd = fin.rdbuf()->pubseekoff(0, std::ios::end);
136 if (finEnd > 0) {
137 size_t finSize = finEnd;
138 try {
139 // Allocate a buffer to read the whole file.
140 content.resize(finSize);
142 // Now read the file from the beginning.
143 fin.seekg(0, std::ios::beg);
144 fin.read(content.data(), finSize);
145 } catch (...) {
146 fin.setstate(std::ios::failbit);
149 fin.close();
150 if (!fin) {
151 value = Json::Value();
152 error = "failed to read from file";
153 return false;
156 // Parse our buffer as json.
157 if (!this->JsonReader->parse(content.data(), content.data() + content.size(),
158 &value, &error)) {
159 value = Json::Value();
160 return false;
163 return true;
166 std::string cmFileAPI::WriteJsonFile(
167 Json::Value const& value, std::string const& prefix,
168 std::string (*computeSuffix)(std::string const&))
170 std::string fileName;
172 // Write the json file with a temporary name.
173 std::string const& tmpFile = this->APIv1 + "/tmp.json";
174 cmsys::ofstream ftmp(tmpFile.c_str());
175 this->JsonWriter->write(value, &ftmp);
176 ftmp << "\n";
177 ftmp.close();
178 if (!ftmp) {
179 cmSystemTools::RemoveFile(tmpFile);
180 return fileName;
183 // Compute the final name for the file.
184 fileName = prefix + "-" + computeSuffix(tmpFile) + ".json";
186 // Create the destination.
187 std::string file = this->APIv1 + "/reply";
188 cmSystemTools::MakeDirectory(file);
189 file += "/";
190 file += fileName;
192 // If the final name already exists then assume it has proper content.
193 // Otherwise, atomically place the reply file at its final name
194 if (cmSystemTools::FileExists(file, true) ||
195 !cmSystemTools::RenameFile(tmpFile, file)) {
196 cmSystemTools::RemoveFile(tmpFile);
199 // Record this among files we have just written.
200 this->ReplyFiles.insert(fileName);
202 return fileName;
205 Json::Value cmFileAPI::MaybeJsonFile(Json::Value in, std::string const& prefix)
207 Json::Value out;
208 if (in.isObject() || in.isArray()) {
209 out = Json::objectValue;
210 out["jsonFile"] = this->WriteJsonFile(in, prefix);
211 } else {
212 out = std::move(in);
214 return out;
217 std::string cmFileAPI::ComputeSuffixHash(std::string const& file)
219 cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_256);
220 std::string hash = hasher.HashFile(file);
221 hash.resize(20, '0');
222 return hash;
225 std::string cmFileAPI::ComputeSuffixTime(std::string const&)
227 std::chrono::milliseconds ms =
228 std::chrono::duration_cast<std::chrono::milliseconds>(
229 std::chrono::system_clock::now().time_since_epoch());
230 std::chrono::seconds s =
231 std::chrono::duration_cast<std::chrono::seconds>(ms);
233 std::time_t ts = s.count();
234 std::size_t tms = ms.count() % 1000;
236 cmTimestamp cmts;
237 std::ostringstream ss;
238 ss << cmts.CreateTimestampFromTimeT(ts, "%Y-%m-%dT%H-%M-%S", true) << '-'
239 << std::setfill('0') << std::setw(4) << tms;
240 return ss.str();
243 bool cmFileAPI::ReadQuery(std::string const& query,
244 std::vector<Object>& objects)
246 // Parse the "<kind>-" syntax.
247 std::string::size_type sep_pos = query.find('-');
248 if (sep_pos == std::string::npos) {
249 return false;
251 std::string kindName = query.substr(0, sep_pos);
252 std::string verStr = query.substr(sep_pos + 1);
253 if (kindName == ObjectKindName(ObjectKind::CodeModel)) {
254 Object o;
255 o.Kind = ObjectKind::CodeModel;
256 if (verStr == "v2") {
257 o.Version = 2;
258 } else {
259 return false;
261 objects.push_back(o);
262 return true;
264 if (kindName == ObjectKindName(ObjectKind::ConfigureLog)) {
265 Object o;
266 o.Kind = ObjectKind::ConfigureLog;
267 if (verStr == "v1") {
268 o.Version = 1;
269 } else {
270 return false;
272 objects.push_back(o);
273 return true;
275 if (kindName == ObjectKindName(ObjectKind::Cache)) {
276 Object o;
277 o.Kind = ObjectKind::Cache;
278 if (verStr == "v2") {
279 o.Version = 2;
280 } else {
281 return false;
283 objects.push_back(o);
284 return true;
286 if (kindName == ObjectKindName(ObjectKind::CMakeFiles)) {
287 Object o;
288 o.Kind = ObjectKind::CMakeFiles;
289 if (verStr == "v1") {
290 o.Version = 1;
291 } else {
292 return false;
294 objects.push_back(o);
295 return true;
297 if (kindName == ObjectKindName(ObjectKind::Toolchains)) {
298 Object o;
299 o.Kind = ObjectKind::Toolchains;
300 if (verStr == "v1") {
301 o.Version = 1;
302 } else {
303 return false;
305 objects.push_back(o);
306 return true;
308 if (kindName == ObjectKindName(ObjectKind::InternalTest)) {
309 Object o;
310 o.Kind = ObjectKind::InternalTest;
311 if (verStr == "v1") {
312 o.Version = 1;
313 } else if (verStr == "v2") {
314 o.Version = 2;
315 } else {
316 return false;
318 objects.push_back(o);
319 return true;
321 return false;
324 void cmFileAPI::ReadClient(std::string const& client)
326 // Load queries for the client.
327 std::string clientDir = this->APIv1 + "/query/" + client;
328 std::vector<std::string> queries = this->LoadDir(clientDir);
330 // Read the queries and save for later.
331 ClientQuery& clientQuery = this->ClientQueries[client];
332 for (std::string& query : queries) {
333 if (query == "query.json") {
334 clientQuery.HaveQueryJson = true;
335 this->ReadClientQuery(client, clientQuery.QueryJson);
336 } else if (!this->ReadQuery(query, clientQuery.DirQuery.Known)) {
337 clientQuery.DirQuery.Unknown.push_back(std::move(query));
342 void cmFileAPI::ReadClientQuery(std::string const& client, ClientQueryJson& q)
344 // Read the query.json file.
345 std::string queryFile = this->APIv1 + "/query/" + client + "/query.json";
346 Json::Value query;
347 if (!this->ReadJsonFile(queryFile, query, q.Error)) {
348 return;
350 if (!query.isObject()) {
351 q.Error = "query root is not an object";
352 return;
355 Json::Value const& clientValue = query["client"];
356 if (!clientValue.isNull()) {
357 q.ClientValue = clientValue;
359 q.RequestsValue = std::move(query["requests"]);
360 q.Requests = this->BuildClientRequests(q.RequestsValue);
363 Json::Value cmFileAPI::BuildReplyIndex()
365 Json::Value index(Json::objectValue);
367 // Report information about this version of CMake.
368 index["cmake"] = this->BuildCMake();
370 // Reply to all queries that we loaded.
371 Json::Value& reply = index["reply"] = this->BuildReply(this->TopQuery);
372 for (auto const& client : this->ClientQueries) {
373 std::string const& clientName = client.first;
374 ClientQuery const& clientQuery = client.second;
375 reply[clientName] = this->BuildClientReply(clientQuery);
378 // Move our index of generated objects into its field.
379 Json::Value& objects = index["objects"] = Json::arrayValue;
380 for (auto& entry : this->ReplyIndexObjects) {
381 objects.append(std::move(entry.second)); // NOLINT(*)
384 return index;
387 Json::Value cmFileAPI::BuildCMake()
389 Json::Value cmake = Json::objectValue;
390 cmake["version"] = this->CMakeInstance->ReportVersionJson();
391 Json::Value& cmake_paths = cmake["paths"] = Json::objectValue;
392 cmake_paths["cmake"] = cmSystemTools::GetCMakeCommand();
393 cmake_paths["ctest"] = cmSystemTools::GetCTestCommand();
394 cmake_paths["cpack"] = cmSystemTools::GetCPackCommand();
395 cmake_paths["root"] = cmSystemTools::GetCMakeRoot();
396 cmake["generator"] = this->CMakeInstance->GetGlobalGenerator()->GetJson();
397 return cmake;
400 Json::Value cmFileAPI::BuildReply(Query const& q)
402 Json::Value reply = Json::objectValue;
403 for (Object const& o : q.Known) {
404 std::string const& name = ObjectName(o);
405 reply[name] = this->AddReplyIndexObject(o);
408 for (std::string const& name : q.Unknown) {
409 reply[name] = cmFileAPI::BuildReplyError("unknown query file");
411 return reply;
414 Json::Value cmFileAPI::BuildReplyError(std::string const& error)
416 Json::Value e = Json::objectValue;
417 e["error"] = error;
418 return e;
421 Json::Value const& cmFileAPI::AddReplyIndexObject(Object const& o)
423 Json::Value& indexEntry = this->ReplyIndexObjects[o];
424 if (!indexEntry.isNull()) {
425 // The reply object has already been generated.
426 return indexEntry;
429 // Generate this reply object.
430 Json::Value const& object = this->BuildObject(o);
431 assert(object.isObject());
433 // Populate this index entry.
434 indexEntry = Json::objectValue;
435 indexEntry["kind"] = object["kind"];
436 indexEntry["version"] = object["version"];
437 indexEntry["jsonFile"] = this->WriteJsonFile(object, ObjectName(o));
438 return indexEntry;
441 const char* cmFileAPI::ObjectKindName(ObjectKind kind)
443 // Keep in sync with ObjectKind enum.
444 static const char* objectKindNames[] = {
445 "codemodel", //
446 "configureLog", //
447 "cache", //
448 "cmakeFiles", //
449 "toolchains", //
450 "__test" //
452 return objectKindNames[static_cast<size_t>(kind)];
455 std::string cmFileAPI::ObjectName(Object const& o)
457 std::string name = cmStrCat(ObjectKindName(o.Kind), "-v", o.Version);
458 return name;
461 Json::Value cmFileAPI::BuildVersion(unsigned int major, unsigned int minor)
463 Json::Value version;
464 version["major"] = major;
465 version["minor"] = minor;
466 return version;
469 Json::Value cmFileAPI::BuildObject(Object const& object)
471 Json::Value value;
473 switch (object.Kind) {
474 case ObjectKind::CodeModel:
475 value = this->BuildCodeModel(object);
476 break;
477 case ObjectKind::ConfigureLog:
478 value = this->BuildConfigureLog(object);
479 break;
480 case ObjectKind::Cache:
481 value = this->BuildCache(object);
482 break;
483 case ObjectKind::CMakeFiles:
484 value = this->BuildCMakeFiles(object);
485 break;
486 case ObjectKind::Toolchains:
487 value = this->BuildToolchains(object);
488 break;
489 case ObjectKind::InternalTest:
490 value = this->BuildInternalTest(object);
491 break;
494 return value;
497 cmFileAPI::ClientRequests cmFileAPI::BuildClientRequests(
498 Json::Value const& requests)
500 ClientRequests result;
501 if (requests.isNull()) {
502 result.Error = "'requests' member missing";
503 return result;
505 if (!requests.isArray()) {
506 result.Error = "'requests' member is not an array";
507 return result;
510 result.reserve(requests.size());
511 for (Json::Value const& request : requests) {
512 result.emplace_back(this->BuildClientRequest(request));
515 return result;
518 cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
519 Json::Value const& request)
521 ClientRequest r;
523 if (!request.isObject()) {
524 r.Error = "request is not an object";
525 return r;
528 Json::Value const& kind = request["kind"];
529 if (kind.isNull()) {
530 r.Error = "'kind' member missing";
531 return r;
533 if (!kind.isString()) {
534 r.Error = "'kind' member is not a string";
535 return r;
537 std::string const& kindName = kind.asString();
539 if (kindName == this->ObjectKindName(ObjectKind::CodeModel)) {
540 r.Kind = ObjectKind::CodeModel;
541 } else if (kindName == this->ObjectKindName(ObjectKind::ConfigureLog)) {
542 r.Kind = ObjectKind::ConfigureLog;
543 } else if (kindName == this->ObjectKindName(ObjectKind::Cache)) {
544 r.Kind = ObjectKind::Cache;
545 } else if (kindName == this->ObjectKindName(ObjectKind::CMakeFiles)) {
546 r.Kind = ObjectKind::CMakeFiles;
547 } else if (kindName == this->ObjectKindName(ObjectKind::Toolchains)) {
548 r.Kind = ObjectKind::Toolchains;
549 } else if (kindName == this->ObjectKindName(ObjectKind::InternalTest)) {
550 r.Kind = ObjectKind::InternalTest;
551 } else {
552 r.Error = "unknown request kind '" + kindName + "'";
553 return r;
556 Json::Value const& version = request["version"];
557 if (version.isNull()) {
558 r.Error = "'version' member missing";
559 return r;
561 std::vector<RequestVersion> versions;
562 if (!cmFileAPI::ReadRequestVersions(version, versions, r.Error)) {
563 return r;
566 switch (r.Kind) {
567 case ObjectKind::CodeModel:
568 this->BuildClientRequestCodeModel(r, versions);
569 break;
570 case ObjectKind::ConfigureLog:
571 this->BuildClientRequestConfigureLog(r, versions);
572 break;
573 case ObjectKind::Cache:
574 this->BuildClientRequestCache(r, versions);
575 break;
576 case ObjectKind::CMakeFiles:
577 this->BuildClientRequestCMakeFiles(r, versions);
578 break;
579 case ObjectKind::Toolchains:
580 this->BuildClientRequestToolchains(r, versions);
581 break;
582 case ObjectKind::InternalTest:
583 this->BuildClientRequestInternalTest(r, versions);
584 break;
587 return r;
590 Json::Value cmFileAPI::BuildClientReply(ClientQuery const& q)
592 Json::Value reply = this->BuildReply(q.DirQuery);
594 if (!q.HaveQueryJson) {
595 return reply;
598 Json::Value& reply_query_json = reply["query.json"];
599 ClientQueryJson const& qj = q.QueryJson;
601 if (!qj.Error.empty()) {
602 reply_query_json = this->BuildReplyError(qj.Error);
603 return reply;
606 if (!qj.ClientValue.isNull()) {
607 reply_query_json["client"] = qj.ClientValue;
610 if (!qj.RequestsValue.isNull()) {
611 reply_query_json["requests"] = qj.RequestsValue;
614 reply_query_json["responses"] = this->BuildClientReplyResponses(qj.Requests);
616 return reply;
619 Json::Value cmFileAPI::BuildClientReplyResponses(
620 ClientRequests const& requests)
622 Json::Value responses;
624 if (!requests.Error.empty()) {
625 responses = this->BuildReplyError(requests.Error);
626 return responses;
629 responses = Json::arrayValue;
630 for (ClientRequest const& request : requests) {
631 responses.append(this->BuildClientReplyResponse(request));
634 return responses;
637 Json::Value cmFileAPI::BuildClientReplyResponse(ClientRequest const& request)
639 Json::Value response;
640 if (!request.Error.empty()) {
641 response = this->BuildReplyError(request.Error);
642 return response;
644 response = this->AddReplyIndexObject(request);
645 return response;
648 bool cmFileAPI::ReadRequestVersions(Json::Value const& version,
649 std::vector<RequestVersion>& versions,
650 std::string& error)
652 if (version.isArray()) {
653 for (Json::Value const& v : version) {
654 if (!ReadRequestVersion(v, /*inArray=*/true, versions, error)) {
655 return false;
658 } else {
659 if (!ReadRequestVersion(version, /*inArray=*/false, versions, error)) {
660 return false;
663 return true;
666 bool cmFileAPI::ReadRequestVersion(Json::Value const& version, bool inArray,
667 std::vector<RequestVersion>& result,
668 std::string& error)
670 if (version.isUInt()) {
671 RequestVersion v;
672 v.Major = version.asUInt();
673 result.push_back(v);
674 return true;
677 if (!version.isObject()) {
678 if (inArray) {
679 error = "'version' array entry is not a non-negative integer or object";
680 } else {
681 error =
682 "'version' member is not a non-negative integer, object, or array";
684 return false;
687 Json::Value const& major = version["major"];
688 if (major.isNull()) {
689 error = "'version' object 'major' member missing";
690 return false;
692 if (!major.isUInt()) {
693 error = "'version' object 'major' member is not a non-negative integer";
694 return false;
697 RequestVersion v;
698 v.Major = major.asUInt();
700 Json::Value const& minor = version["minor"];
701 if (minor.isUInt()) {
702 v.Minor = minor.asUInt();
703 } else if (!minor.isNull()) {
704 error = "'version' object 'minor' member is not a non-negative integer";
705 return false;
708 result.push_back(v);
710 return true;
713 std::string cmFileAPI::NoSupportedVersion(
714 std::vector<RequestVersion> const& versions)
716 std::ostringstream msg;
717 msg << "no supported version specified";
718 if (!versions.empty()) {
719 msg << " among:";
720 for (RequestVersion const& v : versions) {
721 msg << " " << v.Major << "." << v.Minor;
724 return msg.str();
727 // The "codemodel" object kind.
729 // Update Help/manual/cmake-file-api.7.rst when updating this constant.
730 static unsigned int const CodeModelV2Minor = 7;
732 void cmFileAPI::BuildClientRequestCodeModel(
733 ClientRequest& r, std::vector<RequestVersion> const& versions)
735 // Select a known version from those requested.
736 for (RequestVersion const& v : versions) {
737 if ((v.Major == 2 && v.Minor <= CodeModelV2Minor)) {
738 r.Version = v.Major;
739 break;
742 if (!r.Version) {
743 r.Error = NoSupportedVersion(versions);
747 Json::Value cmFileAPI::BuildCodeModel(Object const& object)
749 Json::Value codemodel = cmFileAPICodemodelDump(*this, object.Version);
750 codemodel["kind"] = this->ObjectKindName(object.Kind);
752 Json::Value& version = codemodel["version"];
753 if (object.Version == 2) {
754 version = BuildVersion(2, CodeModelV2Minor);
755 } else {
756 return codemodel; // should be unreachable
759 return codemodel;
762 // The "configureLog" object kind.
764 // Update Help/manual/cmake-file-api.7.rst when updating this constant.
765 static unsigned int const ConfigureLogV1Minor = 0;
767 void cmFileAPI::BuildClientRequestConfigureLog(
768 ClientRequest& r, std::vector<RequestVersion> const& versions)
770 // Select a known version from those requested.
771 for (RequestVersion const& v : versions) {
772 if ((v.Major == 1 && v.Minor <= ConfigureLogV1Minor)) {
773 r.Version = v.Major;
774 break;
777 if (!r.Version) {
778 r.Error = NoSupportedVersion(versions);
782 Json::Value cmFileAPI::BuildConfigureLog(Object const& object)
784 Json::Value configureLog = cmFileAPIConfigureLogDump(*this, object.Version);
785 configureLog["kind"] = this->ObjectKindName(object.Kind);
787 Json::Value& version = configureLog["version"];
788 if (object.Version == 1) {
789 version = BuildVersion(1, ConfigureLogV1Minor);
790 } else {
791 return configureLog; // should be unreachable
794 return configureLog;
797 // The "cache" object kind.
799 static unsigned int const CacheV2Minor = 0;
801 void cmFileAPI::BuildClientRequestCache(
802 ClientRequest& r, std::vector<RequestVersion> const& versions)
804 // Select a known version from those requested.
805 for (RequestVersion const& v : versions) {
806 if ((v.Major == 2 && v.Minor <= CacheV2Minor)) {
807 r.Version = v.Major;
808 break;
811 if (!r.Version) {
812 r.Error = NoSupportedVersion(versions);
816 Json::Value cmFileAPI::BuildCache(Object const& object)
818 Json::Value cache = cmFileAPICacheDump(*this, object.Version);
819 cache["kind"] = this->ObjectKindName(object.Kind);
821 Json::Value& version = cache["version"];
822 if (object.Version == 2) {
823 version = BuildVersion(2, CacheV2Minor);
824 } else {
825 return cache; // should be unreachable
828 return cache;
831 // The "cmakeFiles" object kind.
833 static unsigned int const CMakeFilesV1Minor = 1;
835 void cmFileAPI::BuildClientRequestCMakeFiles(
836 ClientRequest& r, std::vector<RequestVersion> const& versions)
838 // Select a known version from those requested.
839 for (RequestVersion const& v : versions) {
840 if ((v.Major == 1 && v.Minor <= CMakeFilesV1Minor)) {
841 r.Version = v.Major;
842 break;
845 if (!r.Version) {
846 r.Error = NoSupportedVersion(versions);
850 Json::Value cmFileAPI::BuildCMakeFiles(Object const& object)
852 Json::Value cmakeFiles = cmFileAPICMakeFilesDump(*this, object.Version);
853 cmakeFiles["kind"] = this->ObjectKindName(object.Kind);
855 Json::Value& version = cmakeFiles["version"];
856 if (object.Version == 1) {
857 version = BuildVersion(1, CMakeFilesV1Minor);
858 } else {
859 return cmakeFiles; // should be unreachable
862 return cmakeFiles;
865 // The "toolchains" object kind.
867 static unsigned int const ToolchainsV1Minor = 0;
869 void cmFileAPI::BuildClientRequestToolchains(
870 ClientRequest& r, std::vector<RequestVersion> const& versions)
872 // Select a known version from those requested.
873 for (RequestVersion const& v : versions) {
874 if ((v.Major == 1 && v.Minor <= ToolchainsV1Minor)) {
875 r.Version = v.Major;
876 break;
879 if (!r.Version) {
880 r.Error = NoSupportedVersion(versions);
884 Json::Value cmFileAPI::BuildToolchains(Object const& object)
886 Json::Value toolchains = cmFileAPIToolchainsDump(*this, object.Version);
887 toolchains["kind"] = this->ObjectKindName(object.Kind);
889 Json::Value& version = toolchains["version"];
890 if (object.Version == 1) {
891 version = BuildVersion(1, ToolchainsV1Minor);
892 } else {
893 return toolchains; // should be unreachable
896 return toolchains;
899 // The "__test" object kind is for internal testing of CMake.
901 static unsigned int const InternalTestV1Minor = 3;
902 static unsigned int const InternalTestV2Minor = 0;
904 void cmFileAPI::BuildClientRequestInternalTest(
905 ClientRequest& r, std::vector<RequestVersion> const& versions)
907 // Select a known version from those requested.
908 for (RequestVersion const& v : versions) {
909 if ((v.Major == 1 && v.Minor <= InternalTestV1Minor) || //
910 (v.Major == 2 && v.Minor <= InternalTestV2Minor)) {
911 r.Version = v.Major;
912 break;
915 if (!r.Version) {
916 r.Error = NoSupportedVersion(versions);
920 Json::Value cmFileAPI::BuildInternalTest(Object const& object)
922 Json::Value test = Json::objectValue;
923 test["kind"] = this->ObjectKindName(object.Kind);
924 Json::Value& version = test["version"];
925 if (object.Version == 2) {
926 version = BuildVersion(2, InternalTestV2Minor);
927 } else {
928 version = BuildVersion(1, InternalTestV1Minor);
930 return test;
933 Json::Value cmFileAPI::ReportCapabilities()
935 Json::Value capabilities = Json::objectValue;
936 Json::Value& requests = capabilities["requests"] = Json::arrayValue;
939 Json::Value request = Json::objectValue;
940 request["kind"] = ObjectKindName(ObjectKind::CodeModel);
941 Json::Value& versions = request["version"] = Json::arrayValue;
942 versions.append(BuildVersion(2, CodeModelV2Minor));
943 requests.append(std::move(request)); // NOLINT(*)
947 Json::Value request = Json::objectValue;
948 request["kind"] = ObjectKindName(ObjectKind::ConfigureLog);
949 Json::Value& versions = request["version"] = Json::arrayValue;
950 versions.append(BuildVersion(1, ConfigureLogV1Minor));
951 requests.append(std::move(request)); // NOLINT(*)
955 Json::Value request = Json::objectValue;
956 request["kind"] = ObjectKindName(ObjectKind::Cache);
957 Json::Value& versions = request["version"] = Json::arrayValue;
958 versions.append(BuildVersion(2, CacheV2Minor));
959 requests.append(std::move(request)); // NOLINT(*)
963 Json::Value request = Json::objectValue;
964 request["kind"] = ObjectKindName(ObjectKind::CMakeFiles);
965 Json::Value& versions = request["version"] = Json::arrayValue;
966 versions.append(BuildVersion(1, CMakeFilesV1Minor));
967 requests.append(std::move(request)); // NOLINT(*)
971 Json::Value request = Json::objectValue;
972 request["kind"] = ObjectKindName(ObjectKind::Toolchains);
973 Json::Value& versions = request["version"] = Json::arrayValue;
974 versions.append(BuildVersion(1, ToolchainsV1Minor));
975 requests.append(std::move(request)); // NOLINT(*)
978 return capabilities;
981 bool cmFileAPI::AddProjectQuery(cmFileAPI::ObjectKind kind,
982 unsigned majorVersion, unsigned minorVersion)
984 switch (kind) {
985 case ObjectKind::CodeModel:
986 if (majorVersion != 2 || minorVersion > CodeModelV2Minor) {
987 return false;
989 break;
990 case ObjectKind::Cache:
991 if (majorVersion != 2 || minorVersion > CacheV2Minor) {
992 return false;
994 break;
995 case ObjectKind::CMakeFiles:
996 if (majorVersion != 1 || minorVersion > CMakeFilesV1Minor) {
997 return false;
999 break;
1000 case ObjectKind::Toolchains:
1001 if (majorVersion != 1 || minorVersion > ToolchainsV1Minor) {
1002 return false;
1004 break;
1005 // These cannot be requested by the project
1006 case ObjectKind::ConfigureLog:
1007 case ObjectKind::InternalTest:
1008 return false;
1011 Object query;
1012 query.Kind = kind;
1013 query.Version = majorVersion;
1014 if (std::find(this->TopQuery.Known.begin(), this->TopQuery.Known.end(),
1015 query) == this->TopQuery.Known.end()) {
1016 this->TopQuery.Known.emplace_back(query);
1017 this->QueryExists = true;
1020 return true;