fileapi: Generate partial reply when buildsystem generation fails

In particular, the `configureLog` reply is useful for IDEs to read
`CMakeFiles/CMakeConfigureLog.yaml` when configuration fails.

Fixes: #26621
This commit is contained in:
Brad King
2025-04-14 09:34:24 -04:00
parent 09aa968ed0
commit 0cc962665b
42 changed files with 616 additions and 16 deletions

View File

@@ -41,14 +41,22 @@ It has the following subdirectories:
`v1 Client Stateless Query Files`_, or `v1 Client Stateful Query Files`_.
``reply/``
Holds reply files written by CMake whenever it runs to generate a build
system. These are indexed by a `v1 Reply Index File`_ that may
reference additional `v1 Reply Files`_. CMake owns all reply files.
Clients must never remove them.
Holds reply files written by CMake when it runs to generate a build system.
Clients may read reply files only when referenced by a reply index:
Clients may look for and read a reply index file at any time.
``index-*.json``
A `v1 Reply Index File`_ written when CMake generates a build system.
``error-*.json``
.. versionadded:: 4.1
A `v1 Reply Error Index`_ written when CMake fails to generate a build
system due to an error.
Clients may look for and read a reply index at any time.
Clients may optionally create the ``reply/`` directory at any time
and monitor it for the appearance of a new reply index file.
and monitor it for the appearance of a new reply index.
CMake owns all reply files. Clients must never remove them.
.. versionadded:: 3.31
Users can add query files to ``api/v1/query`` inside the
@@ -179,7 +187,7 @@ v1 Reply Index File
-------------------
CMake writes an ``index-*.json`` file to the ``v1/reply/`` directory
whenever it runs to generate a build system. Clients must read the
when it successfully generates a build system. Clients must read the
reply index file first and may read other `v1 Reply Files`_ only by
following references. The form of the reply index file name is::
@@ -300,8 +308,12 @@ The members are:
A member of this form appears for each of the
`v1 Shared Stateless Query Files`_ that CMake recognized as a
request for object kind ``<kind>`` with major version ``<major>``.
The value is a `v1 Reply File Reference`_ to the corresponding
reply file for that object kind and version.
The value is
* a `v1 Reply File Reference`_ to the corresponding reply file for
that object kind and version, or
* in a `v1 Reply Error Index`_, a JSON object with a single ``error``
member containing a string with an error message.
``<unknown>``
A member of this form appears for each of the
@@ -320,8 +332,12 @@ The members are:
A member of this form appears for each of the
`v1 Client Stateless Query Files`_ that CMake recognized as a
request for object kind ``<kind>`` with major version ``<major>``.
The value is a `v1 Reply File Reference`_ to the corresponding
reply file for that object kind and version.
The value is
* a `v1 Reply File Reference`_ to the corresponding reply file for
that object kind and version, or
* in a `v1 Reply Error Index`_, a JSON object with a single ``error``
member containing a string with an error message.
``<unknown>``
A member of this form appears for each of the
@@ -375,6 +391,35 @@ using a JSON object with members:
A JSON string specifying a path relative to the reply index file
to another JSON file containing the object.
.. _`file-api reply error index`:
v1 Reply Error Index
^^^^^^^^^^^^^^^^^^^^
.. versionadded:: 4.1
CMake writes an ``error-*.json`` file to the ``v1/reply/`` directory
when it fails to generate a build system. This reply error index
follows the same naming pattern, syntax, and semantics of a
`v1 Reply Index File`_, with the following exceptions:
* The ``index-`` prefix is replaced by an ``error-`` prefix.
* When a new error index is generated, old index files are *not*
deleted. If a `v1 Reply Index File`_ exists, it indexes replies
from the most recent successful run. If multiple ``index-*.json``
and/or ``error-*.json`` files are present, the one with the largest
name in lexicographic order, excluding the ``index-`` or ``error-``
prefix, is the current index.
* Only a subset of `Object Kinds`_ are provided:
`configureLog <file-api configureLog_>`_
.. versionadded:: 4.1
Index entries for other object kinds contain an ``error`` message
instead of a `v1 Reply File Reference`_.
v1 Reply Files
--------------

