FileAPI: Add "configureLog" object kind

Provide clients with a way to get a known set of configure log event
versions.

Issue: #23200
This commit is contained in:
Brad King
2022-12-12 17:59:41 -05:00
parent 02599da236
commit d811d86fd7
21 changed files with 297 additions and 7 deletions

View File

@@ -73,6 +73,16 @@ they do not understand:
* If an existing build tree is re-configured with a different version of
CMake, the log may contain different versions of the same event kind.
* If :manual:`cmake-file-api(7)` queries request one or more
:ref:`configureLog <file-api configureLog>` object versions,
the log may contain multiple entries for the same event, each
with a different version of its event kind.
IDEs should write a :manual:`cmake-file-api(7)` query requesting a
specific :ref:`configureLog <file-api configureLog>` object version,
before running CMake, and then read the configure log only as described
by the file-api reply.
Text Block Encoding
-------------------

View File

@@ -1298,6 +1298,45 @@ elsewhere in the containing object. The backtrace graph object members are:
directory then the path is specified relative to that directory.
Otherwise the path is absolute.
.. _`file-api configureLog`:
Object Kind "configureLog"
--------------------------
The ``configureLog`` object kind describes the location and contents of
a :manual:`cmake-configure-log(7)` file.
There is only one ``configureLog`` object major version, version 1.
"configureLog" version 1
^^^^^^^^^^^^^^^^^^^^^^^^
``configureLog`` object version 1 is a JSON object:
.. code-block:: json
{
"kind": "configureLog",
"version": { "major": 1, "minor": 0 },
"path": "/path/to/top-level-build-dir/CMakeFiles/CMakeConfigureLog.yaml",
"eventKindNames": [ "try_compile-v1", "try_run-v1" ]
}
The members specific to ``configureLog`` objects are:
``path``
A string specifying the path to the configure log file.
Clients must read the log file from this path, which may be
different than the path documented by :manual:`cmake-configure-log(7)`.
The log file may not exist if no events are logged.
``eventKindNames``
A JSON array whose entries are each a JSON string naming one
of the :manual:`cmake-configure-log(7)` versioned event kinds.
At most one version of each configure log event kind will be listed.
Although the configure log may contain other (versioned) event kinds,
clients must ignore those that are not listed in this field.
Object Kind "cache"
-------------------

View File

@@ -3,3 +3,6 @@ Configure Log
* CMake now writes a YAML log of configure-time checks.
See the :manual:`cmake-configure-log(7)` manual.
* The :manual:`cmake-file-api(7)` gained a new "configureLog" object kind
that enables stable access to the :manual:`cmake-configure-log(7)`.

View File

