mirror of
https://github.com/Kitware/CMake.git
synced 2026-02-21 14:40:26 -06:00
Merge topic 'fileapi'
b9c6f08276Help: Add release note for fileapi feature4b6b2a571cfileapi: extend codemodel v2 with directory detailseb8c7676a4fileapi: extend codemodel v2 with a project model42f0125cebfileapi: Add test for cmakeFiles v16615408193fileapi: add cmakeFiles v13f6ee75a66fileapi: Add test for cache v27489e95b8efileapi: add cache v2ea0a060168fileapi: Add test for codemodel v2 ... Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !2706
This commit is contained in:
@@ -538,7 +538,7 @@ macro (CMAKE_BUILD_UTILITIES)
|
||||
#---------------------------------------------------------------------
|
||||
# Build jsoncpp library.
|
||||
if(CMAKE_USE_SYSTEM_JSONCPP)
|
||||
find_package(JsonCpp)
|
||||
find_package(JsonCpp 1.4.1)
|
||||
if(NOT JsonCpp_FOUND)
|
||||
message(FATAL_ERROR
|
||||
"CMAKE_USE_SYSTEM_JSONCPP is ON but a JsonCpp is not found!")
|
||||
|
||||
@@ -30,6 +30,7 @@ Reference Manuals
|
||||
/manual/cmake-compile-features.7
|
||||
/manual/cmake-developer.7
|
||||
/manual/cmake-env-variables.7
|
||||
/manual/cmake-file-api.7
|
||||
/manual/cmake-generator-expressions.7
|
||||
/manual/cmake-generators.7
|
||||
/manual/cmake-language.7
|
||||
|
||||
1111
Help/manual/cmake-file-api.7.rst
Normal file
1111
Help/manual/cmake-file-api.7.rst
Normal file
File diff suppressed because it is too large
Load Diff
5
Help/release/dev/fileapi.rst
Normal file
5
Help/release/dev/fileapi.rst
Normal file
@@ -0,0 +1,5 @@
|
||||
fileapi
|
||||
-------
|
||||
|
||||
* A file-based api for clients to get semantic buildsystem information
|
||||
has been added. See the :manual:`cmake-file-api(7)` manual.
|
||||
@@ -207,6 +207,14 @@ set(SRCS
|
||||
cmExtraKateGenerator.h
|
||||
cmExtraSublimeTextGenerator.cxx
|
||||
cmExtraSublimeTextGenerator.h
|
||||
cmFileAPI.cxx
|
||||
cmFileAPI.h
|
||||
cmFileAPICache.cxx
|
||||
cmFileAPICache.h
|
||||
cmFileAPICodemodel.cxx
|
||||
cmFileAPICodemodel.h
|
||||
cmFileAPICMakeFiles.cxx
|
||||
cmFileAPICMakeFiles.h
|
||||
cmFileLock.cxx
|
||||
cmFileLock.h
|
||||
cmFileLockPool.cxx
|
||||
|
||||
800
Source/cmFileAPI.cxx
Normal file
800
Source/cmFileAPI.cxx
Normal file
@@ -0,0 +1,800 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmFileAPI.h"
|
||||
|
||||
#include "cmAlgorithms.h"
|
||||
#include "cmCryptoHash.h"
|
||||
#include "cmFileAPICMakeFiles.h"
|
||||
#include "cmFileAPICache.h"
|
||||
#include "cmFileAPICodemodel.h"
|
||||
#include "cmGlobalGenerator.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmTimestamp.h"
|
||||
#include "cmake.h"
|
||||
#include "cmsys/Directory.hxx"
|
||||
#include "cmsys/FStream.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
cmFileAPI::cmFileAPI(cmake* cm)
|
||||
: CMakeInstance(cm)
|
||||
{
|
||||
this->APIv1 =
|
||||
this->CMakeInstance->GetHomeOutputDirectory() + "/.cmake/api/v1";
|
||||
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
rbuilder["collectComments"] = false;
|
||||
rbuilder["failIfExtra"] = true;
|
||||
rbuilder["rejectDupKeys"] = false;
|
||||
rbuilder["strictRoot"] = true;
|
||||
this->JsonReader =
|
||||
std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["indentation"] = "\t";
|
||||
this->JsonWriter =
|
||||
std::unique_ptr<Json::StreamWriter>(wbuilder.newStreamWriter());
|
||||
}
|
||||
|
||||
void cmFileAPI::ReadQueries()
|
||||
{
|
||||
std::string const query_dir = this->APIv1 + "/query";
|
||||
this->QueryExists = cmSystemTools::FileIsDirectory(query_dir);
|
||||
if (!this->QueryExists) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load queries at the top level.
|
||||
std::vector<std::string> queries = cmFileAPI::LoadDir(query_dir);
|
||||
|
||||
// Read the queries and save for later.
|
||||
for (std::string& query : queries) {
|
||||
if (cmHasLiteralPrefix(query, "client-")) {
|
||||
this->ReadClient(query);
|
||||
} else if (!cmFileAPI::ReadQuery(query, this->TopQuery.Known)) {
|
||||
this->TopQuery.Unknown.push_back(std::move(query));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmFileAPI::WriteReplies()
|
||||
{
|
||||
if (this->QueryExists) {
|
||||
cmSystemTools::MakeDirectory(this->APIv1 + "/reply");
|
||||
this->WriteJsonFile(this->BuildReplyIndex(), "index", ComputeSuffixTime);
|
||||
}
|
||||
|
||||
this->RemoveOldReplyFiles();
|
||||
}
|
||||
|
||||
std::vector<std::string> cmFileAPI::LoadDir(std::string const& dir)
|
||||
{
|
||||
std::vector<std::string> files;
|
||||
cmsys::Directory d;
|
||||
d.Load(dir);
|
||||
for (unsigned long i = 0; i < d.GetNumberOfFiles(); ++i) {
|
||||
std::string f = d.GetFile(i);
|
||||
if (f != "." && f != "..") {
|
||||
files.push_back(std::move(f));
|
||||
}
|
||||
}
|
||||
std::sort(files.begin(), files.end());
|
||||
return files;
|
||||
}
|
||||
|
||||
void cmFileAPI::RemoveOldReplyFiles()
|
||||
{
|
||||
std::string const reply_dir = this->APIv1 + "/reply";
|
||||
std::vector<std::string> files = this->LoadDir(reply_dir);
|
||||
for (std::string const& f : files) {
|
||||
if (this->ReplyFiles.find(f) == this->ReplyFiles.end()) {
|
||||
std::string file = reply_dir + "/" + f;
|
||||
cmSystemTools::RemoveFile(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cmFileAPI::ReadJsonFile(std::string const& file, Json::Value& value,
|
||||
std::string& error)
|
||||
{
|
||||
std::vector<char> content;
|
||||
|
||||
cmsys::ifstream fin;
|
||||
if (!cmSystemTools::FileIsDirectory(file)) {
|
||||
fin.open(file.c_str(), std::ios::binary);
|
||||
}
|
||||
auto finEnd = fin.rdbuf()->pubseekoff(0, std::ios::end);
|
||||
if (finEnd > 0) {
|
||||
size_t finSize = finEnd;
|
||||
try {
|
||||
// Allocate a buffer to read the whole file.
|
||||
content.resize(finSize);
|
||||
|
||||
// Now read the file from the beginning.
|
||||
fin.seekg(0, std::ios::beg);
|
||||
fin.read(content.data(), finSize);
|
||||
} catch (...) {
|
||||
fin.setstate(std::ios::failbit);
|
||||
}
|
||||
}
|
||||
fin.close();
|
||||
if (!fin) {
|
||||
value = Json::Value();
|
||||
error = "failed to read from file";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse our buffer as json.
|
||||
if (!this->JsonReader->parse(content.data(), content.data() + content.size(),
|
||||
&value, &error)) {
|
||||
value = Json::Value();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string cmFileAPI::WriteJsonFile(
|
||||
Json::Value const& value, std::string const& prefix,
|
||||
std::string (*computeSuffix)(std::string const&))
|
||||
{
|
||||
std::string fileName;
|
||||
|
||||
// Write the json file with a temporary name.
|
||||
std::string const& tmpFile = this->APIv1 + "/tmp.json";
|
||||
cmsys::ofstream ftmp(tmpFile.c_str());
|
||||
this->JsonWriter->write(value, &ftmp);
|
||||
ftmp << "\n";
|
||||
ftmp.close();
|
||||
if (!ftmp) {
|
||||
cmSystemTools::RemoveFile(tmpFile);
|
||||
return fileName;
|
||||
}
|
||||
|
||||
// Compute the final name for the file.
|
||||
fileName = prefix + "-" + computeSuffix(tmpFile) + ".json";
|
||||
|
||||
// Create the destination.
|
||||
std::string file = this->APIv1 + "/reply";
|
||||
cmSystemTools::MakeDirectory(file);
|
||||
file += "/";
|
||||
file += fileName;
|
||||
|
||||
// If the final name already exists then assume it has proper content.
|
||||
// Otherwise, atomically place the reply file at its final name
|
||||
if (cmSystemTools::FileExists(file, true) ||
|
||||
!cmSystemTools::RenameFile(tmpFile.c_str(), file.c_str())) {
|
||||
cmSystemTools::RemoveFile(tmpFile);
|
||||
}
|
||||
|
||||
// Record this among files we have just written.
|
||||
this->ReplyFiles.insert(fileName);
|
||||
|
||||
return fileName;
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::MaybeJsonFile(Json::Value in, std::string const& prefix)
|
||||
{
|
||||
Json::Value out;
|
||||
if (in.isObject() || in.isArray()) {
|
||||
out = Json::objectValue;
|
||||
out["jsonFile"] = this->WriteJsonFile(in, prefix);
|
||||
} else {
|
||||
out = std::move(in);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string cmFileAPI::ComputeSuffixHash(std::string const& file)
|
||||
{
|
||||
cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_256);
|
||||
std::string hash = hasher.HashFile(file);
|
||||
hash.resize(20, '0');
|
||||
return hash;
|
||||
}
|
||||
|
||||
std::string cmFileAPI::ComputeSuffixTime(std::string const&)
|
||||
{
|
||||
std::chrono::milliseconds ms =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
std::chrono::seconds s =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(ms);
|
||||
|
||||
std::time_t ts = s.count();
|
||||
std::size_t tms = ms.count() % 1000;
|
||||
|
||||
cmTimestamp cmts;
|
||||
std::ostringstream ss;
|
||||
ss << cmts.CreateTimestampFromTimeT(ts, "%Y-%m-%dT%H-%M-%S", true) << '-'
|
||||
<< std::setfill('0') << std::setw(4) << tms;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool cmFileAPI::ReadQuery(std::string const& query,
|
||||
std::vector<Object>& objects)
|
||||
{
|
||||
// Parse the "<kind>-" syntax.
|
||||
std::string::size_type sep_pos = query.find('-');
|
||||
if (sep_pos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
std::string kindName = query.substr(0, sep_pos);
|
||||
std::string verStr = query.substr(sep_pos + 1);
|
||||
if (kindName == ObjectKindName(ObjectKind::CodeModel)) {
|
||||
Object o;
|
||||
o.Kind = ObjectKind::CodeModel;
|
||||
if (verStr == "v2") {
|
||||
o.Version = 2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
objects.push_back(o);
|
||||
return true;
|
||||
}
|
||||
if (kindName == ObjectKindName(ObjectKind::Cache)) {
|
||||
Object o;
|
||||
o.Kind = ObjectKind::Cache;
|
||||
if (verStr == "v2") {
|
||||
o.Version = 2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
objects.push_back(o);
|
||||
return true;
|
||||
}
|
||||
if (kindName == ObjectKindName(ObjectKind::CMakeFiles)) {
|
||||
Object o;
|
||||
o.Kind = ObjectKind::CMakeFiles;
|
||||
if (verStr == "v1") {
|
||||
o.Version = 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
objects.push_back(o);
|
||||
return true;
|
||||
}
|
||||
if (kindName == ObjectKindName(ObjectKind::InternalTest)) {
|
||||
Object o;
|
||||
o.Kind = ObjectKind::InternalTest;
|
||||
if (verStr == "v1") {
|
||||
o.Version = 1;
|
||||
} else if (verStr == "v2") {
|
||||
o.Version = 2;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
objects.push_back(o);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cmFileAPI::ReadClient(std::string const& client)
|
||||
{
|
||||
// Load queries for the client.
|
||||
std::string clientDir = this->APIv1 + "/query/" + client;
|
||||
std::vector<std::string> queries = this->LoadDir(clientDir);
|
||||
|
||||
// Read the queries and save for later.
|
||||
ClientQuery& clientQuery = this->ClientQueries[client];
|
||||
for (std::string& query : queries) {
|
||||
if (query == "query.json") {
|
||||
clientQuery.HaveQueryJson = true;
|
||||
this->ReadClientQuery(client, clientQuery.QueryJson);
|
||||
} else if (!this->ReadQuery(query, clientQuery.DirQuery.Known)) {
|
||||
clientQuery.DirQuery.Unknown.push_back(std::move(query));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmFileAPI::ReadClientQuery(std::string const& client, ClientQueryJson& q)
|
||||
{
|
||||
// Read the query.json file.
|
||||
std::string queryFile = this->APIv1 + "/query/" + client + "/query.json";
|
||||
Json::Value query;
|
||||
if (!this->ReadJsonFile(queryFile, query, q.Error)) {
|
||||
return;
|
||||
}
|
||||
if (!query.isObject()) {
|
||||
q.Error = "query root is not an object";
|
||||
return;
|
||||
}
|
||||
|
||||
Json::Value const& clientValue = query["client"];
|
||||
if (!clientValue.isNull()) {
|
||||
q.ClientValue = clientValue;
|
||||
}
|
||||
q.RequestsValue = std::move(query["requests"]);
|
||||
q.Requests = this->BuildClientRequests(q.RequestsValue);
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildReplyIndex()
|
||||
{
|
||||
Json::Value index(Json::objectValue);
|
||||
|
||||
// Report information about this version of CMake.
|
||||
index["cmake"] = this->BuildCMake();
|
||||
|
||||
// Reply to all queries that we loaded.
|
||||
Json::Value& reply = index["reply"] = this->BuildReply(this->TopQuery);
|
||||
for (auto const& client : this->ClientQueries) {
|
||||
std::string const& clientName = client.first;
|
||||
ClientQuery const& clientQuery = client.second;
|
||||
reply[clientName] = this->BuildClientReply(clientQuery);
|
||||
}
|
||||
|
||||
// Move our index of generated objects into its field.
|
||||
Json::Value& objects = index["objects"] = Json::arrayValue;
|
||||
for (auto& entry : this->ReplyIndexObjects) {
|
||||
objects.append(std::move(entry.second)); // NOLINT(*)
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildCMake()
|
||||
{
|
||||
Json::Value cmake = Json::objectValue;
|
||||
cmake["version"] = this->CMakeInstance->ReportVersionJson();
|
||||
Json::Value& cmake_paths = cmake["paths"] = Json::objectValue;
|
||||
cmake_paths["cmake"] = cmSystemTools::GetCMakeCommand();
|
||||
cmake_paths["ctest"] = cmSystemTools::GetCTestCommand();
|
||||
cmake_paths["cpack"] = cmSystemTools::GetCPackCommand();
|
||||
cmake_paths["root"] = cmSystemTools::GetCMakeRoot();
|
||||
cmake["generator"] = this->CMakeInstance->GetGlobalGenerator()->GetJson();
|
||||
return cmake;
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildReply(Query const& q)
|
||||
{
|
||||
Json::Value reply = Json::objectValue;
|
||||
for (Object const& o : q.Known) {
|
||||
std::string const& name = ObjectName(o);
|
||||
reply[name] = this->AddReplyIndexObject(o);
|
||||
}
|
||||
|
||||
for (std::string const& name : q.Unknown) {
|
||||
reply[name] = cmFileAPI::BuildReplyError("unknown query file");
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildReplyError(std::string const& error)
|
||||
{
|
||||
Json::Value e = Json::objectValue;
|
||||
e["error"] = error;
|
||||
return e;
|
||||
}
|
||||
|
||||
Json::Value const& cmFileAPI::AddReplyIndexObject(Object const& o)
|
||||
{
|
||||
Json::Value& indexEntry = this->ReplyIndexObjects[o];
|
||||
if (!indexEntry.isNull()) {
|
||||
// The reply object has already been generated.
|
||||
return indexEntry;
|
||||
}
|
||||
|
||||
// Generate this reply object.
|
||||
Json::Value const& object = this->BuildObject(o);
|
||||
assert(object.isObject());
|
||||
|
||||
// Populate this index entry.
|
||||
indexEntry = Json::objectValue;
|
||||
indexEntry["kind"] = object["kind"];
|
||||
indexEntry["version"] = object["version"];
|
||||
indexEntry["jsonFile"] = this->WriteJsonFile(object, ObjectName(o));
|
||||
return indexEntry;
|
||||
}
|
||||
|
||||
const char* cmFileAPI::ObjectKindName(ObjectKind kind)
|
||||
{
|
||||
// Keep in sync with ObjectKind enum.
|
||||
static const char* objectKindNames[] = {
|
||||
"codemodel", //
|
||||
"cache", //
|
||||
"cmakeFiles", //
|
||||
"__test" //
|
||||
};
|
||||
return objectKindNames[size_t(kind)];
|
||||
}
|
||||
|
||||
std::string cmFileAPI::ObjectName(Object const& o)
|
||||
{
|
||||
std::string name = ObjectKindName(o.Kind);
|
||||
name += "-v";
|
||||
name += std::to_string(o.Version);
|
||||
return name;
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildObject(Object const& object)
|
||||
{
|
||||
Json::Value value;
|
||||
|
||||
switch (object.Kind) {
|
||||
case ObjectKind::CodeModel:
|
||||
value = this->BuildCodeModel(object);
|
||||
break;
|
||||
case ObjectKind::Cache:
|
||||
value = this->BuildCache(object);
|
||||
break;
|
||||
case ObjectKind::CMakeFiles:
|
||||
value = this->BuildCMakeFiles(object);
|
||||
break;
|
||||
case ObjectKind::InternalTest:
|
||||
value = this->BuildInternalTest(object);
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
cmFileAPI::ClientRequests cmFileAPI::BuildClientRequests(
|
||||
Json::Value const& requests)
|
||||
{
|
||||
ClientRequests result;
|
||||
if (requests.isNull()) {
|
||||
result.Error = "'requests' member missing";
|
||||
return result;
|
||||
}
|
||||
if (!requests.isArray()) {
|
||||
result.Error = "'requests' member is not an array";
|
||||
return result;
|
||||
}
|
||||
|
||||
result.reserve(requests.size());
|
||||
for (Json::Value const& request : requests) {
|
||||
result.emplace_back(this->BuildClientRequest(request));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
|
||||
Json::Value const& request)
|
||||
{
|
||||
ClientRequest r;
|
||||
|
||||
if (!request.isObject()) {
|
||||
r.Error = "request is not an object";
|
||||
return r;
|
||||
}
|
||||
|
||||
Json::Value const& kind = request["kind"];
|
||||
if (kind.isNull()) {
|
||||
r.Error = "'kind' member missing";
|
||||
return r;
|
||||
}
|
||||
if (!kind.isString()) {
|
||||
r.Error = "'kind' member is not a string";
|
||||
return r;
|
||||
}
|
||||
std::string const& kindName = kind.asString();
|
||||
|
||||
if (kindName == this->ObjectKindName(ObjectKind::CodeModel)) {
|
||||
r.Kind = ObjectKind::CodeModel;
|
||||
} else if (kindName == this->ObjectKindName(ObjectKind::Cache)) {
|
||||
r.Kind = ObjectKind::Cache;
|
||||
} else if (kindName == this->ObjectKindName(ObjectKind::CMakeFiles)) {
|
||||
r.Kind = ObjectKind::CMakeFiles;
|
||||
} else if (kindName == this->ObjectKindName(ObjectKind::InternalTest)) {
|
||||
r.Kind = ObjectKind::InternalTest;
|
||||
} else {
|
||||
r.Error = "unknown request kind '" + kindName + "'";
|
||||
return r;
|
||||
}
|
||||
|
||||
Json::Value const& version = request["version"];
|
||||
if (version.isNull()) {
|
||||
r.Error = "'version' member missing";
|
||||
return r;
|
||||
}
|
||||
std::vector<RequestVersion> versions;
|
||||
if (!cmFileAPI::ReadRequestVersions(version, versions, r.Error)) {
|
||||
return r;
|
||||
}
|
||||
|
||||
switch (r.Kind) {
|
||||
case ObjectKind::CodeModel:
|
||||
this->BuildClientRequestCodeModel(r, versions);
|
||||
break;
|
||||
case ObjectKind::Cache:
|
||||
this->BuildClientRequestCache(r, versions);
|
||||
break;
|
||||
case ObjectKind::CMakeFiles:
|
||||
this->BuildClientRequestCMakeFiles(r, versions);
|
||||
break;
|
||||
case ObjectKind::InternalTest:
|
||||
this->BuildClientRequestInternalTest(r, versions);
|
||||
break;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildClientReply(ClientQuery const& q)
|
||||
{
|
||||
Json::Value reply = this->BuildReply(q.DirQuery);
|
||||
|
||||
if (!q.HaveQueryJson) {
|
||||
return reply;
|
||||
}
|
||||
|
||||
Json::Value& reply_query_json = reply["query.json"];
|
||||
ClientQueryJson const& qj = q.QueryJson;
|
||||
|
||||
if (!qj.Error.empty()) {
|
||||
reply_query_json = this->BuildReplyError(qj.Error);
|
||||
return reply;
|
||||
}
|
||||
|
||||
if (!qj.ClientValue.isNull()) {
|
||||
reply_query_json["client"] = qj.ClientValue;
|
||||
}
|
||||
|
||||
if (!qj.RequestsValue.isNull()) {
|
||||
reply_query_json["requests"] = qj.RequestsValue;
|
||||
}
|
||||
|
||||
reply_query_json["responses"] = this->BuildClientReplyResponses(qj.Requests);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildClientReplyResponses(
|
||||
ClientRequests const& requests)
|
||||
{
|
||||
Json::Value responses;
|
||||
|
||||
if (!requests.Error.empty()) {
|
||||
responses = this->BuildReplyError(requests.Error);
|
||||
return responses;
|
||||
}
|
||||
|
||||
responses = Json::arrayValue;
|
||||
for (ClientRequest const& request : requests) {
|
||||
responses.append(this->BuildClientReplyResponse(request));
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildClientReplyResponse(ClientRequest const& request)
|
||||
{
|
||||
Json::Value response;
|
||||
if (!request.Error.empty()) {
|
||||
response = this->BuildReplyError(request.Error);
|
||||
return response;
|
||||
}
|
||||
response = this->AddReplyIndexObject(request);
|
||||
return response;
|
||||
}
|
||||
|
||||
bool cmFileAPI::ReadRequestVersions(Json::Value const& version,
|
||||
std::vector<RequestVersion>& versions,
|
||||
std::string& error)
|
||||
{
|
||||
if (version.isArray()) {
|
||||
for (Json::Value const& v : version) {
|
||||
if (!ReadRequestVersion(v, /*inArray=*/true, versions, error)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!ReadRequestVersion(version, /*inArray=*/false, versions, error)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmFileAPI::ReadRequestVersion(Json::Value const& version, bool inArray,
|
||||
std::vector<RequestVersion>& result,
|
||||
std::string& error)
|
||||
{
|
||||
if (version.isUInt()) {
|
||||
RequestVersion v;
|
||||
v.Major = version.asUInt();
|
||||
result.push_back(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!version.isObject()) {
|
||||
if (inArray) {
|
||||
error = "'version' array entry is not a non-negative integer or object";
|
||||
} else {
|
||||
error =
|
||||
"'version' member is not a non-negative integer, object, or array";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Json::Value const& major = version["major"];
|
||||
if (major.isNull()) {
|
||||
error = "'version' object 'major' member missing";
|
||||
return false;
|
||||
}
|
||||
if (!major.isUInt()) {
|
||||
error = "'version' object 'major' member is not a non-negative integer";
|
||||
return false;
|
||||
}
|
||||
|
||||
RequestVersion v;
|
||||
v.Major = major.asUInt();
|
||||
|
||||
Json::Value const& minor = version["minor"];
|
||||
if (minor.isUInt()) {
|
||||
v.Minor = minor.asUInt();
|
||||
} else if (!minor.isNull()) {
|
||||
error = "'version' object 'minor' member is not a non-negative integer";
|
||||
return false;
|
||||
}
|
||||
|
||||
result.push_back(v);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string cmFileAPI::NoSupportedVersion(
|
||||
std::vector<RequestVersion> const& versions)
|
||||
{
|
||||
std::ostringstream msg;
|
||||
msg << "no supported version specified";
|
||||
if (!versions.empty()) {
|
||||
msg << " among:";
|
||||
for (RequestVersion const& v : versions) {
|
||||
msg << " " << v.Major << "." << v.Minor;
|
||||
}
|
||||
}
|
||||
return msg.str();
|
||||
}
|
||||
|
||||
// The "codemodel" object kind.
|
||||
|
||||
static unsigned int const CodeModelV2Minor = 0;
|
||||
|
||||
void cmFileAPI::BuildClientRequestCodeModel(
|
||||
ClientRequest& r, std::vector<RequestVersion> const& versions)
|
||||
{
|
||||
// Select a known version from those requested.
|
||||
for (RequestVersion const& v : versions) {
|
||||
if ((v.Major == 2 && v.Minor <= CodeModelV2Minor)) {
|
||||
r.Version = v.Major;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!r.Version) {
|
||||
r.Error = NoSupportedVersion(versions);
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildCodeModel(Object const& object)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
Json::Value codemodel = cmFileAPICodemodelDump(*this, object.Version);
|
||||
codemodel["kind"] = this->ObjectKindName(object.Kind);
|
||||
|
||||
Json::Value& version = codemodel["version"] = Json::objectValue;
|
||||
if (object.Version == 2) {
|
||||
version["major"] = 2;
|
||||
version["minor"] = CodeModelV2Minor;
|
||||
} else {
|
||||
return codemodel; // should be unreachable
|
||||
}
|
||||
|
||||
return codemodel;
|
||||
}
|
||||
|
||||
// The "cache" object kind.
|
||||
|
||||
static unsigned int const CacheV2Minor = 0;
|
||||
|
||||
void cmFileAPI::BuildClientRequestCache(
|
||||
ClientRequest& r, std::vector<RequestVersion> const& versions)
|
||||
{
|
||||
// Select a known version from those requested.
|
||||
for (RequestVersion const& v : versions) {
|
||||
if ((v.Major == 2 && v.Minor <= CacheV2Minor)) {
|
||||
r.Version = v.Major;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!r.Version) {
|
||||
r.Error = NoSupportedVersion(versions);
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildCache(Object const& object)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
Json::Value cache = cmFileAPICacheDump(*this, object.Version);
|
||||
cache["kind"] = this->ObjectKindName(object.Kind);
|
||||
|
||||
Json::Value& version = cache["version"] = Json::objectValue;
|
||||
if (object.Version == 2) {
|
||||
version["major"] = 2;
|
||||
version["minor"] = CacheV2Minor;
|
||||
} else {
|
||||
return cache; // should be unreachable
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
// The "cmakeFiles" object kind.
|
||||
|
||||
static unsigned int const CMakeFilesV1Minor = 0;
|
||||
|
||||
void cmFileAPI::BuildClientRequestCMakeFiles(
|
||||
ClientRequest& r, std::vector<RequestVersion> const& versions)
|
||||
{
|
||||
// Select a known version from those requested.
|
||||
for (RequestVersion const& v : versions) {
|
||||
if ((v.Major == 1 && v.Minor <= CMakeFilesV1Minor)) {
|
||||
r.Version = v.Major;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!r.Version) {
|
||||
r.Error = NoSupportedVersion(versions);
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildCMakeFiles(Object const& object)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
Json::Value cmakeFiles = cmFileAPICMakeFilesDump(*this, object.Version);
|
||||
cmakeFiles["kind"] = this->ObjectKindName(object.Kind);
|
||||
|
||||
Json::Value& version = cmakeFiles["version"] = Json::objectValue;
|
||||
if (object.Version == 1) {
|
||||
version["major"] = 1;
|
||||
version["minor"] = CMakeFilesV1Minor;
|
||||
} else {
|
||||
return cmakeFiles; // should be unreachable
|
||||
}
|
||||
|
||||
return cmakeFiles;
|
||||
}
|
||||
|
||||
// The "__test" object kind is for internal testing of CMake.
|
||||
|
||||
static unsigned int const InternalTestV1Minor = 3;
|
||||
static unsigned int const InternalTestV2Minor = 0;
|
||||
|
||||
void cmFileAPI::BuildClientRequestInternalTest(
|
||||
ClientRequest& r, std::vector<RequestVersion> const& versions)
|
||||
{
|
||||
// Select a known version from those requested.
|
||||
for (RequestVersion const& v : versions) {
|
||||
if ((v.Major == 1 && v.Minor <= InternalTestV1Minor) || //
|
||||
(v.Major == 2 && v.Minor <= InternalTestV2Minor)) {
|
||||
r.Version = v.Major;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!r.Version) {
|
||||
r.Error = NoSupportedVersion(versions);
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value cmFileAPI::BuildInternalTest(Object const& object)
|
||||
{
|
||||
Json::Value test = Json::objectValue;
|
||||
test["kind"] = this->ObjectKindName(object.Kind);
|
||||
Json::Value& version = test["version"] = Json::objectValue;
|
||||
if (object.Version == 2) {
|
||||
version["major"] = 2;
|
||||
version["minor"] = InternalTestV2Minor;
|
||||
} else {
|
||||
version["major"] = 1;
|
||||
version["minor"] = InternalTestV1Minor;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
204
Source/cmFileAPI.h
Normal file
204
Source/cmFileAPI.h
Normal file
@@ -0,0 +1,204 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#ifndef cmFileAPI_h
|
||||
#define cmFileAPI_h
|
||||
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include "cm_jsoncpp_reader.h"
|
||||
#include "cm_jsoncpp_value.h"
|
||||
#include "cm_jsoncpp_writer.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory> // IWYU pragma: keep
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
class cmake;
|
||||
|
||||
class cmFileAPI
|
||||
{
|
||||
public:
|
||||
cmFileAPI(cmake* cm);
|
||||
|
||||
/** Read fileapi queries from disk. */
|
||||
void ReadQueries();
|
||||
|
||||
/** Write fileapi replies to disk. */
|
||||
void WriteReplies();
|
||||
|
||||
/** Get the "cmake" instance with which this was constructed. */
|
||||
cmake* GetCMakeInstance() const { return this->CMakeInstance; }
|
||||
|
||||
/** Convert a JSON object or array into an object with a single
|
||||
"jsonFile" member specifying a file named with the given prefix
|
||||
and holding the original object. Other JSON types are unchanged. */
|
||||
Json::Value MaybeJsonFile(Json::Value in, std::string const& prefix);
|
||||
|
||||
private:
|
||||
cmake* CMakeInstance;
|
||||
|
||||
/** The api/v1 directory location. */
|
||||
std::string APIv1;
|
||||
|
||||
/** The set of files we have just written to the reply directory. */
|
||||
std::unordered_set<std::string> ReplyFiles;
|
||||
|
||||
static std::vector<std::string> LoadDir(std::string const& dir);
|
||||
void RemoveOldReplyFiles();
|
||||
|
||||
// Keep in sync with ObjectKindName.
|
||||
enum class ObjectKind
|
||||
{
|
||||
CodeModel,
|
||||
Cache,
|
||||
CMakeFiles,
|
||||
InternalTest
|
||||
};
|
||||
|
||||
/** Identify one object kind and major version. */
|
||||
struct Object
|
||||
{
|
||||
ObjectKind Kind;
|
||||
unsigned long Version = 0;
|
||||
friend bool operator<(Object const& l, Object const& r)
|
||||
{
|
||||
if (l.Kind != r.Kind) {
|
||||
return l.Kind < r.Kind;
|
||||
}
|
||||
return l.Version < r.Version;
|
||||
}
|
||||
};
|
||||
|
||||
/** Represent content of a query directory. */
|
||||
struct Query
|
||||
{
|
||||
/** Known object kind-version pairs. */
|
||||
std::vector<Object> Known;
|
||||
/** Unknown object kind names. */
|
||||
std::vector<std::string> Unknown;
|
||||
};
|
||||
|
||||
/** Represent one request in a client 'query.json'. */
|
||||
struct ClientRequest : public Object
|
||||
{
|
||||
/** Empty if request is valid, else the error string. */
|
||||
std::string Error;
|
||||
};
|
||||
|
||||
/** Represent the "requests" in a client 'query.json'. */
|
||||
struct ClientRequests : public std::vector<ClientRequest>
|
||||
{
|
||||
/** Empty if requests field is valid, else the error string. */
|
||||
std::string Error;
|
||||
};
|
||||
|
||||
/** Represent the content of a client query.json file. */
|
||||
struct ClientQueryJson
|
||||
{
|
||||
/** The error string if parsing failed, else empty. */
|
||||
std::string Error;
|
||||
|
||||
/** The 'query.json' object "client" member if it exists, else null. */
|
||||
Json::Value ClientValue;
|
||||
|
||||
/** The 'query.json' object "requests" member if it exists, else null. */
|
||||
Json::Value RequestsValue;
|
||||
|
||||
/** Requests extracted from 'query.json'. */
|
||||
ClientRequests Requests;
|
||||
};
|
||||
|
||||
/** Represent content of a client query directory. */
|
||||
struct ClientQuery
|
||||
{
|
||||
/** The content of the client query directory except 'query.json'. */
|
||||
Query DirQuery;
|
||||
|
||||
/** True if 'query.json' exists. */
|
||||
bool HaveQueryJson = false;
|
||||
|
||||
/** The 'query.json' content. */
|
||||
ClientQueryJson QueryJson;
|
||||
};
|
||||
|
||||
/** Whether the top-level query directory exists at all. */
|
||||
bool QueryExists = false;
|
||||
|
||||
/** The content of the top-level query directory. */
|
||||
Query TopQuery;
|
||||
|
||||
/** The content of each "client-$client" query directory. */
|
||||
std::map<std::string, ClientQuery> ClientQueries;
|
||||
|
||||
/** Reply index object generated for object kind/version.
|
||||
This populates the "objects" field of the reply index. */
|
||||
std::map<Object, Json::Value> ReplyIndexObjects;
|
||||
|
||||
std::unique_ptr<Json::CharReader> JsonReader;
|
||||
std::unique_ptr<Json::StreamWriter> JsonWriter;
|
||||
|
||||
bool ReadJsonFile(std::string const& file, Json::Value& value,
|
||||
std::string& error);
|
||||
|
||||
std::string WriteJsonFile(
|
||||
Json::Value const& value, std::string const& prefix,
|
||||
std::string (*computeSuffix)(std::string const&) = ComputeSuffixHash);
|
||||
static std::string ComputeSuffixHash(std::string const&);
|
||||
static std::string ComputeSuffixTime(std::string const&);
|
||||
|
||||
static bool ReadQuery(std::string const& query,
|
||||
std::vector<Object>& objects);
|
||||
void ReadClient(std::string const& client);
|
||||
void ReadClientQuery(std::string const& client, ClientQueryJson& q);
|
||||
|
||||
Json::Value BuildReplyIndex();
|
||||
Json::Value BuildCMake();
|
||||
Json::Value BuildReply(Query const& q);
|
||||
static Json::Value BuildReplyError(std::string const& error);
|
||||
Json::Value const& AddReplyIndexObject(Object const& o);
|
||||
|
||||
static const char* ObjectKindName(ObjectKind kind);
|
||||
static std::string ObjectName(Object const& o);
|
||||
|
||||
Json::Value BuildObject(Object const& object);
|
||||
|
||||
ClientRequests BuildClientRequests(Json::Value const& requests);
|
||||
ClientRequest BuildClientRequest(Json::Value const& request);
|
||||
Json::Value BuildClientReply(ClientQuery const& q);
|
||||
Json::Value BuildClientReplyResponses(ClientRequests const& requests);
|
||||
Json::Value BuildClientReplyResponse(ClientRequest const& request);
|
||||
|
||||
struct RequestVersion
|
||||
{
|
||||
unsigned int Major = 0;
|
||||
unsigned int Minor = 0;
|
||||
};
|
||||
static bool ReadRequestVersions(Json::Value const& version,
|
||||
std::vector<RequestVersion>& versions,
|
||||
std::string& error);
|
||||
static bool ReadRequestVersion(Json::Value const& version, bool inArray,
|
||||
std::vector<RequestVersion>& result,
|
||||
std::string& error);
|
||||
static std::string NoSupportedVersion(
|
||||
std::vector<RequestVersion> const& versions);
|
||||
|
||||
void BuildClientRequestCodeModel(
|
||||
ClientRequest& r, std::vector<RequestVersion> const& versions);
|
||||
Json::Value BuildCodeModel(Object const& object);
|
||||
|
||||
void BuildClientRequestCache(ClientRequest& r,
|
||||
std::vector<RequestVersion> const& versions);
|
||||
Json::Value BuildCache(Object const& object);
|
||||
|
||||
void BuildClientRequestCMakeFiles(
|
||||
ClientRequest& r, std::vector<RequestVersion> const& versions);
|
||||
Json::Value BuildCMakeFiles(Object const& object);
|
||||
|
||||
void BuildClientRequestInternalTest(
|
||||
ClientRequest& r, std::vector<RequestVersion> const& versions);
|
||||
Json::Value BuildInternalTest(Object const& object);
|
||||
};
|
||||
|
||||
#endif
|
||||
113
Source/cmFileAPICMakeFiles.cxx
Normal file
113
Source/cmFileAPICMakeFiles.cxx
Normal file
@@ -0,0 +1,113 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmFileAPICMakeFiles.h"
|
||||
|
||||
#include "cmFileAPI.h"
|
||||
#include "cmGlobalGenerator.h"
|
||||
#include "cmLocalGenerator.h"
|
||||
#include "cmMakefile.h"
|
||||
#include "cmSystemTools.h"
|
||||
#include "cmake.h"
|
||||
|
||||
#include "cm_jsoncpp_value.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
class CMakeFiles
|
||||
{
|
||||
cmFileAPI& FileAPI;
|
||||
unsigned long Version;
|
||||
std::string CMakeModules;
|
||||
std::string const& TopSource;
|
||||
std::string const& TopBuild;
|
||||
bool OutOfSource;
|
||||
|
||||
Json::Value DumpPaths();
|
||||
Json::Value DumpInputs();
|
||||
Json::Value DumpInput(std::string const& file);
|
||||
|
||||
public:
|
||||
CMakeFiles(cmFileAPI& fileAPI, unsigned long version);
|
||||
Json::Value Dump();
|
||||
};
|
||||
|
||||
CMakeFiles::CMakeFiles(cmFileAPI& fileAPI, unsigned long version)
|
||||
: FileAPI(fileAPI)
|
||||
, Version(version)
|
||||
, CMakeModules(cmSystemTools::GetCMakeRoot() + "/Modules")
|
||||
, TopSource(this->FileAPI.GetCMakeInstance()->GetHomeDirectory())
|
||||
, TopBuild(this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory())
|
||||
, OutOfSource(TopBuild != TopSource)
|
||||
{
|
||||
static_cast<void>(this->Version);
|
||||
}
|
||||
|
||||
Json::Value CMakeFiles::Dump()
|
||||
{
|
||||
Json::Value cmakeFiles = Json::objectValue;
|
||||
cmakeFiles["paths"] = this->DumpPaths();
|
||||
cmakeFiles["inputs"] = DumpInputs();
|
||||
return cmakeFiles;
|
||||
}
|
||||
|
||||
Json::Value CMakeFiles::DumpPaths()
|
||||
{
|
||||
Json::Value paths = Json::objectValue;
|
||||
paths["source"] = this->TopSource;
|
||||
paths["build"] = this->TopBuild;
|
||||
return paths;
|
||||
}
|
||||
|
||||
Json::Value CMakeFiles::DumpInputs()
|
||||
{
|
||||
Json::Value inputs = Json::arrayValue;
|
||||
|
||||
cmGlobalGenerator* gg =
|
||||
this->FileAPI.GetCMakeInstance()->GetGlobalGenerator();
|
||||
for (cmLocalGenerator const* lg : gg->GetLocalGenerators()) {
|
||||
cmMakefile const* mf = lg->GetMakefile();
|
||||
for (std::string const& file : mf->GetListFiles()) {
|
||||
inputs.append(this->DumpInput(file));
|
||||
}
|
||||
}
|
||||
|
||||
return inputs;
|
||||
}
|
||||
|
||||
Json::Value CMakeFiles::DumpInput(std::string const& file)
|
||||
{
|
||||
Json::Value input = Json::objectValue;
|
||||
|
||||
bool const isCMake = cmSystemTools::IsSubDirectory(file, this->CMakeModules);
|
||||
if (isCMake) {
|
||||
input["isCMake"] = true;
|
||||
}
|
||||
|
||||
if (!cmSystemTools::IsSubDirectory(file, this->TopSource) &&
|
||||
!cmSystemTools::IsSubDirectory(file, this->TopBuild)) {
|
||||
input["isExternal"] = true;
|
||||
}
|
||||
|
||||
if (this->OutOfSource &&
|
||||
cmSystemTools::IsSubDirectory(file, this->TopBuild)) {
|
||||
input["isGenerated"] = true;
|
||||
}
|
||||
|
||||
std::string path = file;
|
||||
if (!isCMake && cmSystemTools::IsSubDirectory(path, this->TopSource)) {
|
||||
// Use a relative path within the source directory.
|
||||
path = cmSystemTools::RelativePath(this->TopSource, path);
|
||||
}
|
||||
input["path"] = path;
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value cmFileAPICMakeFilesDump(cmFileAPI& fileAPI, unsigned long version)
|
||||
{
|
||||
CMakeFiles cmakeFiles(fileAPI, version);
|
||||
return cmakeFiles.Dump();
|
||||
}
|
||||
15
Source/cmFileAPICMakeFiles.h
Normal file
15
Source/cmFileAPICMakeFiles.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#ifndef cmFileAPICMakeFiles_h
|
||||
#define cmFileAPICMakeFiles_h
|
||||
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include "cm_jsoncpp_value.h"
|
||||
|
||||
class cmFileAPI;
|
||||
|
||||
extern Json::Value cmFileAPICMakeFilesDump(cmFileAPI& fileAPI,
|
||||
unsigned long version);
|
||||
|
||||
#endif
|
||||
105
Source/cmFileAPICache.cxx
Normal file
105
Source/cmFileAPICache.cxx
Normal file
@@ -0,0 +1,105 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#include "cmFileAPICache.h"
|
||||
|
||||
#include "cmFileAPI.h"
|
||||
#include "cmState.h"
|
||||
#include "cmake.h"
|
||||
|
||||
#include "cm_jsoncpp_value.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
class Cache
|
||||
{
|
||||
cmFileAPI& FileAPI;
|
||||
unsigned long Version;
|
||||
cmState* State;
|
||||
|
||||
Json::Value DumpEntries();
|
||||
Json::Value DumpEntry(std::string const& name);
|
||||
Json::Value DumpEntryProperties(std::string const& name);
|
||||
Json::Value DumpEntryProperty(std::string const& name,
|
||||
std::string const& prop);
|
||||
|
||||
public:
|
||||
Cache(cmFileAPI& fileAPI, unsigned long version);
|
||||
Json::Value Dump();
|
||||
};
|
||||
|
||||
Cache::Cache(cmFileAPI& fileAPI, unsigned long version)
|
||||
: FileAPI(fileAPI)
|
||||
, Version(version)
|
||||
, State(this->FileAPI.GetCMakeInstance()->GetState())
|
||||
{
|
||||
static_cast<void>(this->Version);
|
||||
}
|
||||
|
||||
Json::Value Cache::Dump()
|
||||
{
|
||||
Json::Value cache = Json::objectValue;
|
||||
cache["entries"] = DumpEntries();
|
||||
return cache;
|
||||
}
|
||||
|
||||
Json::Value Cache::DumpEntries()
|
||||
{
|
||||
Json::Value entries = Json::arrayValue;
|
||||
|
||||
std::vector<std::string> names = this->State->GetCacheEntryKeys();
|
||||
std::sort(names.begin(), names.end());
|
||||
|
||||
for (std::string const& name : names) {
|
||||
entries.append(this->DumpEntry(name));
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
Json::Value Cache::DumpEntry(std::string const& name)
|
||||
{
|
||||
Json::Value entry = Json::objectValue;
|
||||
entry["name"] = name;
|
||||
entry["type"] =
|
||||
cmState::CacheEntryTypeToString(this->State->GetCacheEntryType(name));
|
||||
entry["value"] = this->State->GetCacheEntryValue(name);
|
||||
|
||||
Json::Value properties = this->DumpEntryProperties(name);
|
||||
if (!properties.empty()) {
|
||||
entry["properties"] = std::move(properties);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
Json::Value Cache::DumpEntryProperties(std::string const& name)
|
||||
{
|
||||
Json::Value properties = Json::arrayValue;
|
||||
std::vector<std::string> props =
|
||||
this->State->GetCacheEntryPropertyList(name);
|
||||
std::sort(props.begin(), props.end());
|
||||
for (std::string const& prop : props) {
|
||||
properties.append(this->DumpEntryProperty(name, prop));
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
Json::Value Cache::DumpEntryProperty(std::string const& name,
|
||||
std::string const& prop)
|
||||
{
|
||||
Json::Value property = Json::objectValue;
|
||||
property["name"] = prop;
|
||||
property["value"] = this->State->GetCacheEntryProperty(name, prop);
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value cmFileAPICacheDump(cmFileAPI& fileAPI, unsigned long version)
|
||||
{
|
||||
Cache cache(fileAPI, version);
|
||||
return cache.Dump();
|
||||
}
|
||||
15
Source/cmFileAPICache.h
Normal file
15
Source/cmFileAPICache.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#ifndef cmFileAPICache_h
|
||||
#define cmFileAPICache_h
|
||||
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include "cm_jsoncpp_value.h"
|
||||
|
||||
class cmFileAPI;
|
||||
|
||||
extern Json::Value cmFileAPICacheDump(cmFileAPI& fileAPI,
|
||||
unsigned long version);
|
||||
|
||||
#endif
|
||||
1247
Source/cmFileAPICodemodel.cxx
Normal file
1247
Source/cmFileAPICodemodel.cxx
Normal file
File diff suppressed because it is too large
Load Diff
15
Source/cmFileAPICodemodel.h
Normal file
15
Source/cmFileAPICodemodel.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#ifndef cmFileAPICodemodel_h
|
||||
#define cmFileAPICodemodel_h
|
||||
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include "cm_jsoncpp_value.h"
|
||||
|
||||
class cmFileAPI;
|
||||
|
||||
extern Json::Value cmFileAPICodemodelDump(cmFileAPI& fileAPI,
|
||||
unsigned long version);
|
||||
|
||||
#endif
|
||||
@@ -112,6 +112,15 @@ cmGlobalGenerator::~cmGlobalGenerator()
|
||||
delete this->ExtraGenerator;
|
||||
}
|
||||
|
||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||
Json::Value cmGlobalGenerator::GetJson() const
|
||||
{
|
||||
Json::Value generator = Json::objectValue;
|
||||
generator["name"] = this->GetName();
|
||||
return generator;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool cmGlobalGenerator::SetGeneratorInstance(std::string const& i,
|
||||
cmMakefile* mf)
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||
# include "cmFileLockPool.h"
|
||||
# include "cm_jsoncpp_value.h"
|
||||
#endif
|
||||
|
||||
#define CMAKE_DIRECTORY_ID_SEP "::@"
|
||||
@@ -70,6 +71,11 @@ public:
|
||||
return codecvt::None;
|
||||
}
|
||||
|
||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||
/** Get a JSON object describing the generator. */
|
||||
virtual Json::Value GetJson() const;
|
||||
#endif
|
||||
|
||||
/** Tell the generator about the target system. */
|
||||
virtual bool SetSystemName(std::string const&, cmMakefile*) { return true; }
|
||||
|
||||
|
||||
@@ -254,6 +254,15 @@ cmLocalGenerator* cmGlobalVisualStudio7Generator::CreateLocalGenerator(
|
||||
return lg;
|
||||
}
|
||||
|
||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||
Json::Value cmGlobalVisualStudio7Generator::GetJson() const
|
||||
{
|
||||
Json::Value generator = this->cmGlobalVisualStudioGenerator::GetJson();
|
||||
generator["platform"] = this->GetPlatformName();
|
||||
return generator;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::string const& cmGlobalVisualStudio7Generator::GetPlatformName() const
|
||||
{
|
||||
if (!this->GeneratorPlatform.empty()) {
|
||||
|
||||
@@ -28,6 +28,10 @@ public:
|
||||
///! Create a local generator appropriate to this Global Generator
|
||||
cmLocalGenerator* CreateLocalGenerator(cmMakefile* mf) override;
|
||||
|
||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||
Json::Value GetJson() const override;
|
||||
#endif
|
||||
|
||||
bool SetSystemName(std::string const& s, cmMakefile* mf) override;
|
||||
|
||||
bool SetGeneratorPlatform(std::string const& p, cmMakefile* mf) override;
|
||||
|
||||
@@ -66,6 +66,12 @@ bool cmStateSnapshot::IsValid() const
|
||||
: false;
|
||||
}
|
||||
|
||||
cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectory() const
|
||||
{
|
||||
return cmStateSnapshot(this->State,
|
||||
this->Position->BuildSystemDirectory->DirectoryEnd);
|
||||
}
|
||||
|
||||
cmStateSnapshot cmStateSnapshot::GetBuildsystemDirectoryParent() const
|
||||
{
|
||||
cmStateSnapshot snapshot;
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
std::vector<cmStateSnapshot> GetChildren();
|
||||
|
||||
bool IsValid() const;
|
||||
cmStateSnapshot GetBuildsystemDirectory() const;
|
||||
cmStateSnapshot GetBuildsystemDirectoryParent() const;
|
||||
cmStateSnapshot GetCallStackParent() const;
|
||||
cmStateSnapshot GetCallStackBottom() const;
|
||||
|
||||
@@ -23,12 +23,12 @@ public:
|
||||
const std::string& formatString,
|
||||
bool utcFlag);
|
||||
|
||||
private:
|
||||
time_t CreateUtcTimeTFromTm(struct tm& timeStruct) const;
|
||||
|
||||
std::string CreateTimestampFromTimeT(time_t timeT, std::string formatString,
|
||||
bool utcFlag) const;
|
||||
|
||||
private:
|
||||
time_t CreateUtcTimeTFromTm(struct tm& timeStruct) const;
|
||||
|
||||
std::string AddTimestampComponent(char flag, struct tm& timeStruct,
|
||||
time_t timeT) const;
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||
# include "cm_jsoncpp_writer.h"
|
||||
|
||||
# include "cmFileAPI.h"
|
||||
# include "cmGraphVizWriter.h"
|
||||
# include "cmVariableWatch.h"
|
||||
# include <unordered_map>
|
||||
@@ -1443,6 +1444,11 @@ int cmake::ActualConfigure()
|
||||
this->TruncateOutputLog("CMakeError.log");
|
||||
}
|
||||
|
||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||
this->FileAPI = cm::make_unique<cmFileAPI>(this);
|
||||
this->FileAPI->ReadQueries();
|
||||
#endif
|
||||
|
||||
// actually do the configure
|
||||
this->GlobalGenerator->Configure();
|
||||
// Before saving the cache
|
||||
@@ -1682,6 +1688,10 @@ int cmake::Generate()
|
||||
// for the Visual Studio and Xcode generators.)
|
||||
this->SaveCache(this->GetHomeOutputDirectory());
|
||||
|
||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||
this->FileAPI->WriteReplies();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include <map>
|
||||
#include <memory> // IWYU pragma: keep
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
@@ -21,6 +22,7 @@
|
||||
#endif
|
||||
|
||||
class cmExternalMakefileProjectGeneratorFactory;
|
||||
class cmFileAPI;
|
||||
class cmFileTimeComparison;
|
||||
class cmGlobalGenerator;
|
||||
class cmGlobalGeneratorFactory;
|
||||
@@ -528,6 +530,7 @@ private:
|
||||
|
||||
#if defined(CMAKE_BUILD_WITH_CMAKE)
|
||||
cmVariableWatch* VariableWatch;
|
||||
std::unique_ptr<cmFileAPI> FileAPI;
|
||||
#endif
|
||||
|
||||
cmState* State;
|
||||
|
||||
@@ -66,6 +66,9 @@ function(add_RunCMake_test_group test types)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
# Some tests use python for extra checks.
|
||||
find_package(PythonInterp QUIET)
|
||||
|
||||
if(XCODE_VERSION AND "${XCODE_VERSION}" VERSION_LESS 6.1)
|
||||
set(Swift_ARGS -DXCODE_BELOW_6_1=1)
|
||||
endif()
|
||||
@@ -155,6 +158,7 @@ add_RunCMake_test(DisallowedCommands)
|
||||
add_RunCMake_test(ExternalData)
|
||||
add_RunCMake_test(FeatureSummary)
|
||||
add_RunCMake_test(FPHSA)
|
||||
add_RunCMake_test(FileAPI -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE})
|
||||
add_RunCMake_test(FindBoost)
|
||||
add_RunCMake_test(FindLua)
|
||||
add_RunCMake_test(FindOpenGL)
|
||||
|
||||
3
Tests/RunCMake/FileAPI/CMakeLists.txt
Normal file
3
Tests/RunCMake/FileAPI/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
project(${RunCMake_TEST} NONE)
|
||||
include(${RunCMake_TEST}.cmake)
|
||||
68
Tests/RunCMake/FileAPI/ClientStateful-check.cmake
Normal file
68
Tests/RunCMake/FileAPI/ClientStateful-check.cmake
Normal file
@@ -0,0 +1,68 @@
|
||||
set(expect
|
||||
query
|
||||
query/client-client-member
|
||||
query/client-client-member/query.json
|
||||
query/client-empty-array
|
||||
query/client-empty-array/query.json
|
||||
query/client-empty-object
|
||||
query/client-empty-object/query.json
|
||||
query/client-json-bad-root
|
||||
query/client-json-bad-root/query.json
|
||||
query/client-json-empty
|
||||
query/client-json-empty/query.json
|
||||
query/client-json-extra
|
||||
query/client-json-extra/query.json
|
||||
query/client-not-file
|
||||
query/client-not-file/query.json
|
||||
query/client-request-array-negative-major-version
|
||||
query/client-request-array-negative-major-version/query.json
|
||||
query/client-request-array-negative-minor-version
|
||||
query/client-request-array-negative-minor-version/query.json
|
||||
query/client-request-array-negative-version
|
||||
query/client-request-array-negative-version/query.json
|
||||
query/client-request-array-no-major-version
|
||||
query/client-request-array-no-major-version/query.json
|
||||
query/client-request-array-no-supported-version
|
||||
query/client-request-array-no-supported-version-among
|
||||
query/client-request-array-no-supported-version-among/query.json
|
||||
query/client-request-array-no-supported-version/query.json
|
||||
query/client-request-array-version-1
|
||||
query/client-request-array-version-1-1
|
||||
query/client-request-array-version-1-1/query.json
|
||||
query/client-request-array-version-1/query.json
|
||||
query/client-request-array-version-2
|
||||
query/client-request-array-version-2/query.json
|
||||
query/client-request-negative-major-version
|
||||
query/client-request-negative-major-version/query.json
|
||||
query/client-request-negative-minor-version
|
||||
query/client-request-negative-minor-version/query.json
|
||||
query/client-request-negative-version
|
||||
query/client-request-negative-version/query.json
|
||||
query/client-request-no-major-version
|
||||
query/client-request-no-major-version/query.json
|
||||
query/client-request-no-version
|
||||
query/client-request-no-version/query.json
|
||||
query/client-request-version-1
|
||||
query/client-request-version-1-1
|
||||
query/client-request-version-1-1/query.json
|
||||
query/client-request-version-1/query.json
|
||||
query/client-request-version-2
|
||||
query/client-request-version-2/query.json
|
||||
query/client-requests-bad
|
||||
query/client-requests-bad/query.json
|
||||
query/client-requests-empty
|
||||
query/client-requests-empty/query.json
|
||||
query/client-requests-not-kinded
|
||||
query/client-requests-not-kinded/query.json
|
||||
query/client-requests-not-objects
|
||||
query/client-requests-not-objects/query.json
|
||||
query/client-requests-unknown
|
||||
query/client-requests-unknown/query.json
|
||||
reply
|
||||
reply/__test-v1-[0-9a-f]+.json
|
||||
reply/__test-v2-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(ClientStateful)
|
||||
258
Tests/RunCMake/FileAPI/ClientStateful-check.py
Normal file
258
Tests/RunCMake/FileAPI/ClientStateful-check.py
Normal file
@@ -0,0 +1,258 @@
|
||||
from check_index import *
|
||||
|
||||
def check_reply(q):
|
||||
assert is_dict(q)
|
||||
assert sorted(q.keys()) == [
|
||||
"client-client-member",
|
||||
"client-empty-array",
|
||||
"client-empty-object",
|
||||
"client-json-bad-root",
|
||||
"client-json-empty",
|
||||
"client-json-extra",
|
||||
"client-not-file",
|
||||
"client-request-array-negative-major-version",
|
||||
"client-request-array-negative-minor-version",
|
||||
"client-request-array-negative-version",
|
||||
"client-request-array-no-major-version",
|
||||
"client-request-array-no-supported-version",
|
||||
"client-request-array-no-supported-version-among",
|
||||
"client-request-array-version-1",
|
||||
"client-request-array-version-1-1",
|
||||
"client-request-array-version-2",
|
||||
"client-request-negative-major-version",
|
||||
"client-request-negative-minor-version",
|
||||
"client-request-negative-version",
|
||||
"client-request-no-major-version",
|
||||
"client-request-no-version",
|
||||
"client-request-version-1",
|
||||
"client-request-version-1-1",
|
||||
"client-request-version-2",
|
||||
"client-requests-bad",
|
||||
"client-requests-empty",
|
||||
"client-requests-not-kinded",
|
||||
"client-requests-not-objects",
|
||||
"client-requests-unknown",
|
||||
]
|
||||
expected = [
|
||||
(check_query_client_member, "client-client-member"),
|
||||
(check_query_empty_array, "client-empty-array"),
|
||||
(check_query_empty_object, "client-empty-object"),
|
||||
(check_query_json_bad_root, "client-json-bad-root"),
|
||||
(check_query_json_empty, "client-json-empty"),
|
||||
(check_query_json_extra, "client-json-extra"),
|
||||
(check_query_not_file, "client-not-file"),
|
||||
(check_query_requests_bad, "client-requests-bad"),
|
||||
(check_query_requests_empty, "client-requests-empty"),
|
||||
(check_query_requests_not_kinded, "client-requests-not-kinded"),
|
||||
(check_query_requests_not_objects, "client-requests-not-objects"),
|
||||
(check_query_requests_unknown, "client-requests-unknown"),
|
||||
]
|
||||
for (f, k) in expected:
|
||||
assert is_dict(q[k])
|
||||
assert sorted(q[k].keys()) == ["query.json"]
|
||||
f(q[k]["query.json"])
|
||||
expected = [
|
||||
(check_query_response_array_negative_major_version, "client-request-array-negative-major-version"),
|
||||
(check_query_response_array_negative_minor_version, "client-request-array-negative-minor-version"),
|
||||
(check_query_response_array_negative_version, "client-request-array-negative-version"),
|
||||
(check_query_response_array_no_major_version, "client-request-array-no-major-version"),
|
||||
(check_query_response_array_no_supported_version, "client-request-array-no-supported-version"),
|
||||
(check_query_response_array_no_supported_version_among, "client-request-array-no-supported-version-among"),
|
||||
(check_query_response_array_version_1, "client-request-array-version-1"),
|
||||
(check_query_response_array_version_1_1, "client-request-array-version-1-1"),
|
||||
(check_query_response_array_version_2, "client-request-array-version-2"),
|
||||
(check_query_response_negative_major_version, "client-request-negative-major-version"),
|
||||
(check_query_response_negative_minor_version, "client-request-negative-minor-version"),
|
||||
(check_query_response_negative_version, "client-request-negative-version"),
|
||||
(check_query_response_no_major_version, "client-request-no-major-version"),
|
||||
(check_query_response_no_version, "client-request-no-version"),
|
||||
(check_query_response_version_1, "client-request-version-1"),
|
||||
(check_query_response_version_1_1, "client-request-version-1-1"),
|
||||
(check_query_response_version_2, "client-request-version-2"),
|
||||
]
|
||||
for (f, k) in expected:
|
||||
assert is_dict(q[k])
|
||||
assert sorted(q[k].keys()) == ["query.json"]
|
||||
assert is_dict(q[k]["query.json"])
|
||||
assert sorted(q[k]["query.json"].keys()) == ["requests", "responses"]
|
||||
r = q[k]["query.json"]["requests"]
|
||||
assert is_list(r)
|
||||
assert len(r) == 1
|
||||
assert is_dict(r[0])
|
||||
assert r[0]["kind"] == "__test"
|
||||
r = q[k]["query.json"]["responses"]
|
||||
assert is_list(r)
|
||||
assert len(r) == 1
|
||||
assert is_dict(r[0])
|
||||
f(r[0])
|
||||
|
||||
def check_query_client_member(q):
|
||||
assert is_dict(q)
|
||||
assert sorted(q.keys()) == ["client", "responses"]
|
||||
assert is_dict(q["client"])
|
||||
assert sorted(q["client"].keys()) == []
|
||||
check_error(q["responses"], "'requests' member missing")
|
||||
|
||||
def check_query_empty_array(q):
|
||||
check_error(q, "query root is not an object")
|
||||
|
||||
def check_query_empty_object(q):
|
||||
assert is_dict(q)
|
||||
assert sorted(q.keys()) == ["responses"]
|
||||
check_error(q["responses"], "'requests' member missing")
|
||||
|
||||
def check_query_json_bad_root(q):
|
||||
check_error_re(q, "A valid JSON document must be either an array or an object value")
|
||||
|
||||
def check_query_json_empty(q):
|
||||
check_error_re(q, "value, object or array expected")
|
||||
|
||||
def check_query_json_extra(q):
|
||||
check_error_re(q, "Extra non-whitespace after JSON value")
|
||||
|
||||
def check_query_not_file(q):
|
||||
check_error_re(q, "failed to read from file")
|
||||
|
||||
def check_query_requests_bad(q):
|
||||
assert is_dict(q)
|
||||
assert sorted(q.keys()) == ["requests", "responses"]
|
||||
r = q["requests"]
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == []
|
||||
check_error(q["responses"], "'requests' member is not an array")
|
||||
|
||||
def check_query_requests_empty(q):
|
||||
assert is_dict(q)
|
||||
assert sorted(q.keys()) == ["requests", "responses"]
|
||||
r = q["requests"]
|
||||
assert is_list(r)
|
||||
assert len(r) == 0
|
||||
r = q["responses"]
|
||||
assert is_list(r)
|
||||
assert len(r) == 0
|
||||
|
||||
def check_query_requests_not_kinded(q):
|
||||
assert is_dict(q)
|
||||
assert sorted(q.keys()) == ["requests", "responses"]
|
||||
r = q["requests"]
|
||||
assert is_list(r)
|
||||
assert len(r) == 4
|
||||
assert is_dict(r[0])
|
||||
assert sorted(r[0].keys()) == []
|
||||
assert is_dict(r[1])
|
||||
assert sorted(r[1].keys()) == ["kind"]
|
||||
assert is_dict(r[1]["kind"])
|
||||
assert is_dict(r[2])
|
||||
assert sorted(r[2].keys()) == ["kind"]
|
||||
assert is_list(r[2]["kind"])
|
||||
assert is_dict(r[3])
|
||||
assert sorted(r[3].keys()) == ["kind"]
|
||||
assert is_int(r[3]["kind"])
|
||||
r = q["responses"]
|
||||
assert is_list(r)
|
||||
assert len(r) == 4
|
||||
check_error(r[0], "'kind' member missing")
|
||||
check_error(r[1], "'kind' member is not a string")
|
||||
check_error(r[2], "'kind' member is not a string")
|
||||
check_error(r[3], "'kind' member is not a string")
|
||||
|
||||
def check_query_requests_not_objects(q):
|
||||
assert is_dict(q)
|
||||
assert sorted(q.keys()) == ["requests", "responses"]
|
||||
r = q["requests"]
|
||||
assert is_list(r)
|
||||
assert len(r) == 3
|
||||
assert is_int(r[0])
|
||||
assert is_string(r[1])
|
||||
assert is_list(r[2])
|
||||
r = q["responses"]
|
||||
assert is_list(r)
|
||||
assert len(r) == 3
|
||||
check_error(r[0], "request is not an object")
|
||||
check_error(r[1], "request is not an object")
|
||||
check_error(r[2], "request is not an object")
|
||||
|
||||
def check_query_requests_unknown(q):
|
||||
assert is_dict(q)
|
||||
assert sorted(q.keys()) == ["requests", "responses"]
|
||||
r = q["requests"]
|
||||
assert is_list(r)
|
||||
assert len(r) == 3
|
||||
assert is_dict(r[0])
|
||||
assert sorted(r[0].keys()) == ["kind"]
|
||||
assert r[0]["kind"] == "unknownC"
|
||||
assert is_dict(r[1])
|
||||
assert sorted(r[1].keys()) == ["kind"]
|
||||
assert r[1]["kind"] == "unknownB"
|
||||
assert is_dict(r[2])
|
||||
assert sorted(r[2].keys()) == ["kind"]
|
||||
assert r[2]["kind"] == "unknownA"
|
||||
r = q["responses"]
|
||||
assert is_list(r)
|
||||
assert len(r) == 3
|
||||
check_error(r[0], "unknown request kind 'unknownC'")
|
||||
check_error(r[1], "unknown request kind 'unknownB'")
|
||||
check_error(r[2], "unknown request kind 'unknownA'")
|
||||
|
||||
def check_query_response_array_negative_major_version(r):
|
||||
check_error(r, "'version' object 'major' member is not a non-negative integer")
|
||||
|
||||
def check_query_response_array_negative_minor_version(r):
|
||||
check_error(r, "'version' object 'minor' member is not a non-negative integer")
|
||||
|
||||
def check_query_response_array_negative_version(r):
|
||||
check_error(r, "'version' array entry is not a non-negative integer or object")
|
||||
|
||||
def check_query_response_array_no_major_version(r):
|
||||
check_error(r, "'version' object 'major' member missing")
|
||||
|
||||
def check_query_response_array_no_supported_version(r):
|
||||
check_error(r, "no supported version specified")
|
||||
|
||||
def check_query_response_array_no_supported_version_among(r):
|
||||
check_error(r, "no supported version specified among: 4.0 3.0")
|
||||
|
||||
def check_query_response_array_version_1(r):
|
||||
check_index__test(r, 1, 3)
|
||||
|
||||
def check_query_response_array_version_1_1(r):
|
||||
check_index__test(r, 1, 3) # always uses latest minor version
|
||||
|
||||
def check_query_response_array_version_2(r):
|
||||
check_index__test(r, 2, 0)
|
||||
|
||||
def check_query_response_negative_major_version(r):
|
||||
check_error(r, "'version' object 'major' member is not a non-negative integer")
|
||||
|
||||
def check_query_response_negative_minor_version(r):
|
||||
check_error(r, "'version' object 'minor' member is not a non-negative integer")
|
||||
|
||||
def check_query_response_negative_version(r):
|
||||
check_error(r, "'version' member is not a non-negative integer, object, or array")
|
||||
|
||||
def check_query_response_no_major_version(r):
|
||||
check_error(r, "'version' object 'major' member missing")
|
||||
|
||||
def check_query_response_no_version(r):
|
||||
check_error(r, "'version' member missing")
|
||||
|
||||
def check_query_response_version_1(r):
|
||||
check_index__test(r, 1, 3)
|
||||
|
||||
def check_query_response_version_1_1(r):
|
||||
check_index__test(r, 1, 3) # always uses latest minor version
|
||||
|
||||
def check_query_response_version_2(r):
|
||||
check_index__test(r, 2, 0)
|
||||
|
||||
def check_objects(o):
|
||||
assert is_list(o)
|
||||
assert len(o) == 2
|
||||
check_index__test(o[0], 1, 3)
|
||||
check_index__test(o[1], 2, 0)
|
||||
|
||||
assert is_dict(index)
|
||||
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
|
||||
check_cmake(index["cmake"])
|
||||
check_reply(index["reply"])
|
||||
check_objects(index["objects"])
|
||||
73
Tests/RunCMake/FileAPI/ClientStateful-prep.cmake
Normal file
73
Tests/RunCMake/FileAPI/ClientStateful-prep.cmake
Normal file
@@ -0,0 +1,73 @@
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-client-member/query.json" [[{ "client": {} }]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-empty-array/query.json" "[]")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-empty-object/query.json" "{}")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-json-bad-root/query.json" [["invalid root"]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-json-empty/query.json" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-json-extra/query.json" "{}x")
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-not-file/query.json")
|
||||
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-bad/query.json" [[{ "requests": {} }]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-empty/query.json" [[{ "requests": [] }]])
|
||||
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-not-objects/query.json" [[
|
||||
{ "requests": [ 0, "", [] ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-not-kinded/query.json" [[
|
||||
{ "requests": [ {}, { "kind": {} }, { "kind": [] }, { "kind": 0 } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-requests-unknown/query.json" [[
|
||||
{ "requests": [ { "kind": "unknownC" }, { "kind": "unknownB" }, { "kind": "unknownA" } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-no-version/query.json" [[
|
||||
{ "requests": [ { "kind": "__test" } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-negative-version/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : -1 } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-no-major-version/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : {} } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-negative-major-version/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : { "major": -1 } } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-negative-minor-version/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : { "major": 0, "minor": -1 } } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-negative-version/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : [ 1, -1 ] } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-no-major-version/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : [ 1, {} ] } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-negative-major-version/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : [ 1, { "major": -1 } ] } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-negative-minor-version/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : [ 1, { "major": 0, "minor": -1 } ] } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-no-supported-version/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : [] } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-no-supported-version-among/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : [4, 3] } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-version-1/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : 1 } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-version-1-1/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : { "major": 1, "minor": 1 } } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-version-2/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : { "major": 2 } } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-version-1/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : [3, 1] } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-version-1-1/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : [3, { "major": 1, "minor": 1 }, 2 ] } ] }
|
||||
]])
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-request-array-version-2/query.json" [[
|
||||
{ "requests": [ { "kind": "__test", "version" : [3, { "major": 2 } ] } ] }
|
||||
]])
|
||||
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
|
||||
0
Tests/RunCMake/FileAPI/ClientStateful.cmake
Normal file
0
Tests/RunCMake/FileAPI/ClientStateful.cmake
Normal file
15
Tests/RunCMake/FileAPI/ClientStateless-check.cmake
Normal file
15
Tests/RunCMake/FileAPI/ClientStateless-check.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
set(expect
|
||||
query
|
||||
query/client-foo
|
||||
query/client-foo/__test-v1
|
||||
query/client-foo/__test-v2
|
||||
query/client-foo/__test-v3
|
||||
query/client-foo/unknown
|
||||
reply
|
||||
reply/__test-v1-[0-9a-f]+.json
|
||||
reply/__test-v2-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(ClientStateless)
|
||||
26
Tests/RunCMake/FileAPI/ClientStateless-check.py
Normal file
26
Tests/RunCMake/FileAPI/ClientStateless-check.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from check_index import *
|
||||
|
||||
def check_reply(r):
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == ["client-foo"]
|
||||
check_reply_client_foo(r["client-foo"])
|
||||
|
||||
def check_reply_client_foo(r):
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "unknown"]
|
||||
check_index__test(r["__test-v1"], 1, 3)
|
||||
check_index__test(r["__test-v2"], 2, 0)
|
||||
check_error(r["__test-v3"], "unknown query file")
|
||||
check_error(r["unknown"], "unknown query file")
|
||||
|
||||
def check_objects(o):
|
||||
assert is_list(o)
|
||||
assert len(o) == 2
|
||||
check_index__test(o[0], 1, 3)
|
||||
check_index__test(o[1], 2, 0)
|
||||
|
||||
assert is_dict(index)
|
||||
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
|
||||
check_cmake(index["cmake"])
|
||||
check_reply(index["reply"])
|
||||
check_objects(index["objects"])
|
||||
5
Tests/RunCMake/FileAPI/ClientStateless-prep.cmake
Normal file
5
Tests/RunCMake/FileAPI/ClientStateless-prep.cmake
Normal file
@@ -0,0 +1,5 @@
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v1" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v2" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v3" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/unknown" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
|
||||
0
Tests/RunCMake/FileAPI/ClientStateless.cmake
Normal file
0
Tests/RunCMake/FileAPI/ClientStateless.cmake
Normal file
20
Tests/RunCMake/FileAPI/DuplicateStateless-check.cmake
Normal file
20
Tests/RunCMake/FileAPI/DuplicateStateless-check.cmake
Normal file
@@ -0,0 +1,20 @@
|
||||
set(expect
|
||||
query
|
||||
query/__test-v1
|
||||
query/__test-v2
|
||||
query/__test-v3
|
||||
query/client-foo
|
||||
query/client-foo/__test-v1
|
||||
query/client-foo/__test-v2
|
||||
query/client-foo/__test-v3
|
||||
query/client-foo/unknown
|
||||
query/query.json
|
||||
query/unknown
|
||||
reply
|
||||
reply/__test-v1-[0-9a-f]+.json
|
||||
reply/__test-v2-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(DuplicateStateless)
|
||||
31
Tests/RunCMake/FileAPI/DuplicateStateless-check.py
Normal file
31
Tests/RunCMake/FileAPI/DuplicateStateless-check.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from check_index import *
|
||||
|
||||
def check_reply(r):
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "client-foo", "query.json", "unknown"]
|
||||
check_index__test(r["__test-v1"], 1, 3)
|
||||
check_index__test(r["__test-v2"], 2, 0)
|
||||
check_error(r["__test-v3"], "unknown query file")
|
||||
check_reply_client_foo(r["client-foo"])
|
||||
check_error(r["query.json"], "unknown query file")
|
||||
check_error(r["unknown"], "unknown query file")
|
||||
|
||||
def check_reply_client_foo(r):
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "unknown"]
|
||||
check_index__test(r["__test-v1"], 1, 3)
|
||||
check_index__test(r["__test-v2"], 2, 0)
|
||||
check_error(r["__test-v3"], "unknown query file")
|
||||
check_error(r["unknown"], "unknown query file")
|
||||
|
||||
def check_objects(o):
|
||||
assert is_list(o)
|
||||
assert len(o) == 2
|
||||
check_index__test(o[0], 1, 3)
|
||||
check_index__test(o[1], 2, 0)
|
||||
|
||||
assert is_dict(index)
|
||||
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
|
||||
check_cmake(index["cmake"])
|
||||
check_reply(index["reply"])
|
||||
check_objects(index["objects"])
|
||||
10
Tests/RunCMake/FileAPI/DuplicateStateless-prep.cmake
Normal file
10
Tests/RunCMake/FileAPI/DuplicateStateless-prep.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v1" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v2" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v3" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/query.json" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/unknown" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v1" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v2" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v3" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/unknown" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
|
||||
0
Tests/RunCMake/FileAPI/DuplicateStateless.cmake
Normal file
0
Tests/RunCMake/FileAPI/DuplicateStateless.cmake
Normal file
8
Tests/RunCMake/FileAPI/Empty-check.cmake
Normal file
8
Tests/RunCMake/FileAPI/Empty-check.cmake
Normal file
@@ -0,0 +1,8 @@
|
||||
set(expect
|
||||
query
|
||||
reply
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(Empty)
|
||||
15
Tests/RunCMake/FileAPI/Empty-check.py
Normal file
15
Tests/RunCMake/FileAPI/Empty-check.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from check_index import *
|
||||
|
||||
def check_reply(r):
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == []
|
||||
|
||||
def check_objects(o):
|
||||
assert is_list(o)
|
||||
assert len(o) == 0
|
||||
|
||||
assert is_dict(index)
|
||||
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
|
||||
check_cmake(index["cmake"])
|
||||
check_reply(index["reply"])
|
||||
check_objects(index["objects"])
|
||||
1
Tests/RunCMake/FileAPI/Empty-prep.cmake
Normal file
1
Tests/RunCMake/FileAPI/Empty-prep.cmake
Normal file
@@ -0,0 +1 @@
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query")
|
||||
0
Tests/RunCMake/FileAPI/Empty.cmake
Normal file
0
Tests/RunCMake/FileAPI/Empty.cmake
Normal file
9
Tests/RunCMake/FileAPI/EmptyClient-check.cmake
Normal file
9
Tests/RunCMake/FileAPI/EmptyClient-check.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
set(expect
|
||||
query
|
||||
query/client-foo
|
||||
reply
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(EmptyClient)
|
||||
20
Tests/RunCMake/FileAPI/EmptyClient-check.py
Normal file
20
Tests/RunCMake/FileAPI/EmptyClient-check.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from check_index import *
|
||||
|
||||
def check_reply(r):
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == ["client-foo"]
|
||||
check_reply_client_foo(r["client-foo"])
|
||||
|
||||
def check_reply_client_foo(r):
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == []
|
||||
|
||||
def check_objects(o):
|
||||
assert is_list(o)
|
||||
assert len(o) == 0
|
||||
|
||||
assert is_dict(index)
|
||||
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
|
||||
check_cmake(index["cmake"])
|
||||
check_reply(index["reply"])
|
||||
check_objects(index["objects"])
|
||||
2
Tests/RunCMake/FileAPI/EmptyClient-prep.cmake
Normal file
2
Tests/RunCMake/FileAPI/EmptyClient-prep.cmake
Normal file
@@ -0,0 +1,2 @@
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
|
||||
0
Tests/RunCMake/FileAPI/EmptyClient.cmake
Normal file
0
Tests/RunCMake/FileAPI/EmptyClient.cmake
Normal file
16
Tests/RunCMake/FileAPI/MixedStateless-check.cmake
Normal file
16
Tests/RunCMake/FileAPI/MixedStateless-check.cmake
Normal file
@@ -0,0 +1,16 @@
|
||||
set(expect
|
||||
query
|
||||
query/__test-v1
|
||||
query/__test-v3
|
||||
query/client-foo
|
||||
query/client-foo/__test-v2
|
||||
query/client-foo/unknown
|
||||
query/query.json
|
||||
reply
|
||||
reply/__test-v1-[0-9a-f]+.json
|
||||
reply/__test-v2-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(MixedStateless)
|
||||
27
Tests/RunCMake/FileAPI/MixedStateless-check.py
Normal file
27
Tests/RunCMake/FileAPI/MixedStateless-check.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from check_index import *
|
||||
|
||||
def check_reply(r):
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == ["__test-v1", "__test-v3", "client-foo", "query.json"]
|
||||
check_index__test(r["__test-v1"], 1, 3)
|
||||
check_error(r["__test-v3"], "unknown query file")
|
||||
check_reply_client_foo(r["client-foo"])
|
||||
check_error(r["query.json"], "unknown query file")
|
||||
|
||||
def check_reply_client_foo(r):
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == ["__test-v2", "unknown"]
|
||||
check_index__test(r["__test-v2"], 2, 0)
|
||||
check_error(r["unknown"], "unknown query file")
|
||||
|
||||
def check_objects(o):
|
||||
assert is_list(o)
|
||||
assert len(o) == 2
|
||||
check_index__test(o[0], 1, 3)
|
||||
check_index__test(o[1], 2, 0)
|
||||
|
||||
assert is_dict(index)
|
||||
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
|
||||
check_cmake(index["cmake"])
|
||||
check_reply(index["reply"])
|
||||
check_objects(index["objects"])
|
||||
6
Tests/RunCMake/FileAPI/MixedStateless-prep.cmake
Normal file
6
Tests/RunCMake/FileAPI/MixedStateless-prep.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v1" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/__test-v2" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v3" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/query.json" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/unknown" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
|
||||
0
Tests/RunCMake/FileAPI/MixedStateless.cmake
Normal file
0
Tests/RunCMake/FileAPI/MixedStateless.cmake
Normal file
1
Tests/RunCMake/FileAPI/Nothing-check.cmake
Normal file
1
Tests/RunCMake/FileAPI/Nothing-check.cmake
Normal file
@@ -0,0 +1 @@
|
||||
check_api("^$")
|
||||
1
Tests/RunCMake/FileAPI/Nothing-prep.cmake
Normal file
1
Tests/RunCMake/FileAPI/Nothing-prep.cmake
Normal file
@@ -0,0 +1 @@
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1")
|
||||
0
Tests/RunCMake/FileAPI/Nothing.cmake
Normal file
0
Tests/RunCMake/FileAPI/Nothing.cmake
Normal file
58
Tests/RunCMake/FileAPI/RunCMakeTest.cmake
Normal file
58
Tests/RunCMake/FileAPI/RunCMakeTest.cmake
Normal file
@@ -0,0 +1,58 @@
|
||||
include(RunCMake)
|
||||
|
||||
# Function called in *-check.cmake scripts to check api files.
|
||||
function(check_api expect)
|
||||
file(GLOB_RECURSE actual
|
||||
LIST_DIRECTORIES TRUE
|
||||
RELATIVE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1
|
||||
${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/*
|
||||
)
|
||||
if(NOT "${actual}" MATCHES "${expect}")
|
||||
set(RunCMake_TEST_FAILED "API files:
|
||||
${actual}
|
||||
do not match what we expected:
|
||||
${expect}
|
||||
in directory:
|
||||
${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(check_python case)
|
||||
if(RunCMake_TEST_FAILED OR NOT PYTHON_EXECUTABLE)
|
||||
return()
|
||||
endif()
|
||||
file(GLOB index ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/index-*.json)
|
||||
execute_process(
|
||||
COMMAND ${PYTHON_EXECUTABLE} "${RunCMake_SOURCE_DIR}/${case}-check.py" "${index}"
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
ERROR_VARIABLE output
|
||||
)
|
||||
if(NOT result EQUAL 0)
|
||||
string(REPLACE "\n" "\n " output " ${output}")
|
||||
set(RunCMake_TEST_FAILED "Unexpected index:\n${output}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
run_cmake(Nothing)
|
||||
run_cmake(Empty)
|
||||
run_cmake(EmptyClient)
|
||||
run_cmake(Stale)
|
||||
run_cmake(SharedStateless)
|
||||
run_cmake(ClientStateless)
|
||||
run_cmake(MixedStateless)
|
||||
run_cmake(DuplicateStateless)
|
||||
run_cmake(ClientStateful)
|
||||
|
||||
function(run_object object)
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${object}-build)
|
||||
run_cmake(${object})
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
run_cmake_command(${object}-SharedStateless ${CMAKE_COMMAND} .)
|
||||
run_cmake_command(${object}-ClientStateless ${CMAKE_COMMAND} .)
|
||||
run_cmake_command(${object}-ClientStateful ${CMAKE_COMMAND} .)
|
||||
endfunction()
|
||||
|
||||
run_object(codemodel-v2)
|
||||
run_object(cache-v2)
|
||||
run_object(cmakeFiles-v1)
|
||||
15
Tests/RunCMake/FileAPI/SharedStateless-check.cmake
Normal file
15
Tests/RunCMake/FileAPI/SharedStateless-check.cmake
Normal file
@@ -0,0 +1,15 @@
|
||||
set(expect
|
||||
query
|
||||
query/__test-v1
|
||||
query/__test-v2
|
||||
query/__test-v3
|
||||
query/query.json
|
||||
query/unknown
|
||||
reply
|
||||
reply/__test-v1-[0-9a-f]+.json
|
||||
reply/__test-v2-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(SharedStateless)
|
||||
22
Tests/RunCMake/FileAPI/SharedStateless-check.py
Normal file
22
Tests/RunCMake/FileAPI/SharedStateless-check.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from check_index import *
|
||||
|
||||
def check_reply(r):
|
||||
assert is_dict(r)
|
||||
assert sorted(r.keys()) == ["__test-v1", "__test-v2", "__test-v3", "query.json", "unknown"]
|
||||
check_index__test(r["__test-v1"], 1, 3)
|
||||
check_index__test(r["__test-v2"], 2, 0)
|
||||
check_error(r["__test-v3"], "unknown query file")
|
||||
check_error(r["query.json"], "unknown query file")
|
||||
check_error(r["unknown"], "unknown query file")
|
||||
|
||||
def check_objects(o):
|
||||
assert is_list(o)
|
||||
assert len(o) == 2
|
||||
check_index__test(o[0], 1, 3)
|
||||
check_index__test(o[1], 2, 0)
|
||||
|
||||
assert is_dict(index)
|
||||
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
|
||||
check_cmake(index["cmake"])
|
||||
check_reply(index["reply"])
|
||||
check_objects(index["objects"])
|
||||
6
Tests/RunCMake/FileAPI/SharedStateless-prep.cmake
Normal file
6
Tests/RunCMake/FileAPI/SharedStateless-prep.cmake
Normal file
@@ -0,0 +1,6 @@
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v1" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v2" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/__test-v3" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/query.json" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/unknown" "")
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
|
||||
0
Tests/RunCMake/FileAPI/SharedStateless.cmake
Normal file
0
Tests/RunCMake/FileAPI/SharedStateless.cmake
Normal file
4
Tests/RunCMake/FileAPI/Stale-check.cmake
Normal file
4
Tests/RunCMake/FileAPI/Stale-check.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
set(expect
|
||||
reply
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
1
Tests/RunCMake/FileAPI/Stale-prep.cmake
Normal file
1
Tests/RunCMake/FileAPI/Stale-prep.cmake
Normal file
@@ -0,0 +1 @@
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/reply/object-to-be-deleted.json" "")
|
||||
0
Tests/RunCMake/FileAPI/Stale.cmake
Normal file
0
Tests/RunCMake/FileAPI/Stale.cmake
Normal file
10
Tests/RunCMake/FileAPI/alias/CMakeLists.txt
Normal file
10
Tests/RunCMake/FileAPI/alias/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
project(Alias)
|
||||
enable_language(CXX)
|
||||
|
||||
add_library(c_alias_lib ALIAS c_lib)
|
||||
add_executable(c_alias_exe ../empty.c)
|
||||
target_link_libraries(c_alias_exe PRIVATE c_alias_lib)
|
||||
|
||||
add_library(cxx_alias_lib ALIAS cxx_lib)
|
||||
add_executable(cxx_alias_exe ../empty.cxx)
|
||||
target_link_libraries(cxx_alias_exe PRIVATE cxx_alias_lib)
|
||||
11
Tests/RunCMake/FileAPI/cache-v2-ClientStateful-check.cmake
Normal file
11
Tests/RunCMake/FileAPI/cache-v2-ClientStateful-check.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
set(expect
|
||||
query
|
||||
query/client-foo
|
||||
query/client-foo/query.json
|
||||
reply
|
||||
reply/cache-v2-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(cache-v2)
|
||||
@@ -0,0 +1,4 @@
|
||||
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[
|
||||
{ "requests": [ { "kind": "cache", "version" : 2 } ] }
|
||||
]])
|
||||
11
Tests/RunCMake/FileAPI/cache-v2-ClientStateless-check.cmake
Normal file
11
Tests/RunCMake/FileAPI/cache-v2-ClientStateless-check.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
set(expect
|
||||
query
|
||||
query/client-foo
|
||||
query/client-foo/cache-v2
|
||||
reply
|
||||
reply/cache-v2-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(cache-v2)
|
||||
@@ -0,0 +1,2 @@
|
||||
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/cache-v2" "")
|
||||
10
Tests/RunCMake/FileAPI/cache-v2-SharedStateless-check.cmake
Normal file
10
Tests/RunCMake/FileAPI/cache-v2-SharedStateless-check.cmake
Normal file
@@ -0,0 +1,10 @@
|
||||
set(expect
|
||||
query
|
||||
query/cache-v2
|
||||
reply
|
||||
reply/cache-v2-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(cache-v2)
|
||||
@@ -0,0 +1,2 @@
|
||||
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/cache-v2" "")
|
||||
134
Tests/RunCMake/FileAPI/cache-v2-check.py
Normal file
134
Tests/RunCMake/FileAPI/cache-v2-check.py
Normal file
@@ -0,0 +1,134 @@
|
||||
from check_index import *
|
||||
|
||||
def check_objects(o):
|
||||
assert is_list(o)
|
||||
assert len(o) == 1
|
||||
check_index_object(o[0], "cache", 2, 0, check_object_cache)
|
||||
|
||||
def check_cache_entry(actual, expected):
|
||||
assert is_dict(actual)
|
||||
assert sorted(actual.keys()) == ["name", "properties", "type", "value"]
|
||||
|
||||
assert is_string(actual["type"], expected["type"])
|
||||
assert is_string(actual["value"], expected["value"])
|
||||
|
||||
def check_property(actual, expected):
|
||||
assert is_dict(actual)
|
||||
assert sorted(actual.keys()) == ["name", "value"]
|
||||
assert is_string(actual["value"], expected["value"])
|
||||
|
||||
check_list_match(lambda a, e: is_string(a["name"], e["name"]), actual["properties"], expected["properties"], check=check_property)
|
||||
|
||||
def check_object_cache(o):
|
||||
assert sorted(o.keys()) == ["entries", "kind", "version"]
|
||||
# The "kind" and "version" members are handled by check_index_object.
|
||||
check_list_match(lambda a, e: is_string(a["name"], e["name"]), o["entries"], [
|
||||
{
|
||||
"name": "CM_OPTION_BOOL",
|
||||
"type": "BOOL",
|
||||
"value": "OFF",
|
||||
"properties": [
|
||||
{
|
||||
"name": "HELPSTRING",
|
||||
"value": "Testing option()",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "CM_SET_BOOL",
|
||||
"type": "BOOL",
|
||||
"value": "ON",
|
||||
"properties": [
|
||||
{
|
||||
"name": "HELPSTRING",
|
||||
"value": "Testing set(CACHE BOOL)",
|
||||
},
|
||||
{
|
||||
"name": "ADVANCED",
|
||||
"value": "1",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "CM_SET_FILEPATH",
|
||||
"type": "FILEPATH",
|
||||
"value": "dir1/dir2/empty.txt",
|
||||
"properties": [
|
||||
{
|
||||
"name": "HELPSTRING",
|
||||
"value": "Testing set(CACHE FILEPATH)",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "CM_SET_PATH",
|
||||
"type": "PATH",
|
||||
"value": "dir1/dir2",
|
||||
"properties": [
|
||||
{
|
||||
"name": "HELPSTRING",
|
||||
"value": "Testing set(CACHE PATH)",
|
||||
},
|
||||
{
|
||||
"name": "ADVANCED",
|
||||
"value": "ON",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "CM_SET_STRING",
|
||||
"type": "STRING",
|
||||
"value": "test",
|
||||
"properties": [
|
||||
{
|
||||
"name": "HELPSTRING",
|
||||
"value": "Testing set(CACHE STRING)",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "CM_SET_STRINGS",
|
||||
"type": "STRING",
|
||||
"value": "1",
|
||||
"properties": [
|
||||
{
|
||||
"name": "HELPSTRING",
|
||||
"value": "Testing set(CACHE STRING) with STRINGS",
|
||||
},
|
||||
{
|
||||
"name": "STRINGS",
|
||||
"value": "1;2;3;4",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "CM_SET_INTERNAL",
|
||||
"type": "INTERNAL",
|
||||
"value": "int2",
|
||||
"properties": [
|
||||
{
|
||||
"name": "HELPSTRING",
|
||||
"value": "Testing set(CACHE INTERNAL)",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "CM_SET_TYPE",
|
||||
"type": "STRING",
|
||||
"value": "1",
|
||||
"properties": [
|
||||
{
|
||||
"name": "HELPSTRING",
|
||||
"value": "Testing set(CACHE INTERNAL) with set_property(TYPE)",
|
||||
},
|
||||
{
|
||||
"name": "ADVANCED",
|
||||
"value": "0",
|
||||
},
|
||||
],
|
||||
},
|
||||
], check=check_cache_entry, allow_extra=True)
|
||||
|
||||
assert is_dict(index)
|
||||
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
|
||||
check_objects(index["objects"])
|
||||
14
Tests/RunCMake/FileAPI/cache-v2.cmake
Normal file
14
Tests/RunCMake/FileAPI/cache-v2.cmake
Normal file
@@ -0,0 +1,14 @@
|
||||
option(CM_OPTION_BOOL "Testing option()" "OFF")
|
||||
set(CM_SET_BOOL "ON" CACHE BOOL "Testing set(CACHE BOOL)")
|
||||
mark_as_advanced(CM_SET_BOOL)
|
||||
set(CM_SET_FILEPATH "dir1/dir2/empty.txt" CACHE FILEPATH "Testing set(CACHE FILEPATH)")
|
||||
set(CM_SET_PATH "dir1/dir2" CACHE PATH "Testing set(CACHE PATH)")
|
||||
set_property(CACHE CM_SET_PATH PROPERTY ADVANCED ON)
|
||||
set(CM_SET_STRING "test" CACHE STRING "Testing set(CACHE STRING)")
|
||||
set(CM_SET_STRINGS "1" CACHE STRING "Testing set(CACHE STRING) with STRINGS")
|
||||
set_property(CACHE CM_SET_STRINGS PROPERTY STRINGS "1;2;3;4")
|
||||
set(CM_SET_INTERNAL "int" CACHE INTERNAL "Testing set(CACHE INTERNAL)")
|
||||
set_property(CACHE CM_SET_INTERNAL PROPERTY VALUE "int2")
|
||||
set(CM_SET_TYPE "1" CACHE INTERNAL "Testing set(CACHE INTERNAL) with set_property(TYPE)")
|
||||
set_property(CACHE CM_SET_TYPE PROPERTY TYPE "STRING")
|
||||
set_property(CACHE CM_SET_TYPE PROPERTY ADVANCED "0")
|
||||
163
Tests/RunCMake/FileAPI/check_index.py
Normal file
163
Tests/RunCMake/FileAPI/check_index.py
Normal file
@@ -0,0 +1,163 @@
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import re
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
unicode = str
|
||||
|
||||
def is_bool(x, val=None):
|
||||
return isinstance(x, bool) and (val is None or x == val)
|
||||
|
||||
def is_dict(x):
|
||||
return isinstance(x, dict)
|
||||
|
||||
def is_list(x):
|
||||
return isinstance(x, list)
|
||||
|
||||
def is_int(x, val=None):
|
||||
return (isinstance(x, int) or isinstance(x, long)) and (val is None or x == val)
|
||||
|
||||
def is_string(x, val=None):
|
||||
return (isinstance(x, str) or isinstance(x, unicode)) and (val is None or x == val)
|
||||
|
||||
def matches(s, pattern):
|
||||
return is_string(s) and bool(re.search(pattern, s))
|
||||
|
||||
def check_list_match(match, actual, expected, check=None, check_exception=None, missing_exception=None, extra_exception=None, allow_extra=False):
|
||||
"""
|
||||
Handle the common pattern of making sure every actual item "matches" some
|
||||
item in the expected list, and that neither list has extra items after
|
||||
matching is completed.
|
||||
|
||||
@param match: Callback to check if an actual item matches an expected
|
||||
item. Return True if the item matches, return False if the item doesn't
|
||||
match.
|
||||
@param actual: List of actual items to search.
|
||||
@param expected: List of expected items to match.
|
||||
@param check: Optional function to check that the actual item is valid by
|
||||
comparing it to the expected item.
|
||||
@param check_exception: Optional function that returns an argument to
|
||||
append to any exception thrown by the check function.
|
||||
@param missing_exception: Optional function that returns an argument to
|
||||
append to the exception thrown when an item is not found.
|
||||
@param extra_exception: Optional function that returns an argument to
|
||||
append to the exception thrown when an extra item is found.
|
||||
@param allow_extra: Optional parameter allowing there to be extra actual
|
||||
items after all the expected items have been found.
|
||||
"""
|
||||
assert is_list(actual)
|
||||
_actual = actual[:]
|
||||
for expected_item in expected:
|
||||
found = False
|
||||
for i, actual_item in enumerate(_actual):
|
||||
if match(actual_item, expected_item):
|
||||
if check:
|
||||
try:
|
||||
check(actual_item, expected_item)
|
||||
except BaseException as e:
|
||||
if check_exception:
|
||||
e.args += (check_exception(actual_item, expected_item),)
|
||||
raise
|
||||
found = True
|
||||
del _actual[i]
|
||||
break
|
||||
if missing_exception:
|
||||
assert found, missing_exception(expected_item)
|
||||
else:
|
||||
assert found
|
||||
if not allow_extra:
|
||||
if extra_exception:
|
||||
assert len(_actual) == 0, [extra_exception(a) for a in _actual]
|
||||
else:
|
||||
assert len(_actual) == 0
|
||||
|
||||
def filter_list(f, l):
|
||||
if l is not None:
|
||||
l = list(filter(f, l))
|
||||
if l == []:
|
||||
l = None
|
||||
return l
|
||||
|
||||
def check_cmake(cmake):
|
||||
assert is_dict(cmake)
|
||||
assert sorted(cmake.keys()) == ["generator", "paths", "version"]
|
||||
check_cmake_version(cmake["version"])
|
||||
check_cmake_paths(cmake["paths"])
|
||||
check_cmake_generator(cmake["generator"])
|
||||
|
||||
def check_cmake_version(v):
|
||||
assert is_dict(v)
|
||||
assert sorted(v.keys()) == ["isDirty", "major", "minor", "patch", "string", "suffix"]
|
||||
assert is_string(v["string"])
|
||||
assert is_int(v["major"])
|
||||
assert is_int(v["minor"])
|
||||
assert is_int(v["patch"])
|
||||
assert is_string(v["suffix"])
|
||||
assert is_bool(v["isDirty"])
|
||||
|
||||
def check_cmake_paths(v):
|
||||
assert is_dict(v)
|
||||
assert sorted(v.keys()) == ["cmake", "cpack", "ctest", "root"]
|
||||
assert is_string(v["cmake"])
|
||||
assert is_string(v["cpack"])
|
||||
assert is_string(v["ctest"])
|
||||
assert is_string(v["root"])
|
||||
|
||||
def check_cmake_generator(g):
|
||||
assert is_dict(g)
|
||||
name = g.get("name", None)
|
||||
assert is_string(name)
|
||||
if name.startswith("Visual Studio"):
|
||||
assert sorted(g.keys()) == ["name", "platform"]
|
||||
assert is_string(g["platform"])
|
||||
else:
|
||||
assert sorted(g.keys()) == ["name"]
|
||||
|
||||
def check_index_object(indexEntry, kind, major, minor, check):
|
||||
assert is_dict(indexEntry)
|
||||
assert sorted(indexEntry.keys()) == ["jsonFile", "kind", "version"]
|
||||
assert is_string(indexEntry["kind"])
|
||||
assert indexEntry["kind"] == kind
|
||||
assert is_dict(indexEntry["version"])
|
||||
assert sorted(indexEntry["version"].keys()) == ["major", "minor"]
|
||||
assert indexEntry["version"]["major"] == major
|
||||
assert indexEntry["version"]["minor"] == minor
|
||||
assert is_string(indexEntry["jsonFile"])
|
||||
filepath = os.path.join(reply_dir, indexEntry["jsonFile"])
|
||||
with open(filepath) as f:
|
||||
object = json.load(f)
|
||||
assert is_dict(object)
|
||||
assert "kind" in object
|
||||
assert is_string(object["kind"])
|
||||
assert object["kind"] == kind
|
||||
assert "version" in object
|
||||
assert is_dict(object["version"])
|
||||
assert sorted(object["version"].keys()) == ["major", "minor"]
|
||||
assert object["version"]["major"] == major
|
||||
assert object["version"]["minor"] == minor
|
||||
if check:
|
||||
check(object)
|
||||
|
||||
def check_index__test(indexEntry, major, minor):
|
||||
def check(object):
|
||||
assert sorted(object.keys()) == ["kind", "version"]
|
||||
check_index_object(indexEntry, "__test", major, minor, check)
|
||||
|
||||
def check_error(value, error):
|
||||
assert is_dict(value)
|
||||
assert sorted(value.keys()) == ["error"]
|
||||
assert is_string(value["error"])
|
||||
assert value["error"] == error
|
||||
|
||||
def check_error_re(value, error):
|
||||
assert is_dict(value)
|
||||
assert sorted(value.keys()) == ["error"]
|
||||
assert is_string(value["error"])
|
||||
assert re.search(error, value["error"])
|
||||
|
||||
reply_index = sys.argv[1]
|
||||
reply_dir = os.path.dirname(reply_index)
|
||||
|
||||
with open(reply_index) as f:
|
||||
index = json.load(f)
|
||||
@@ -0,0 +1,11 @@
|
||||
set(expect
|
||||
query
|
||||
query/client-foo
|
||||
query/client-foo/query.json
|
||||
reply
|
||||
reply/cmakeFiles-v1-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(cmakeFiles-v1)
|
||||
@@ -0,0 +1,4 @@
|
||||
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[
|
||||
{ "requests": [ { "kind": "cmakeFiles", "version" : 1 } ] }
|
||||
]])
|
||||
@@ -0,0 +1,11 @@
|
||||
set(expect
|
||||
query
|
||||
query/client-foo
|
||||
query/client-foo/cmakeFiles-v1
|
||||
reply
|
||||
reply/cmakeFiles-v1-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(cmakeFiles-v1)
|
||||
@@ -0,0 +1,2 @@
|
||||
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/cmakeFiles-v1" "")
|
||||
@@ -0,0 +1,10 @@
|
||||
set(expect
|
||||
query
|
||||
query/cmakeFiles-v1
|
||||
reply
|
||||
reply/cmakeFiles-v1-[0-9a-f]+.json
|
||||
reply/index-[0-9.T-]+.json
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(cmakeFiles-v1)
|
||||
@@ -0,0 +1,2 @@
|
||||
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/cmakeFiles-v1" "")
|
||||
94
Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py
Normal file
94
Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py
Normal file
@@ -0,0 +1,94 @@
|
||||
from check_index import *
|
||||
|
||||
def check_objects(o):
|
||||
assert is_list(o)
|
||||
assert len(o) == 1
|
||||
check_index_object(o[0], "cmakeFiles", 1, 0, check_object_cmakeFiles)
|
||||
|
||||
def check_input(actual, expected):
|
||||
assert is_dict(actual)
|
||||
expected_keys = ["path"]
|
||||
|
||||
if expected["isGenerated"] is not None:
|
||||
expected_keys.append("isGenerated")
|
||||
assert is_bool(actual["isGenerated"], expected["isGenerated"])
|
||||
|
||||
if expected["isExternal"] is not None:
|
||||
expected_keys.append("isExternal")
|
||||
assert is_bool(actual["isExternal"], expected["isExternal"])
|
||||
|
||||
if expected["isCMake"] is not None:
|
||||
expected_keys.append("isCMake")
|
||||
assert is_bool(actual["isCMake"], expected["isCMake"])
|
||||
|
||||
assert sorted(actual.keys()) == sorted(expected_keys)
|
||||
|
||||
def check_object_cmakeFiles(o):
|
||||
assert sorted(o.keys()) == ["inputs", "kind", "paths", "version"]
|
||||
# The "kind" and "version" members are handled by check_index_object.
|
||||
assert is_dict(o["paths"])
|
||||
assert sorted(o["paths"].keys()) == ["build", "source"]
|
||||
assert matches(o["paths"]["build"], "^.*/Tests/RunCMake/FileAPI/cmakeFiles-v1-build$")
|
||||
assert matches(o["paths"]["source"], "^.*/Tests/RunCMake/FileAPI$")
|
||||
|
||||
expected = [
|
||||
{
|
||||
"path": "^CMakeLists\\.txt$",
|
||||
"isGenerated": None,
|
||||
"isExternal": None,
|
||||
"isCMake": None,
|
||||
},
|
||||
{
|
||||
"path": "^cmakeFiles-v1\\.cmake$",
|
||||
"isGenerated": None,
|
||||
"isExternal": None,
|
||||
"isCMake": None,
|
||||
},
|
||||
{
|
||||
"path": "^dir/CMakeLists\\.txt$",
|
||||
"isGenerated": None,
|
||||
"isExternal": None,
|
||||
"isCMake": None,
|
||||
},
|
||||
{
|
||||
"path": "^dir/dir/CMakeLists\\.txt$",
|
||||
"isGenerated": None,
|
||||
"isExternal": None,
|
||||
"isCMake": None,
|
||||
},
|
||||
{
|
||||
"path": "^dir/dirtest\\.cmake$",
|
||||
"isGenerated": None,
|
||||
"isExternal": None,
|
||||
"isCMake": None,
|
||||
},
|
||||
{
|
||||
"path": "^.*/Tests/RunCMake/FileAPIDummyFile\\.cmake$",
|
||||
"isGenerated": None,
|
||||
"isExternal": True,
|
||||
"isCMake": None,
|
||||
},
|
||||
{
|
||||
"path": "^.*/Tests/RunCMake/FileAPI/cmakeFiles-v1-build/generated\\.cmake",
|
||||
"isGenerated": True,
|
||||
"isExternal": None,
|
||||
"isCMake": None,
|
||||
},
|
||||
{
|
||||
"path": "^.*/Modules/CMakeParseArguments\\.cmake$",
|
||||
"isGenerated": None,
|
||||
"isExternal": True,
|
||||
"isCMake": True,
|
||||
},
|
||||
]
|
||||
|
||||
inSource = os.path.dirname(o["paths"]["build"]) == o["paths"]["source"]
|
||||
if inSource:
|
||||
for e in expected:
|
||||
e["path"] = e["path"].replace("^.*/Tests/RunCMake/FileAPI/", "^", 1)
|
||||
|
||||
check_list_match(lambda a, e: matches(a["path"], e["path"]), o["inputs"], expected, check=check_input, allow_extra=True)
|
||||
|
||||
assert is_dict(index)
|
||||
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
|
||||
check_objects(index["objects"])
|
||||
8
Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
Normal file
8
Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake
Normal file
@@ -0,0 +1,8 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/dir/dirtest.cmake")
|
||||
include(CMakeParseArguments)
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generated.cmake" "")
|
||||
include("${CMAKE_CURRENT_BINARY_DIR}/generated.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../FileAPIDummyFile.cmake")
|
||||
|
||||
add_subdirectory(dir)
|
||||
@@ -0,0 +1,12 @@
|
||||
set(expect
|
||||
query
|
||||
query/client-foo
|
||||
query/client-foo/query.json
|
||||
reply
|
||||
reply/codemodel-v2-[0-9a-f]+\\.json
|
||||
reply/index-[0-9.T-]+\\.json
|
||||
.*
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(codemodel-v2)
|
||||
@@ -0,0 +1,4 @@
|
||||
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/query.json" [[
|
||||
{ "requests": [ { "kind": "codemodel", "version" : 2 } ] }
|
||||
]])
|
||||
@@ -0,0 +1,12 @@
|
||||
set(expect
|
||||
query
|
||||
query/client-foo
|
||||
query/client-foo/codemodel-v2
|
||||
reply
|
||||
reply/codemodel-v2-[0-9a-f]+\\.json
|
||||
reply/index-[0-9.T-]+\\.json
|
||||
.*
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(codemodel-v2)
|
||||
@@ -0,0 +1,2 @@
|
||||
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/codemodel-v2" "")
|
||||
@@ -0,0 +1,11 @@
|
||||
set(expect
|
||||
query
|
||||
query/codemodel-v2
|
||||
reply
|
||||
reply/codemodel-v2-[0-9a-f]+\\.json
|
||||
reply/index-[0-9.T-]+\\.json
|
||||
.*
|
||||
)
|
||||
check_api("^${expect}$")
|
||||
|
||||
check_python(codemodel-v2)
|
||||
@@ -0,0 +1,2 @@
|
||||
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
|
||||
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/codemodel-v2" "")
|
||||
5085
Tests/RunCMake/FileAPI/codemodel-v2-check.py
Normal file
5085
Tests/RunCMake/FileAPI/codemodel-v2-check.py
Normal file
File diff suppressed because it is too large
Load Diff
35
Tests/RunCMake/FileAPI/codemodel-v2.cmake
Normal file
35
Tests/RunCMake/FileAPI/codemodel-v2.cmake
Normal file
@@ -0,0 +1,35 @@
|
||||
enable_language(C)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/include_test.cmake")
|
||||
|
||||
add_library(c_lib empty.c)
|
||||
add_executable(c_exe empty.c)
|
||||
target_link_libraries(c_exe PRIVATE c_lib)
|
||||
|
||||
add_library(c_shared_lib SHARED empty.c)
|
||||
add_executable(c_shared_exe empty.c)
|
||||
target_link_libraries(c_shared_exe PRIVATE c_shared_lib)
|
||||
|
||||
add_library(c_static_lib STATIC empty.c)
|
||||
add_executable(c_static_exe empty.c)
|
||||
target_link_libraries(c_static_exe PRIVATE c_static_lib)
|
||||
|
||||
add_subdirectory(cxx)
|
||||
add_subdirectory(alias)
|
||||
add_subdirectory(object)
|
||||
add_subdirectory(imported)
|
||||
add_subdirectory(custom)
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../FileAPIExternalSource" "${CMAKE_CURRENT_BINARY_DIR}/../FileAPIExternalBuild")
|
||||
add_subdirectory(dir)
|
||||
|
||||
set_property(TARGET c_shared_lib PROPERTY LIBRARY_OUTPUT_DIRECTORY lib)
|
||||
set_property(TARGET c_shared_lib PROPERTY RUNTIME_OUTPUT_DIRECTORY lib)
|
||||
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT _ipo LANGUAGES C)
|
||||
if(_ipo)
|
||||
set_property(TARGET c_shared_lib PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
|
||||
set_property(TARGET c_shared_exe PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
|
||||
set_property(TARGET c_static_lib PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
|
||||
file(WRITE "${CMAKE_BINARY_DIR}/ipo_enabled.txt" "")
|
||||
endif()
|
||||
5
Tests/RunCMake/FileAPI/custom/CMakeLists.txt
Normal file
5
Tests/RunCMake/FileAPI/custom/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
project(Custom)
|
||||
|
||||
add_custom_target(custom_tgt COMMAND ${CMAKE_COMMAND} -E echo "Building custom_tgt")
|
||||
add_executable(custom_exe ../empty.c)
|
||||
add_dependencies(custom_exe custom_tgt)
|
||||
15
Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
Normal file
15
Tests/RunCMake/FileAPI/cxx/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
project(Cxx)
|
||||
enable_language(CXX)
|
||||
|
||||
add_library(cxx_lib ../empty.cxx)
|
||||
add_executable(cxx_exe ../empty.cxx)
|
||||
target_link_libraries(cxx_exe PRIVATE cxx_lib)
|
||||
set_property(TARGET cxx_exe PROPERTY FOLDER bin)
|
||||
|
||||
add_library(cxx_shared_lib SHARED ../empty.cxx)
|
||||
add_executable(cxx_shared_exe ../empty.cxx)
|
||||
target_link_libraries(cxx_shared_exe PRIVATE cxx_shared_lib)
|
||||
|
||||
add_library(cxx_static_lib STATIC ../empty.cxx)
|
||||
add_executable(cxx_static_exe ../empty.cxx)
|
||||
target_link_libraries(cxx_static_exe PRIVATE cxx_static_lib)
|
||||
1
Tests/RunCMake/FileAPI/dir/CMakeLists.txt
Normal file
1
Tests/RunCMake/FileAPI/dir/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
||||
add_subdirectory(dir)
|
||||
0
Tests/RunCMake/FileAPI/dir/dir/CMakeLists.txt
Normal file
0
Tests/RunCMake/FileAPI/dir/dir/CMakeLists.txt
Normal file
0
Tests/RunCMake/FileAPI/dir/dirtest.cmake
Normal file
0
Tests/RunCMake/FileAPI/dir/dirtest.cmake
Normal file
0
Tests/RunCMake/FileAPI/empty.c
Normal file
0
Tests/RunCMake/FileAPI/empty.c
Normal file
0
Tests/RunCMake/FileAPI/empty.cxx
Normal file
0
Tests/RunCMake/FileAPI/empty.cxx
Normal file
24
Tests/RunCMake/FileAPI/imported/CMakeLists.txt
Normal file
24
Tests/RunCMake/FileAPI/imported/CMakeLists.txt
Normal file
@@ -0,0 +1,24 @@
|
||||
project(Imported)
|
||||
|
||||
add_library(imported_lib UNKNOWN IMPORTED)
|
||||
add_executable(imported_exe IMPORTED)
|
||||
add_executable(link_imported_exe ../empty.c)
|
||||
target_link_libraries(link_imported_exe PRIVATE imported_lib)
|
||||
|
||||
add_library(imported_shared_lib SHARED IMPORTED)
|
||||
add_executable(link_imported_shared_exe ../empty.c)
|
||||
target_link_libraries(link_imported_shared_exe PRIVATE imported_shared_lib)
|
||||
|
||||
add_library(imported_static_lib STATIC IMPORTED)
|
||||
add_executable(link_imported_static_exe ../empty.c)
|
||||
target_link_libraries(link_imported_static_exe PRIVATE imported_static_lib)
|
||||
|
||||
if(NOT CMAKE_GENERATOR STREQUAL "Xcode" OR NOT CMAKE_OSX_ARCHITECTURES MATCHES "[;$]")
|
||||
add_library(imported_object_lib OBJECT IMPORTED)
|
||||
add_executable(link_imported_object_exe ../empty.c)
|
||||
target_link_libraries(link_imported_object_exe PRIVATE imported_object_lib)
|
||||
endif()
|
||||
|
||||
add_library(imported_interface_lib INTERFACE IMPORTED)
|
||||
add_executable(link_imported_interface_exe ../empty.c)
|
||||
target_link_libraries(link_imported_interface_exe PRIVATE imported_interface_lib)
|
||||
9
Tests/RunCMake/FileAPI/include_test.cmake
Normal file
9
Tests/RunCMake/FileAPI/include_test.cmake
Normal file
@@ -0,0 +1,9 @@
|
||||
add_library(interface_lib INTERFACE)
|
||||
target_compile_definitions(interface_lib INTERFACE COMPILED_WITH_INTERFACE_LIB)
|
||||
add_executable(interface_exe empty.c)
|
||||
target_link_libraries(interface_exe PRIVATE inteface_lib)
|
||||
set_property(TARGET interface_exe PROPERTY ENABLE_EXPORTS ON)
|
||||
set_property(TARGET interface_exe PROPERTY RUNTIME_OUTPUT_DIRECTORY bin)
|
||||
set_property(TARGET interface_exe PROPERTY ARCHIVE_OUTPUT_DIRECTORY lib)
|
||||
set_property(TARGET interface_exe PROPERTY OUTPUT_NAME my_interface_exe)
|
||||
set_property(TARGET interface_exe PROPERTY SUFFIX .myexe)
|
||||
13
Tests/RunCMake/FileAPI/object/CMakeLists.txt
Normal file
13
Tests/RunCMake/FileAPI/object/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(Object)
|
||||
enable_language(CXX)
|
||||
|
||||
add_library(c_object_lib OBJECT ../empty.c)
|
||||
add_executable(c_object_exe ../empty.c)
|
||||
target_link_libraries(c_object_exe PRIVATE c_object_lib)
|
||||
|
||||
add_library(cxx_object_lib OBJECT ../empty.cxx)
|
||||
add_executable(cxx_object_exe ../empty.cxx)
|
||||
target_link_libraries(cxx_object_exe PRIVATE cxx_object_lib)
|
||||
|
||||
install(TARGETS c_object_exe cxx_object_exe DESTINATION bin)
|
||||
0
Tests/RunCMake/FileAPIDummyFile.cmake
Normal file
0
Tests/RunCMake/FileAPIDummyFile.cmake
Normal file
12
Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt
Normal file
12
Tests/RunCMake/FileAPIExternalSource/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
project(External)
|
||||
enable_language(CXX)
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx" "")
|
||||
add_executable(generated_exe empty.c)
|
||||
target_sources(generated_exe PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx")
|
||||
source_group("Generated Source Files" FILES "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx")
|
||||
set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/generated.cxx" PROPERTY GENERATED ON)
|
||||
set_property(SOURCE empty.c PROPERTY COMPILE_DEFINITIONS EMPTY_C=1 SRC_DUMMY)
|
||||
set_property(SOURCE empty.c PROPERTY INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
target_include_directories(generated_exe SYSTEM PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
target_compile_definitions(generated_exe PRIVATE GENERATED_EXE=1 -DTGT_DUMMY)
|
||||
0
Tests/RunCMake/FileAPIExternalSource/empty.c
Normal file
0
Tests/RunCMake/FileAPIExternalSource/empty.c
Normal file
@@ -45,6 +45,11 @@ function(run_cmake test)
|
||||
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
|
||||
endif()
|
||||
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
|
||||
if(RunCMake-prep-file AND EXISTS ${top_src}/${RunCMake-prep-file})
|
||||
include(${top_src}/${RunCMake-prep-file})
|
||||
else()
|
||||
include(${top_src}/${test}-prep.cmake OPTIONAL)
|
||||
endif()
|
||||
if(NOT DEFINED RunCMake_TEST_OPTIONS)
|
||||
set(RunCMake_TEST_OPTIONS "")
|
||||
endif()
|
||||
|
||||
@@ -123,6 +123,8 @@
|
||||
{ symbol: [ "SIGINT", private, "\"cm_uv.h\"", public ] },
|
||||
{ symbol: [ "ssize_t", private, "\"cm_uv.h\"", public ] },
|
||||
|
||||
{ symbol: [ "Json::ArrayIndex", private, "\"cm_jsoncpp_value.h\"", public ] },
|
||||
|
||||
{ symbol: [ "std::ifstream", private, "\"cmsys/FStream.hxx\"", public ] },
|
||||
{ symbol: [ "std::ofstream", private, "\"cmsys/FStream.hxx\"", public ] },
|
||||
{ symbol: [ "cmsys::ifstream", private, "\"cmsys/FStream.hxx\"", public ] },
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user