View File

@@ -0,0 +1,6 @@
fileapi-reply-on-failure
------------------------
* The :manual:`cmake-file-api(7)` :ref:`v1 <file-api v1>` now writes
partial replies when buildsystem generation fails with an error.
See the :ref:`v1 Reply Error Index <file-api reply error index>`.

View File

@@ -105,14 +105,20 @@ std::vector<unsigned long> cmFileAPI::GetConfigureLogVersions()
return versions;
}
void cmFileAPI::WriteReplies()
void cmFileAPI::WriteReplies(IndexFor indexFor)
{
bool const success = indexFor == IndexFor::Success;
this->ReplyIndexFor = indexFor;
if (this->QueryExists) {
cmSystemTools::MakeDirectory(this->APIv1 + "/reply");
this->WriteJsonFile(this->BuildReplyIndex(), "index", ComputeSuffixTime);
this->WriteJsonFile(this->BuildReplyIndex(), success ? "index" : "error",
ComputeSuffixTime);
}
this->RemoveOldReplyFiles();
if (success) {
this->RemoveOldReplyFiles();
}
}
std::vector<std::string> cmFileAPI::LoadDir(std::string const& dir)
@@ -453,6 +459,18 @@ Json::Value cmFileAPI::BuildReply(Query const& q)
Json::Value cmFileAPI::BuildReplyEntry(Object const& object)
{
if (this->ReplyIndexFor != IndexFor::Success) {
switch (object.Kind) {
case ObjectKind::ConfigureLog:
break;
case ObjectKind::CodeModel:
case ObjectKind::Cache:
case ObjectKind::CMakeFiles:
case ObjectKind::Toolchains:
case ObjectKind::InternalTest:
return this->BuildReplyError("no buildsystem generated");
}
}
return this->AddReplyIndexObject(object);
}

View File

@@ -27,8 +27,17 @@ public:
/** Get the list of configureLog object kind versions requested. */
std::vector<unsigned long> GetConfigureLogVersions();
/** Identify the situation in which WriteReplies is called. */
enum class IndexFor
{
Success,
FailedConfigure,
FailedCompute,
FailedGenerate,
};
/** Write fileapi replies to disk. */
void WriteReplies();
void WriteReplies(IndexFor indexFor);
/** Get the "cmake" instance with which this was constructed. */
cmake* GetCMakeInstance() const { return this->CMakeInstance; }
@@ -157,6 +166,9 @@ private:
This populates the "objects" field of the reply index. */
std::map<Object, Json::Value> ReplyIndexObjects;
/** Identify the situation in which WriteReplies was called. */
IndexFor ReplyIndexFor = IndexFor::Success;
std::unique_ptr<Json::CharReader> JsonReader;
std::unique_ptr<Json::StreamWriter> JsonWriter;

View File

@@ -2725,6 +2725,9 @@ int cmake::ActualConfigure()
this->Messenger.get());
this->SaveCache(this->GetHomeOutputDirectory());
if (cmSystemTools::GetErrorOccurredFlag()) {
#if !defined(CMAKE_BOOTSTRAP)
this->FileAPI->WriteReplies(cmFileAPI::IndexFor::FailedConfigure);
#endif
return -1;
}
return 0;
@@ -3041,6 +3044,7 @@ int cmake::Generate()
auto profilingRAII = this->CreateProfilingEntry("project", "generate");
auto doGenerate = [this]() -> int {
if (!this->GlobalGenerator->Compute()) {
this->FileAPI->WriteReplies(cmFileAPI::IndexFor::FailedCompute);
return -1;
}
this->GlobalGenerator->Generate();
@@ -3080,6 +3084,9 @@ int cmake::Generate()
this->RunCheckForUnusedVariables();
}
if (cmSystemTools::GetErrorOccurredFlag()) {
#if !defined(CMAKE_BOOTSTRAP)
this->FileAPI->WriteReplies(cmFileAPI::IndexFor::FailedGenerate);
#endif
return -1;
}
// Save the cache again after a successful Generate so that any internal
@@ -3089,7 +3096,7 @@ int cmake::Generate()
#if !defined(CMAKE_BOOTSTRAP)
this->GlobalGenerator->WriteInstallJson();
this->FileAPI->WriteReplies();
this->FileAPI->WriteReplies(cmFileAPI::IndexFor::Success);
#endif
return 0;