@@ -237,6 +237,8 @@ add_library(
cmFileAPICache.h
cmFileAPICodemodel.cxx
cmFileAPICodemodel.h
cmFileAPIConfigureLog.cxx
cmFileAPIConfigureLog.h
cmFileAPICMakeFiles.cxx
cmFileAPICMakeFiles.h
cmFileAPIToolchains.cxx

View File

@@ -18,6 +18,7 @@
#include "cmFileAPICMakeFiles.h"
#include "cmFileAPICache.h"
#include "cmFileAPICodemodel.h"
#include "cmFileAPIConfigureLog.h"
#include "cmFileAPIToolchains.h"
#include "cmGlobalGenerator.h"
#include "cmStringAlgorithms.h"
@@ -66,6 +67,26 @@ void cmFileAPI::ReadQueries()
}
}
std::vector<unsigned long> cmFileAPI::GetConfigureLogVersions()
{
std::vector<unsigned long> versions;
auto getConfigureLogVersions = [&versions](Query const& q) {
for (Object const& o : q.Known) {
if (o.Kind == ObjectKind::ConfigureLog) {
versions.emplace_back(o.Version);
}
}
};
getConfigureLogVersions(this->TopQuery);
for (auto const& client : this->ClientQueries) {
getConfigureLogVersions(client.second.DirQuery);
}
std::sort(versions.begin(), versions.end());
versions.erase(std::unique(versions.begin(), versions.end()),
versions.end());
return versions;
}
void cmFileAPI::WriteReplies()
{
if (this->QueryExists) {
@@ -241,6 +262,17 @@ bool cmFileAPI::ReadQuery(std::string const& query,
objects.push_back(o);
return true;
}
if (kindName == ObjectKindName(ObjectKind::ConfigureLog)) {
Object o;
o.Kind = ObjectKind::ConfigureLog;
if (verStr == "v1") {
o.Version = 1;
} else {
return false;
}
objects.push_back(o);
return true;
}
if (kindName == ObjectKindName(ObjectKind::Cache)) {
Object o;
o.Kind = ObjectKind::Cache;
@@ -411,11 +443,12 @@ const char* cmFileAPI::ObjectKindName(ObjectKind kind)
{
// Keep in sync with ObjectKind enum.
static const char* objectKindNames[] = {
"codemodel", //
"cache", //
"cmakeFiles", //
"toolchains", //
"__test" //
"codemodel", //
"configureLog", //
"cache", //
"cmakeFiles", //
"toolchains", //
"__test" //
};
return objectKindNames[static_cast<size_t>(kind)];
}
@@ -442,6 +475,9 @@ Json::Value cmFileAPI::BuildObject(Object const& object)
case ObjectKind::CodeModel:
value = this->BuildCodeModel(object);
break;
case ObjectKind::ConfigureLog:
value = this->BuildConfigureLog(object);
break;
case ObjectKind::Cache:
value = this->BuildCache(object);
break;
@@ -503,6 +539,8 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
if (kindName == this->ObjectKindName(ObjectKind::CodeModel)) {
r.Kind = ObjectKind::CodeModel;
} else if (kindName == this->ObjectKindName(ObjectKind::ConfigureLog)) {
r.Kind = ObjectKind::ConfigureLog;
} else if (kindName == this->ObjectKindName(ObjectKind::Cache)) {
r.Kind = ObjectKind::Cache;
} else if (kindName == this->ObjectKindName(ObjectKind::CMakeFiles)) {
@@ -530,6 +568,9 @@ cmFileAPI::ClientRequest cmFileAPI::BuildClientRequest(
case ObjectKind::CodeModel:
this->BuildClientRequestCodeModel(r, versions);
break;
case ObjectKind::ConfigureLog:
this->BuildClientRequestConfigureLog(r, versions);
break;
case ObjectKind::Cache:
this->BuildClientRequestCache(r, versions);
break;
@@ -719,6 +760,41 @@ Json::Value cmFileAPI::BuildCodeModel(Object const& object)
return codemodel;
}
// The "configureLog" object kind.
// Update Help/manual/cmake-file-api.7.rst when updating this constant.
static unsigned int const ConfigureLogV1Minor = 0;
void cmFileAPI::BuildClientRequestConfigureLog(
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 <= ConfigureLogV1Minor)) {
r.Version = v.Major;
break;
}
}
if (!r.Version) {
r.Error = NoSupportedVersion(versions);
}
}
Json::Value cmFileAPI::BuildConfigureLog(Object const& object)
{
Json::Value configureLog = cmFileAPIConfigureLogDump(*this, object.Version);
configureLog["kind"] = this->ObjectKindName(object.Kind);
Json::Value& version = configureLog["version"];
if (object.Version == 1) {
version = BuildVersion(1, ConfigureLogV1Minor);
} else {
return configureLog; // should be unreachable
}
return configureLog;
}
// The "cache" object kind.
static unsigned int const CacheV2Minor = 0;
@@ -868,6 +944,14 @@ Json::Value cmFileAPI::ReportCapabilities()
requests.append(std::move(request)); // NOLINT(*)
}
{
Json::Value request = Json::objectValue;
request["kind"] = ObjectKindName(ObjectKind::ConfigureLog);
Json::Value& versions = request["version"] = Json::arrayValue;
versions.append(BuildVersion(1, ConfigureLogV1Minor));
requests.append(std::move(request)); // NOLINT(*)
}
{
Json::Value request = Json::objectValue;
request["kind"] = ObjectKindName(ObjectKind::Cache);

View File

@@ -24,6 +24,9 @@ public:
/** Read fileapi queries from disk. */
void ReadQueries();
/** Get the list of configureLog object kind versions requested. */
std::vector<unsigned long> GetConfigureLogVersions();
/** Write fileapi replies to disk. */
void WriteReplies();
@@ -54,6 +57,7 @@ private:
enum class ObjectKind
{
CodeModel,
ConfigureLog,
Cache,
CMakeFiles,
Toolchains,
@@ -193,6 +197,10 @@ private:
ClientRequest& r, std::vector<RequestVersion> const& versions);
Json::Value BuildCodeModel(Object const& object);
void BuildClientRequestConfigureLog(
ClientRequest& r, std::vector<RequestVersion> const& versions);
Json::Value BuildConfigureLog(Object const& object);
void BuildClientRequestCache(ClientRequest& r,
std::vector<RequestVersion> const& versions);
Json::Value BuildCache(Object const& object);

View File

@@ -0,0 +1,67 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmFileAPIConfigureLog.h"
#include <cm3p/json/value.h>
#include "cmFileAPI.h"
#include "cmStringAlgorithms.h"
#include "cmake.h"
namespace {
class ConfigureLog
{
cmFileAPI& FileAPI;
unsigned long Version;
Json::Value DumpPath();
Json::Value DumpEventKindNames();
public:
ConfigureLog(cmFileAPI& fileAPI, unsigned long version);
Json::Value Dump();
};
ConfigureLog::ConfigureLog(cmFileAPI& fileAPI, unsigned long version)
: FileAPI(fileAPI)
, Version(version)
{
static_cast<void>(this->Version);
}
Json::Value ConfigureLog::Dump()
{
Json::Value configureLog = Json::objectValue;
configureLog["path"] = this->DumpPath();
configureLog["eventKindNames"] = this->DumpEventKindNames();
return configureLog;
}
Json::Value ConfigureLog::DumpPath()
{
return cmStrCat(this->FileAPI.GetCMakeInstance()->GetHomeOutputDirectory(),
"/CMakeFiles/CMakeConfigureLog.yaml");
}
Json::Value ConfigureLog::DumpEventKindNames()
{
// Report at most one version of each event kind.
// If a new event kind is added, increment ConfigureLogV1Minor.
// If a new version of an existing event kind is added, a new
// major version of the configureLog object kind is needed.
Json::Value eventKindNames = Json::arrayValue;
if (this->Version == 1) {
eventKindNames.append("try_compile-v1"); // WriteTryCompileEvent
eventKindNames.append("try_run-v1"); // WriteTryRunEvent
}
return eventKindNames;
}
}
Json::Value cmFileAPIConfigureLogDump(cmFileAPI& fileAPI,
unsigned long version)
{
ConfigureLog configureLog(fileAPI, version);
return configureLog.Dump();
}

View File

@@ -0,0 +1,12 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
#include "cmConfigure.h" // IWYU pragma: keep
#include <cm3p/json/value.h>
class cmFileAPI;
extern Json::Value cmFileAPIConfigureLogDump(cmFileAPI& fileAPI,
unsigned long version);

View File

@@ -21,6 +21,7 @@ namespace {
void WriteTryCompileEvent(cmConfigureLog& log, cmMakefile const& mf,
cmTryCompileResult const& compileResult)
{
// Keep in sync with cmFileAPIConfigureLog's DumpEventKindNames.
static const std::vector<unsigned long> LogVersionsWithTryCompileV1{ 1 };
if (log.IsAnyLogVersionEnabled(LogVersionsWithTryCompileV1)) {

View File

@@ -40,6 +40,7 @@ void WriteTryRunEvent(cmConfigureLog& log, cmMakefile const& mf,
cmTryCompileResult const& compileResult,
cmTryRunResult const& runResult)
{
// Keep in sync with cmFileAPIConfigureLog's DumpEventKindNames.
static const std::vector<unsigned long> LogVersionsWithTryRunV1{ 1 };
if (log.IsAnyLogVersionEnabled(LogVersionsWithTryRunV1)) {

View File

@@ -2429,7 +2429,7 @@ int cmake::ActualConfigure()
this->TruncateOutputLog("CMakeConfigureLog.yaml");
this->ConfigureLog = cm::make_unique<cmConfigureLog>(
cmStrCat(this->GetHomeOutputDirectory(), "/CMakeFiles"_s),
std::vector<unsigned long>());
this->FileAPI->GetConfigureLogVersions());
}
#endif

View File

@@ -1 +1 @@
^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":5}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$
^{"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":5}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$

View File

@@ -65,6 +65,7 @@ function(run_object object)
endfunction()
run_object(codemodel-v2)
run_object(configureLog-v1)
run_object(cache-v2)
run_object(cmakeFiles-v1)
run_object(toolchains-v1)

View File

@@ -0,0 +1,11 @@
set(expect
query
query/client-foo
query/client-foo/query.json
reply
reply/configureLog-v1-[0-9a-f]+.json
reply/index-[0-9.T-]+.json
)
check_api("^${expect}$")
check_python(configureLog-v1)

View File

@@ -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": "configureLog", "version" : 1 } ] }
]])

View File

@@ -0,0 +1,11 @@
set(expect
query
query/client-foo
query/client-foo/configureLog-v1
reply
reply/configureLog-v1-[0-9a-f]+.json
reply/index-[0-9.T-]+.json
)
check_api("^${expect}$")
check_python(configureLog-v1)

View File

@@ -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/configureLog-v1" "")

View File

@@ -0,0 +1,10 @@
set(expect
query
query/configureLog-v1
reply
reply/configureLog-v1-[0-9a-f]+.json
reply/index-[0-9.T-]+.json
)
check_api("^${expect}$")
check_python(configureLog-v1)

View File

@@ -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/configureLog-v1" "")

View File

@@ -0,0 +1,21 @@
from check_index import *
import os
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) == ["try_compile-v1", "try_run-v1"]
assert is_dict(index)
assert sorted(index.keys()) == ["cmake", "objects", "reply"]
check_objects(index["objects"])

View File

@@ -0,0 +1 @@
enable_language(C)