View File

@@ -0,0 +1,22 @@
set(expect
query
query/cache-v2
query/client-bar
query/client-bar/query.json
query/client-foo
query/client-foo/cache-v2
query/client-foo/cmakeFiles-v1
query/client-foo/codemodel-v2
query/client-foo/configureLog-v1
query/client-foo/toolchains-v1
query/cmakeFiles-v1
query/codemodel-v2
query/configureLog-v1
query/toolchains-v1
reply
reply/configureLog-v1-[0-9a-f]+\\.json
reply/error-[0-9.T-]+\\.json
)
check_api("^${expect}$")
check_python(FailConfigure error)

View File

@@ -0,0 +1,74 @@
from check_index import *
import os
def check_reply(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"cache-v2",
"client-bar",
"client-foo",
"cmakeFiles-v1",
"codemodel-v2",
"configureLog-v1",
"toolchains-v1",
]
check_error(r["cache-v2"], "no buildsystem generated")
check_error(r["cmakeFiles-v1"], "no buildsystem generated")
check_reply_client_bar(r["client-bar"])
check_reply_client_foo(r["client-foo"])
check_error(r["codemodel-v2"], "no buildsystem generated")
check_index_object(r["configureLog-v1"], "configureLog", 1, 0, None)
check_error(r["toolchains-v1"], "no buildsystem generated")
def check_reply_client_bar(r):
assert is_dict(r)
assert sorted(r.keys()) == ["query.json"]
query = r["query.json"]
assert sorted(query.keys()) == ["requests", "responses"]
requests = query["requests"]
assert is_list(requests)
assert len(requests) == 5
responses = query["responses"]
assert is_list(responses)
assert len(responses) == 5
check_error(responses[0], "no buildsystem generated")
check_index_object(responses[1], "configureLog", 1, 0, None)
check_error(responses[2], "no buildsystem generated")
check_error(responses[3], "no buildsystem generated")
check_error(responses[4], "no buildsystem generated")
def check_reply_client_foo(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"cache-v2",
"cmakeFiles-v1",
"codemodel-v2",
"configureLog-v1",
"toolchains-v1",
]
check_error(r["cache-v2"], "no buildsystem generated")
check_error(r["cmakeFiles-v1"], "no buildsystem generated")
check_error(r["codemodel-v2"], "no buildsystem generated")
check_index_object(r["configureLog-v1"], "configureLog", 1, 0, None)
check_error(r["toolchains-v1"], "no buildsystem generated")
def check_objects(o):
assert is_list(o)
assert len(o) == 1
check_index_object(o[0], "configureLog", 1, 0, check_object_configureLog)
def check_object_configureLog(o):
assert sorted(o.keys()) == ["eventKindNames", "kind", "path", "version"]
# The "kind" and "version" members are handled by check_index_object.
path = o["path"]
assert matches(path, "^.*/CMakeFiles/CMakeConfigureLog\\.yaml$")
assert os.path.exists(path)
eventKindNames = o["eventKindNames"]
assert is_list(eventKindNames)
assert sorted(eventKindNames) == ["message-v1", "try_compile-v1", "try_run-v1"]
assert is_dict(index)
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
check_cmake(index["cmake"])
check_reply(index["reply"])
check_objects(index["objects"])

View File

@@ -0,0 +1,26 @@
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
# Shared Stateless
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/codemodel-v2" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/configureLog-v1" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/cache-v2" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/cmakeFiles-v1" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/toolchains-v1" "")
# Client Stateless
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/codemodel-v2" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/configureLog-v1" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/cache-v2" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/cmakeFiles-v1" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/toolchains-v1" "")
# Client Stateful
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-bar/query.json" [[
{ "requests": [
{ "kind": "codemodel", "version" : 2 },
{ "kind": "configureLog", "version" : 1 },
{ "kind": "cache", "version" : 2 },
{ "kind": "cmakeFiles", "version" : 1 },
{ "kind": "toolchains", "version" : 1 }
] }
]])

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at FailConfigure.cmake:1 \(message\):
Intentionally fail to configure
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1 @@
message(FATAL_ERROR "Intentionally fail to configure")

View File

@@ -54,6 +54,7 @@ run_cmake(DuplicateStateless)
run_cmake(ClientStateful)
run_cmake(ProjectQueryGood)
run_cmake(ProjectQueryBad)
run_cmake(FailConfigure)
function(run_object object)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${object}-build)
@@ -64,6 +65,7 @@ function(run_object object)
run_cmake_command(${object}-SharedStateless ${CMAKE_COMMAND} .)
run_cmake_command(${object}-ClientStateless ${CMAKE_COMMAND} .)
run_cmake_command(${object}-ClientStateful ${CMAKE_COMMAND} .)
run_cmake_command(${object}-FailConfigure ${CMAKE_COMMAND} . -DFAIL=1)
endfunction()
run_object(codemodel-v2)

View File

@@ -0,0 +1,16 @@
set(expect
query
query/cache-v2
query/client-bar
query/client-bar/query.json
query/client-foo
query/client-foo/cache-v2
reply
reply/cache-v2-[0-9a-f]+\\.json
reply/error-[0-9.T-]+.json
reply/index-[0-9.T-]+.json
)
check_api("^${expect}$")
check_python(cache-v2-FailConfigure error)
check_python(cache-v2 index) # Last-good index is intact.

View File

@@ -0,0 +1,43 @@
from check_index import *
import os
def check_reply(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"cache-v2",
"client-bar",
"client-foo",
]
check_error(r["cache-v2"], "no buildsystem generated")
check_reply_client_bar(r["client-bar"])
check_reply_client_foo(r["client-foo"])
def check_reply_client_bar(r):
assert is_dict(r)
assert sorted(r.keys()) == ["query.json"]
query = r["query.json"]
assert sorted(query.keys()) == ["requests", "responses"]
requests = query["requests"]
assert is_list(requests)
assert len(requests) == 1
responses = query["responses"]
assert is_list(responses)
assert len(responses) == 1
check_error(responses[0], "no buildsystem generated")
def check_reply_client_foo(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"cache-v2",
]
check_error(r["cache-v2"], "no buildsystem generated")
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"])

View File

@@ -0,0 +1,6 @@
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/cache-v2" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/cache-v2" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-bar/query.json" [[
{ "requests": [ { "kind": "cache", "version" : 2 } ] }
]])

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at cache-v2.cmake:[0-9]+ \(message\):
Intentionally fail to configure
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -12,3 +12,7 @@ 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")
if(FAIL)
message(FATAL_ERROR "Intentionally fail to configure")
endif()

View File

@@ -0,0 +1,16 @@
set(expect
query
query/client-bar
query/client-bar/query.json
query/client-foo
query/client-foo/cmakeFiles-v1
query/cmakeFiles-v1
reply
reply/cmakeFiles-v1-[0-9a-f]+\\.json
reply/error-[0-9.T-]+.json
reply/index-[0-9.T-]+.json
)
check_api("^${expect}$")
check_python(cmakeFiles-v1-FailConfigure error)
check_python(cmakeFiles-v1 index) # Last-good index is intact.

View File

@@ -0,0 +1,43 @@
from check_index import *
import os
def check_reply(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"client-bar",
"client-foo",
"cmakeFiles-v1",
]
check_reply_client_bar(r["client-bar"])
check_reply_client_foo(r["client-foo"])
check_error(r["cmakeFiles-v1"], "no buildsystem generated")
def check_reply_client_bar(r):
assert is_dict(r)
assert sorted(r.keys()) == ["query.json"]
query = r["query.json"]
assert sorted(query.keys()) == ["requests", "responses"]
requests = query["requests"]
assert is_list(requests)
assert len(requests) == 1
responses = query["responses"]
assert is_list(responses)
assert len(responses) == 1
check_error(responses[0], "no buildsystem generated")
def check_reply_client_foo(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"cmakeFiles-v1",
]
check_error(r["cmakeFiles-v1"], "no buildsystem generated")
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"])

View File

@@ -0,0 +1,6 @@
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/cmakeFiles-v1" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/cmakeFiles-v1" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-bar/query.json" [[
{ "requests": [ { "kind": "cmakeFiles", "version" : 1 } ] }
]])

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at cmakeFiles-v1.cmake:[0-9]+ \(message\):
Intentionally fail to configure
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -16,3 +16,7 @@ file(GLOB_RECURSE var
"${CMAKE_CURRENT_SOURCE_DIR}/dir/*.cmake")
add_subdirectory(dir)
if(FAIL)
message(FATAL_ERROR "Intentionally fail to configure")
endif()

View File

@@ -0,0 +1,18 @@
set(expect
query
query/client-bar
query/client-bar/query.json
query/client-foo
query/client-foo/codemodel-v2
query/codemodel-v2
reply
reply/codemodel-v2-[0-9a-f]+\\.json
.*
reply/error-[0-9.T-]+.json
reply/index-[0-9.T-]+.json
.*
)
check_api("^${expect}$")
check_python(codemodel-v2-FailConfigure error)
check_python(codemodel-v2 index) # Last-good index is intact.

View File

@@ -0,0 +1,43 @@
from check_index import *
import os
def check_reply(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"client-bar",
"client-foo",
"codemodel-v2",
]
check_reply_client_bar(r["client-bar"])
check_reply_client_foo(r["client-foo"])
check_error(r["codemodel-v2"], "no buildsystem generated")
def check_reply_client_bar(r):
assert is_dict(r)
assert sorted(r.keys()) == ["query.json"]
query = r["query.json"]
assert sorted(query.keys()) == ["requests", "responses"]
requests = query["requests"]
assert is_list(requests)
assert len(requests) == 1
responses = query["responses"]
assert is_list(responses)
assert len(responses) == 1
check_error(responses[0], "no buildsystem generated")
def check_reply_client_foo(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"codemodel-v2",
]
check_error(r["codemodel-v2"], "no buildsystem generated")
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"])

View File

@@ -0,0 +1,6 @@
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/codemodel-v2" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/codemodel-v2" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-bar/query.json" [[
{ "requests": [ { "kind": "codemodel", "version" : 2 } ] }
]])

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at codemodel-v2.cmake:[0-9]+ \(message\):
Intentionally fail to configure
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -60,3 +60,7 @@ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/dir
install(EXPORT FooTargets DESTINATION lib/cmake/foo)
install(SCRIPT InstallScript.cmake)
install(CODE "message(foo)" ALL_COMPONENTS)
if(FAIL)
message(FATAL_ERROR "Intentionally fail to configure")
endif()

View File

@@ -0,0 +1,16 @@
set(expect
query
query/client-bar
query/client-bar/query.json
query/client-foo
query/client-foo/configureLog-v1
query/configureLog-v1
reply
reply/configureLog-v1-[0-9a-f]+.json
reply/error-[0-9.T-]+.json
reply/index-[0-9.T-]+.json
)
check_api("^${expect}$")
check_python(configureLog-v1-FailConfigure error)
check_python(configureLog-v1 index) # Last-good index is intact.

View File

@@ -0,0 +1,54 @@
from check_index import *
import os
def check_reply(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"client-bar",
"client-foo",
"configureLog-v1",
]
check_reply_client_bar(r["client-bar"])
check_reply_client_foo(r["client-foo"])
check_index_object(r["configureLog-v1"], "configureLog", 1, 0, None)
def check_reply_client_bar(r):
assert is_dict(r)
assert sorted(r.keys()) == ["query.json"]
query = r["query.json"]
assert sorted(query.keys()) == ["requests", "responses"]
requests = query["requests"]
assert is_list(requests)
assert len(requests) == 1
responses = query["responses"]
assert is_list(responses)
assert len(responses) == 1
check_index_object(responses[0], "configureLog", 1, 0, None)
def check_reply_client_foo(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"configureLog-v1",
]
check_index_object(r["configureLog-v1"], "configureLog", 1, 0, None)
def check_objects(o):
assert is_list(o)
assert len(o) == 1
check_index_object(o[0], "configureLog", 1, 0, check_object_configureLog)
def check_object_configureLog(o):
assert sorted(o.keys()) == ["eventKindNames", "kind", "path", "version"]
# The "kind" and "version" members are handled by check_index_object.
path = o["path"]
assert matches(path, "^.*/CMakeFiles/CMakeConfigureLog\\.yaml$")
assert os.path.exists(path)
eventKindNames = o["eventKindNames"]
assert is_list(eventKindNames)
assert sorted(eventKindNames) == ["message-v1", "try_compile-v1", "try_run-v1"]
assert is_dict(index)
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
check_cmake(index["cmake"])
check_reply(index["reply"])
check_objects(index["objects"])

View File

@@ -0,0 +1,6 @@
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/configureLog-v1" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/configureLog-v1" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-bar/query.json" [[
{ "requests": [ { "kind": "configureLog", "version" : 1 } ] }
]])

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at configureLog-v1.cmake:[0-9]+ \(message\):
Intentionally fail to configure
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -1 +1,4 @@
enable_language(C)
if(FAIL)
message(FATAL_ERROR "Intentionally fail to configure")
endif()

View File

@@ -0,0 +1,16 @@
set(expect
query
query/client-bar
query/client-bar/query.json
query/client-foo
query/client-foo/toolchains-v1
query/toolchains-v1
reply
reply/error-[0-9.T-]+.json
reply/index-[0-9.T-]+.json
reply/toolchains-v1-[0-9a-f]+\\.json
)
check_api("^${expect}$")
check_python(toolchains-v1-FailConfigure error)
check_python(toolchains-v1 index) # Last-good index is intact.

View File

@@ -0,0 +1,43 @@
from check_index import *
import os
def check_reply(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"client-bar",
"client-foo",
"toolchains-v1",
]
check_reply_client_bar(r["client-bar"])
check_reply_client_foo(r["client-foo"])
check_error(r["toolchains-v1"], "no buildsystem generated")
def check_reply_client_bar(r):
assert is_dict(r)
assert sorted(r.keys()) == ["query.json"]
query = r["query.json"]
assert sorted(query.keys()) == ["requests", "responses"]
requests = query["requests"]
assert is_list(requests)
assert len(requests) == 1
responses = query["responses"]
assert is_list(responses)
assert len(responses) == 1
check_error(responses[0], "no buildsystem generated")
def check_reply_client_foo(r):
assert is_dict(r)
assert sorted(r.keys()) == [
"toolchains-v1",
]
check_error(r["toolchains-v1"], "no buildsystem generated")
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"])

View File

@@ -0,0 +1,6 @@
file(REMOVE_RECURSE ${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query)
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/toolchains-v1" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-foo/toolchains-v1" "")
file(WRITE "${RunCMake_TEST_BINARY_DIR}/.cmake/api/v1/query/client-bar/query.json" [[
{ "requests": [ { "kind": "toolchains", "version" : 1 } ] }
]])

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
^CMake Error at toolchains-v1.cmake:[0-9]+ \(message\):
Intentionally fail to configure
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -20,3 +20,7 @@ foreach(variable_suffix ${variable_suffixes})
endforeach()
file(WRITE ${CMAKE_BINARY_DIR}/toolchain_variables.json "${json}")
if(FAIL)
message(FATAL_ERROR "Intentionally fail to configure")
endif()