mirror of
https://github.com/Kitware/CMake.git
synced 2026-06-26 09:48:16 +00:00
instrumentation: Revise Data Version format
Give data version the new format <major>.<minor>, so that the version can be incremented with the introduction of new features without bumping the major version. Add support for loading instrumentation JSON queries of unknown data versions without error. Issue: #27833
This commit is contained in:
@@ -19,10 +19,14 @@ This allows for configuring instrumentation at the project-level.
|
||||
[CUSTOM_CONTENT <name> <type> <content>]
|
||||
)
|
||||
|
||||
The ``API_VERSION`` and ``DATA_VERSION`` must always be given. Currently, the
|
||||
only supported value for both fields is 1. See
|
||||
:ref:`cmake-instrumentation API v1` for details of the ``API_VERSION`` and
|
||||
:ref:`cmake-instrumentation Data Version` for details of the ``DATA_VERSION``.
|
||||
The ``API_VERSION`` and ``DATA_VERSION`` must always be given.
|
||||
|
||||
``API_VERSION`` is an integer. Currently, the only supported value is ``1``.
|
||||
See :ref:`cmake-instrumentation API v1` for details.
|
||||
|
||||
``DATA_VERSION`` is a version value of the form ``major`` or ``major.minor``.
|
||||
Currently, the only supported version is ``1.0``. See
|
||||
:ref:`cmake-instrumentation Data Version` for details.
|
||||
|
||||
Each of the optional keywords ``HOOKS``, ``OPTIONS``, and ``CALLBACK``
|
||||
correspond to one of the parameters to the :ref:`cmake-instrumentation v1 Query Files`.
|
||||
@@ -70,7 +74,7 @@ equivalent JSON query file.
|
||||
|
||||
cmake_instrumentation(
|
||||
API_VERSION 1
|
||||
DATA_VERSION 1
|
||||
DATA_VERSION 1.0
|
||||
HOOKS postGenerate preCMakeBuild postCMakeBuild
|
||||
OPTIONS staticSystemInformation dynamicSystemInformation trace
|
||||
CALLBACK ${CMAKE_COMMAND} -P /path/to/handle_data.cmake
|
||||
@@ -91,7 +95,7 @@ equivalent JSON query file.
|
||||
"staticSystemInformation", "dynamicSystemInformation", "trace"
|
||||
],
|
||||
"callbacks": [
|
||||
"/path/to/cmake -P /path/to/handle_data.cmake"
|
||||
"/path/to/cmake -P /path/to/handle_data.cmake",
|
||||
"/path/to/cmake -P /path/to/handle_data_2.cmake"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -229,9 +229,11 @@ request a specific Data Version, and `v1 Data Files`_ of the corresponding
|
||||
version will be generated and sent to the user `Callbacks`_ defined in that
|
||||
query.
|
||||
|
||||
Currently, the only supported version is ``1``. A new version number will be
|
||||
created whenever previously included data is removed or reformatted such that
|
||||
scripts written to parse this data may become incompatible with the new format.
|
||||
Currently, the only supported version is ``1.0``. A new major version number
|
||||
will be created whenever previously included data is removed or reformatted such
|
||||
that scripts written to parse this data may become incompatible with the new
|
||||
format. A new minor version number will be created whenever new data becomes
|
||||
available.
|
||||
|
||||
.. _`cmake-instrumentation v1 Query Files`:
|
||||
|
||||
@@ -245,8 +247,17 @@ These files must contain a JSON object with the following keys. The ``version``
|
||||
key is required, but all other fields are optional.
|
||||
|
||||
``version``
|
||||
The `Data Version`_ of snippet file to generate, an integer. Currently the only
|
||||
supported version is ``1``.
|
||||
The `Data Version`_ of snippet files to generate.
|
||||
|
||||
In query files, this may be specified either as an integer major version or
|
||||
as an object with ``major`` and ``minor`` members. For example, ``1`` and
|
||||
``{ "major": 1, "minor": 0 }`` both request version ``1.0``. Specifying a
|
||||
minor version is optional. CMake will always generate instrumentation data
|
||||
for the most recent minor version, even if an earlier minor version is
|
||||
requested.
|
||||
|
||||
Currently, the only supported version is ``1.0``. Query files with an unknown
|
||||
data version will be ignored.
|
||||
|
||||
``callbacks``
|
||||
A list of command-line strings for `Callbacks`_ to handle collected
|
||||
@@ -402,8 +413,8 @@ Snippet files have a filename with the syntax
|
||||
``<role>-<hash>-<timestamp>.json`` and contain the following data:
|
||||
|
||||
``version``
|
||||
The `Data Version`_ of the snippet file, an integer. Currently the version is
|
||||
always ``1``.
|
||||
The `Data Version`_ of the snippet file. Currently the version is
|
||||
always ``{ "major": 1, "minor": 0 }``.
|
||||
|
||||
``command``
|
||||
The full command executed. Excluded when ``role`` is ``build``.
|
||||
@@ -507,7 +518,10 @@ Example:
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"version": 1,
|
||||
"version": {
|
||||
"major": 1,
|
||||
"minor": 0
|
||||
},
|
||||
"command" : "\"/usr/bin/c++\" \"-MD\" \"-MT\" \"CMakeFiles/main.dir/main.cxx.o\" \"-MF\" \"CMakeFiles/main.dir/main.cxx.o.d\" \"-o\" \"CMakeFiles/main.dir/main.cxx.o\" \"-c\" \"<src>/main.cxx\"",
|
||||
"role" : "compile",
|
||||
"result" : 1,
|
||||
@@ -538,8 +552,8 @@ generated whenever `Indexing`_ occurs and deleted after any user-specified
|
||||
`Callbacks`_ are executed.
|
||||
|
||||
``version``
|
||||
The `Data Version`_ of the index file, an integer. Currently the version is
|
||||
always ``1``.
|
||||
The `Data Version`_ of the index file. Currently this is always written as:
|
||||
``{ "major": 1, "minor": 0 }``.
|
||||
|
||||
``buildDir``
|
||||
The build directory of the CMake project.
|
||||
@@ -604,7 +618,10 @@ Example:
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"version": 1,
|
||||
"version": {
|
||||
"major": 1,
|
||||
"minor": 0
|
||||
},
|
||||
"hook": "manual",
|
||||
"buildDir": "<build>",
|
||||
"dataDir": "<build>/.cmake/instrumentation/v1/data",
|
||||
@@ -633,6 +650,10 @@ corresponding to the CMake invocation responsible for generating its command.
|
||||
|
||||
Each CMake content file contains the following:
|
||||
|
||||
``version``
|
||||
The `Data Version`_ of the content file. Currently the version is
|
||||
always ``{ "major": 1, "minor": 0 }``.
|
||||
|
||||
``project``
|
||||
The value of :variable:`CMAKE_PROJECT_NAME`.
|
||||
|
||||
|
||||
@@ -4,10 +4,29 @@
|
||||
"required": ["version"],
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "integer",
|
||||
"description": "The data version of snippet file to generate.",
|
||||
"enum": [
|
||||
1
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "integer",
|
||||
"const": 1
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"major"
|
||||
],
|
||||
"properties": {
|
||||
"major": {
|
||||
"type": "integer",
|
||||
"const": 1
|
||||
},
|
||||
"minor": {
|
||||
"type": "integer",
|
||||
"const": 0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"callbacks": {
|
||||
|
||||
@@ -44,6 +44,11 @@
|
||||
|
||||
using LoadQueriesAfter = cmInstrumentation::LoadQueriesAfter;
|
||||
|
||||
namespace {
|
||||
cmInstrumentationQuery::Version latestDataVersion =
|
||||
cmInstrumentationQuery::LatestDataVersion();
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> cmInstrumentation::cdashSnippetsMap = {
|
||||
{
|
||||
"configure",
|
||||
@@ -145,7 +150,7 @@ void cmInstrumentation::CheckCDashVariable()
|
||||
options_.insert(cmInstrumentationQuery::Option::CDashVerbose);
|
||||
}
|
||||
std::set<cmInstrumentationQuery::Hook> hooks_;
|
||||
this->WriteJSONQuery(options_, hooks_, {});
|
||||
this->WriteJSONQuery(latestDataVersion, options_, hooks_, {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,12 +203,12 @@ bool cmInstrumentation::HasErrors() const
|
||||
}
|
||||
|
||||
void cmInstrumentation::WriteJSONQuery(
|
||||
cmInstrumentationQuery::Version dataVersion,
|
||||
std::set<cmInstrumentationQuery::Option> const& options_,
|
||||
std::set<cmInstrumentationQuery::Hook> const& hooks_,
|
||||
std::vector<std::vector<std::string>> const& callbacks_)
|
||||
{
|
||||
Json::Value root;
|
||||
root["version"] = 1;
|
||||
root["options"] = Json::arrayValue;
|
||||
for (auto const& option : options_) {
|
||||
root["options"].append(cmInstrumentationQuery::OptionString[option]);
|
||||
@@ -217,7 +222,7 @@ void cmInstrumentation::WriteJSONQuery(
|
||||
root["callbacks"].append(cmInstrumentation::GetCommandStr(callback));
|
||||
}
|
||||
this->WriteInstrumentationJson(
|
||||
root, "query/generated",
|
||||
dataVersion, root, "query/generated",
|
||||
cmStrCat("query-", this->writtenJsonQueries++, ".json"));
|
||||
}
|
||||
|
||||
@@ -236,7 +241,7 @@ void cmInstrumentation::WriteCMakeContent(
|
||||
root["project"] =
|
||||
gg->GetCMakeInstance()->GetCacheDefinition("CMAKE_PROJECT_NAME").GetCStr();
|
||||
this->WriteInstrumentationJson(
|
||||
root, "data/content",
|
||||
latestDataVersion, root, "data/content",
|
||||
cmStrCat("cmake-", this->ComputeSuffixTime(), ".json"));
|
||||
}
|
||||
|
||||
@@ -362,6 +367,8 @@ int cmInstrumentation::CollectTimingData(cmInstrumentationQuery::Hook hook)
|
||||
cmsys::Directory d;
|
||||
std::string last_index_name =
|
||||
this->GetFileByTimestamp(LatestOrOldest::Latest, "index", index_name);
|
||||
std::string last_index_path =
|
||||
cmStrCat(this->dataDir, "/index/", last_index_name);
|
||||
if (d.Load(this->dataDir)) {
|
||||
for (unsigned int i = 0; i < d.GetNumberOfFiles(); i++) {
|
||||
std::string fpath = d.GetFilePath(i);
|
||||
@@ -379,18 +386,16 @@ int cmInstrumentation::CollectTimingData(cmInstrumentationQuery::Hook hook)
|
||||
index["hook"] = cmInstrumentationQuery::HookString[hook];
|
||||
index["dataDir"] = this->dataDir;
|
||||
index["buildDir"] = this->binaryDir;
|
||||
index["version"] = 1;
|
||||
if (this->HasOption(
|
||||
cmInstrumentationQuery::Option::StaticSystemInformation)) {
|
||||
this->InsertStaticSystemInformation(index);
|
||||
}
|
||||
|
||||
for (auto const& file : files) {
|
||||
if (last_index_name.empty()) {
|
||||
index["snippets"].append(file.first);
|
||||
} else {
|
||||
int compare;
|
||||
std::string last_index_path =
|
||||
cmStrCat(this->dataDir, "/index/", last_index_name);
|
||||
cmSystemTools::FileTimeCompare(file.second, last_index_path, &compare);
|
||||
if (compare == 1) {
|
||||
index["snippets"].append(file.first);
|
||||
@@ -406,13 +411,14 @@ int cmInstrumentation::CollectTimingData(cmInstrumentationQuery::Hook hook)
|
||||
}
|
||||
|
||||
// Write index file
|
||||
this->WriteInstrumentationJson(index, "data/index", index_name);
|
||||
this->WriteInstrumentationJson(latestDataVersion, index, "data/index",
|
||||
index_name);
|
||||
|
||||
// Execute callbacks
|
||||
for (auto& cb : this->callbacks) {
|
||||
cmSystemTools::RunSingleCommand(cmStrCat(cb, " \"", index_path, '"'),
|
||||
nullptr, nullptr, nullptr, nullptr,
|
||||
cmSystemTools::OUTPUT_PASSTHROUGH);
|
||||
for (auto const& cb : this->callbacks) {
|
||||
cmSystemTools::RunSingleCommand(
|
||||
cmStrCat(cb.Command, " \"", index_path, '"'), nullptr, nullptr, nullptr,
|
||||
nullptr, cmSystemTools::OUTPUT_PASSTHROUGH);
|
||||
}
|
||||
|
||||
// Special case for CDash collation
|
||||
@@ -545,10 +551,14 @@ Json::Value cmInstrumentation::ReadJsonSnippet(std::string const& file_name)
|
||||
return snippetData;
|
||||
}
|
||||
|
||||
void cmInstrumentation::WriteInstrumentationJson(Json::Value& root,
|
||||
std::string const& subdir,
|
||||
std::string const& file_name)
|
||||
void cmInstrumentation::WriteInstrumentationJson(
|
||||
cmInstrumentationQuery::Version version, Json::Value& root,
|
||||
std::string const& subdir, std::string const& file_name)
|
||||
{
|
||||
root["version"] = Json::objectValue;
|
||||
root["version"]["major"] = version.Major;
|
||||
root["version"]["minor"] = version.Minor;
|
||||
|
||||
Json::StreamWriterBuilder wbuilder;
|
||||
wbuilder["indentation"] = "\t";
|
||||
std::unique_ptr<Json::StreamWriter> JsonWriter =
|
||||
@@ -581,7 +591,6 @@ std::string cmInstrumentation::InstrumentTest(
|
||||
// Store command info
|
||||
Json::Value root(this->preTestStats);
|
||||
std::string command_str = cmStrCat(command, ' ', GetCommandStr(args));
|
||||
root["version"] = 1;
|
||||
root["command"] = command_str;
|
||||
root["role"] = "test";
|
||||
root["testName"] = name;
|
||||
@@ -603,7 +612,7 @@ std::string cmInstrumentation::InstrumentTest(
|
||||
"test-",
|
||||
this->ComputeSuffixHash(cmStrCat(command_str, info.GetProcessId())), '-',
|
||||
this->ComputeSuffixTime(endTime), ".json");
|
||||
this->WriteInstrumentationJson(root, "data", file_name);
|
||||
this->WriteInstrumentationJson(latestDataVersion, root, "data", file_name);
|
||||
return file_name;
|
||||
}
|
||||
|
||||
@@ -637,7 +646,6 @@ int cmInstrumentation::InstrumentCommand(
|
||||
if (!command_str.empty()) {
|
||||
root["command"] = command_str;
|
||||
}
|
||||
root["version"] = 1;
|
||||
|
||||
// Pre-Command
|
||||
auto steady_start = std::chrono::steady_clock::now();
|
||||
@@ -759,11 +767,12 @@ int cmInstrumentation::InstrumentCommand(
|
||||
} else {
|
||||
addCMakeContent(it->second);
|
||||
}
|
||||
this->WriteInstrumentationJson(it->second, "data", it->first);
|
||||
this->WriteInstrumentationJson(latestDataVersion, it->second, "data",
|
||||
it->first);
|
||||
}
|
||||
this->configureSnippetData.clear();
|
||||
}
|
||||
this->WriteInstrumentationJson(root, "data", file_name);
|
||||
this->WriteInstrumentationJson(latestDataVersion, root, "data", file_name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ class cmGlobalGenerator;
|
||||
class cmInstrumentation
|
||||
{
|
||||
public:
|
||||
using Callback = cmInstrumentationQuery::Callback;
|
||||
|
||||
enum class LoadQueriesAfter
|
||||
{
|
||||
Yes,
|
||||
@@ -60,7 +62,8 @@ public:
|
||||
bool HasHook(cmInstrumentationQuery::Hook hook) const;
|
||||
bool ReadJSONQueries(std::string const& directory);
|
||||
void ReadJSONQuery(std::string const& file);
|
||||
void WriteJSONQuery(std::set<cmInstrumentationQuery::Option> const& options,
|
||||
void WriteJSONQuery(cmInstrumentationQuery::Version dataVersion,
|
||||
std::set<cmInstrumentationQuery::Option> const& options,
|
||||
std::set<cmInstrumentationQuery::Hook> const& hooks,
|
||||
std::vector<std::vector<std::string>> const& callback);
|
||||
void AddCustomContent(std::string const& name, Json::Value const& contents);
|
||||
@@ -90,7 +93,8 @@ private:
|
||||
Json::Value ReadJsonSnippet(std::string const& file_name);
|
||||
bool AcquireLock(std::string const& lock_file, cmFileLock& lock,
|
||||
unsigned long timeout);
|
||||
void WriteInstrumentationJson(Json::Value& index,
|
||||
void WriteInstrumentationJson(cmInstrumentationQuery::Version version,
|
||||
Json::Value& index,
|
||||
std::string const& directory,
|
||||
std::string const& file_name);
|
||||
void InsertStaticSystemInformation(Json::Value& index);
|
||||
@@ -122,7 +126,7 @@ private:
|
||||
std::string dataDir;
|
||||
std::set<cmInstrumentationQuery::Option> options;
|
||||
std::set<cmInstrumentationQuery::Hook> hooks;
|
||||
std::vector<std::string> callbacks;
|
||||
std::vector<Callback> callbacks;
|
||||
std::vector<std::string> queryFiles;
|
||||
static std::map<std::string, std::string> cdashSnippetsMap;
|
||||
Json::Value preTestStats;
|
||||
|
||||
@@ -29,6 +29,8 @@ file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
|
||||
namespace {
|
||||
|
||||
using Version = cmInstrumentationQuery::Version;
|
||||
|
||||
bool validateVersion(std::string const& key, std::string const& versionString,
|
||||
int& version, cmExecutionStatus& status)
|
||||
{
|
||||
@@ -47,6 +49,45 @@ bool validateVersion(std::string const& key, std::string const& versionString,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool validateDataVersion(std::string const& versionString, Version& version,
|
||||
cmExecutionStatus& status)
|
||||
{
|
||||
char const* vStart = versionString.c_str();
|
||||
if (!std::all_of(versionString.begin(), versionString.end(), [](char c) {
|
||||
return cmsysString_isdigit(c) || c == '.';
|
||||
})) {
|
||||
status.SetError(
|
||||
cmStrCat("given a malformed DATA_VERSION \"", versionString,
|
||||
"\". A numeric major or major.minor version is required."));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
version.Major = std::atoi(vStart);
|
||||
version.Minor = 0;
|
||||
std::string::size_type pos = versionString.find('.');
|
||||
if (pos != std::string::npos) {
|
||||
vStart += pos + 1;
|
||||
version.Minor = std::atoi(vStart);
|
||||
}
|
||||
|
||||
if (version.Major < 1 || version.Minor < 0) {
|
||||
status.SetError(
|
||||
cmStrCat("given a malformed DATA_VERSION \"", versionString,
|
||||
"\". A numeric major or major.minor version is required."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cmInstrumentationQuery::ValidDataVersion(version)) {
|
||||
status.SetError(
|
||||
cmStrCat("given an unsupported DATA_VERSION \"", versionString,
|
||||
"\" (the only currently supported version is 1.0)."));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
std::function<bool(std::string const&, E&)> EnumParser(
|
||||
std::vector<std::string> const toString)
|
||||
@@ -110,11 +151,10 @@ bool cmInstrumentationCommand(std::vector<std::string> const& args,
|
||||
return false;
|
||||
}
|
||||
int apiVersion;
|
||||
int dataVersion;
|
||||
Version dataVersion;
|
||||
if (!validateVersion("API_VERSION", arguments.ApiVersion, apiVersion,
|
||||
status) ||
|
||||
!validateVersion("DATA_VERSION", arguments.DataVersion, dataVersion,
|
||||
status)) {
|
||||
!validateDataVersion(arguments.DataVersion, dataVersion, status)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -182,7 +222,8 @@ bool cmInstrumentationCommand(std::vector<std::string> const& args,
|
||||
}
|
||||
|
||||
// Write query file
|
||||
instrumentation->WriteJSONQuery(options, hooks, arguments.Callbacks);
|
||||
instrumentation->WriteJSONQuery(dataVersion, options, hooks,
|
||||
arguments.Callbacks);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
@@ -47,6 +46,7 @@ JsonErrors::ErrorGenerator InvalidRootQueryObject(
|
||||
};
|
||||
|
||||
using JSONHelperBuilder = cmJSONHelperBuilder;
|
||||
using Version = cmInstrumentationQuery::Version;
|
||||
|
||||
template <typename E>
|
||||
static std::function<bool(E&, Json::Value const*, cmJSONState*)> EnumHelper(
|
||||
@@ -67,7 +67,7 @@ static std::function<bool(E&, Json::Value const*, cmJSONState*)> EnumHelper(
|
||||
}
|
||||
static auto const OptionHelper = EnumHelper<cmInstrumentationQuery::Option>(
|
||||
cmInstrumentationQuery::OptionString, "option");
|
||||
static auto const QueryListHelper =
|
||||
static auto const OptionListHelper =
|
||||
JSONHelperBuilder::Vector<cmInstrumentationQuery::Option>(
|
||||
ErrorMessages::InvalidArray, OptionHelper);
|
||||
static auto const HookHelper = EnumHelper<cmInstrumentationQuery::Hook>(
|
||||
@@ -78,7 +78,36 @@ static auto const HookListHelper =
|
||||
static auto const CallbackHelper = JSONHelperBuilder::String();
|
||||
static auto const CallbackListHelper = JSONHelperBuilder::Vector<std::string>(
|
||||
ErrorMessages::InvalidArray, CallbackHelper);
|
||||
static auto const VersionHelper = JSONHelperBuilder::Int();
|
||||
|
||||
JsonErrors::ErrorGenerator InvalidVersionObject(
|
||||
JsonErrors::ObjectError errorType, Json::Value::Members const& extraFields)
|
||||
{
|
||||
return JsonErrors::INVALID_NAMED_OBJECT(
|
||||
[](Json::Value const*, cmJSONState*) -> std::string {
|
||||
return "version object";
|
||||
})(errorType, extraFields);
|
||||
}
|
||||
|
||||
static auto const VersionObjectHelper =
|
||||
JSONHelperBuilder::Object<Version>(InvalidVersionObject, false)
|
||||
.Bind("major"_s, &Version::Major, JSONHelperBuilder::Int(), true)
|
||||
.Bind("minor"_s, &Version::Minor, JSONHelperBuilder::Int(), false);
|
||||
|
||||
bool VersionHelper(Version& out, Json::Value const* value, cmJSONState* state)
|
||||
{
|
||||
out.Minor = 0;
|
||||
if (value->isInt()) {
|
||||
out.Major = value->asInt();
|
||||
} else if (value->isObject()) {
|
||||
if (!VersionObjectHelper(out, value, state)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
state->AddErrorAtValue("Version must be an integer or object", value);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
using QueryRoot = cmInstrumentationQuery::QueryJSONRoot;
|
||||
|
||||
@@ -86,22 +115,35 @@ static auto const QueryRootHelper =
|
||||
JSONHelperBuilder::Object<QueryRoot>(ErrorMessages::InvalidRootQueryObject,
|
||||
false)
|
||||
.Bind("version"_s, &QueryRoot::version, VersionHelper, true)
|
||||
.Bind("options"_s, &QueryRoot::options, QueryListHelper, false)
|
||||
.Bind("options"_s, &QueryRoot::options, OptionListHelper, false)
|
||||
.Bind("hooks"_s, &QueryRoot::hooks, HookListHelper, false)
|
||||
.Bind("callbacks"_s, &QueryRoot::callbacks, CallbackListHelper, false);
|
||||
|
||||
static auto const QueryRootVersionOnlyHelper =
|
||||
JSONHelperBuilder::Object<QueryRoot>(ErrorMessages::InvalidRootQueryObject,
|
||||
true)
|
||||
.Bind("version"_s, &QueryRoot::version, VersionHelper, true);
|
||||
|
||||
bool cmInstrumentationQuery::ReadJSON(std::string const& filename,
|
||||
std::string& errorMessage,
|
||||
std::set<Option>& options,
|
||||
std::set<Hook>& hooks,
|
||||
std::vector<std::string>& callbacks)
|
||||
std::vector<Callback>& callbacks)
|
||||
{
|
||||
Json::Value root;
|
||||
this->parseState = cmJSONState(filename, &root);
|
||||
if (!this->parseState.errors.empty()) {
|
||||
std::cerr << this->parseState.GetErrorMessage(true) << std::endl;
|
||||
errorMessage = this->parseState.GetErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
if (!QueryRootVersionOnlyHelper(this->queryRoot, &root, &this->parseState)) {
|
||||
errorMessage = this->parseState.GetErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
if (!ValidDataVersion(this->queryRoot.version)) {
|
||||
// Ignore invalid data versions
|
||||
return true;
|
||||
}
|
||||
if (!QueryRootHelper(this->queryRoot, &root, &this->parseState)) {
|
||||
errorMessage = this->parseState.GetErrorMessage(true);
|
||||
return false;
|
||||
@@ -110,7 +152,22 @@ bool cmInstrumentationQuery::ReadJSON(std::string const& filename,
|
||||
std::inserter(options, options.end()));
|
||||
std::move(this->queryRoot.hooks.begin(), this->queryRoot.hooks.end(),
|
||||
std::inserter(hooks, hooks.end()));
|
||||
std::move(this->queryRoot.callbacks.begin(), this->queryRoot.callbacks.end(),
|
||||
std::back_inserter(callbacks));
|
||||
for (auto const& callback : this->queryRoot.callbacks) {
|
||||
callbacks.push_back({ callback, this->queryRoot.version });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmInstrumentationQuery::ValidDataVersion(Version version)
|
||||
{
|
||||
auto const latest = LatestDataVersion();
|
||||
return version.Major == latest.Major && version.Minor == latest.Minor;
|
||||
}
|
||||
|
||||
Version cmInstrumentationQuery::LatestDataVersion()
|
||||
{
|
||||
Version latest;
|
||||
latest.Major = 1;
|
||||
latest.Minor = 0;
|
||||
return latest;
|
||||
}
|
||||
|
||||
@@ -37,18 +37,32 @@ public:
|
||||
};
|
||||
static std::vector<std::string> const HookString;
|
||||
|
||||
struct Version
|
||||
{
|
||||
int Major = 0;
|
||||
int Minor = 0;
|
||||
};
|
||||
|
||||
struct Callback
|
||||
{
|
||||
std::string Command;
|
||||
Version DataVersion;
|
||||
};
|
||||
|
||||
struct QueryJSONRoot
|
||||
{
|
||||
std::vector<cmInstrumentationQuery::Option> options;
|
||||
std::vector<cmInstrumentationQuery::Hook> hooks;
|
||||
std::vector<std::string> callbacks;
|
||||
int version;
|
||||
Version version;
|
||||
};
|
||||
|
||||
cmInstrumentationQuery() = default;
|
||||
bool ReadJSON(std::string const& file, std::string& errorMessage,
|
||||
std::set<Option>& options, std::set<Hook>& hooks,
|
||||
std::vector<std::string>& callbacks);
|
||||
std::vector<Callback>& callbacks);
|
||||
QueryJSONRoot queryRoot;
|
||||
cmJSONState parseState;
|
||||
static Version LatestDataVersion();
|
||||
static bool ValidDataVersion(Version version);
|
||||
};
|
||||
|
||||
@@ -63,7 +63,7 @@ function(instrument test)
|
||||
if (EXISTS ${query})
|
||||
file(MAKE_DIRECTORY ${v1}/query)
|
||||
configure_file(${query} ${v1}/query/${test}.json)
|
||||
else ()
|
||||
else()
|
||||
if (NOT EXISTS ${cmake_file} AND NOT EXISTS ${cmake_file}.in)
|
||||
set(cmake_file ${query_dir}/default.cmake)
|
||||
endif()
|
||||
@@ -195,6 +195,18 @@ instrument(empty BAD_QUERY
|
||||
instrument(bad-version BAD_QUERY
|
||||
CHECK_SCRIPT check-query-dir.cmake
|
||||
)
|
||||
instrument(bad-version-major BAD_QUERY
|
||||
CHECK_SCRIPT check-query-dir.cmake
|
||||
)
|
||||
instrument(bad-version-minor BAD_QUERY
|
||||
CHECK_SCRIPT check-query-dir.cmake
|
||||
)
|
||||
instrument(bad-version-object BAD_QUERY
|
||||
CHECK_SCRIPT check-query-dir.cmake
|
||||
)
|
||||
instrument(hooks-invalid-version-ignored BUILD
|
||||
CHECK_SCRIPT check-hooks-invalid-version-ignored.cmake
|
||||
)
|
||||
|
||||
# Verify Hooks Run and Index File
|
||||
instrument(hooks-1 BUILD INSTALL TEST STATIC_QUERY
|
||||
@@ -236,6 +248,7 @@ instrument(cmake-command-data
|
||||
)
|
||||
instrument(cmake-command-bad-api-version)
|
||||
instrument(cmake-command-bad-data-version)
|
||||
instrument(cmake-command-unsupported-data-version)
|
||||
instrument(cmake-command-missing-version)
|
||||
instrument(cmake-command-bad-arg)
|
||||
instrument(cmake-command-parallel-install
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,5 @@
|
||||
^CMake Error: Could not load instrumentation queries from [^
|
||||
]+:
|
||||
bad-version-object\.json:4: Invalid extra field "patch" in version object
|
||||
"patch": 0
|
||||
\^
|
||||
@@ -15,6 +15,11 @@ set(firstFile "")
|
||||
foreach(content_file IN LISTS content_files)
|
||||
read_json("${content_file}" contents)
|
||||
|
||||
# Check version
|
||||
string(JSON version GET "${contents}" version)
|
||||
json_assert_key("${content_file}" "${version}" major "1")
|
||||
json_assert_key("${content_file}" "${version}" minor "0")
|
||||
|
||||
# Check project name
|
||||
json_assert_key("${content_file}" "${contents}" project "instrumentation")
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
if (EXISTS ${v1}/postCMakeBuild.hook)
|
||||
string(APPEND RunCMake_TEST_FAILED
|
||||
"Invalid-version query should have been ignored, but postCMakeBuild hook ran\n")
|
||||
endif()
|
||||
@@ -1,5 +1,6 @@
|
||||
CMake Error at [^
|
||||
]*\(cmake_instrumentation\):
|
||||
cmake_instrumentation given a non-integer DATA_VERSION\.
|
||||
cmake_instrumentation given a malformed DATA_VERSION "NOT_AN_INT"\. A
|
||||
numeric major or major\.minor version is required\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:5 \(include\)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
CMake Error at [^
|
||||
]*\(cmake_instrumentation\):
|
||||
cmake_instrumentation given an unsupported DATA_VERSION "" \(the only
|
||||
currently supported version is 1\)\.
|
||||
cmake_instrumentation given a malformed DATA_VERSION ""\. A numeric major
|
||||
or major\.minor version is required\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:5 \(include\)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,6 @@
|
||||
CMake Error at [^
|
||||
]*\(cmake_instrumentation\):
|
||||
cmake_instrumentation given an unsupported DATA_VERSION "1\.1" \(the only
|
||||
currently supported version is 1\.0\)\.
|
||||
Call Stack \(most recent call first\):
|
||||
CMakeLists\.txt:5 \(include\)
|
||||
@@ -6,18 +6,22 @@ include(${CMAKE_CURRENT_LIST_DIR}/verify-trace.cmake)
|
||||
|
||||
# Test CALLBACK script. Prints output information and verifies index file
|
||||
# Called as: cmake -P hook.cmake [CheckForStaticQuery?] [CheckForTrace?] [index.json]
|
||||
set(index ${CMAKE_ARGV5})
|
||||
if (NOT ${CMAKE_ARGV3})
|
||||
set(hasStaticInfo "UNEXPECTED")
|
||||
endif()
|
||||
if (NOT ${CMAKE_ARGV4})
|
||||
set(hasTrace "UNEXPECTED")
|
||||
endif()
|
||||
set(index ${CMAKE_ARGV5})
|
||||
cmake_path(GET index PARENT_PATH indexDir)
|
||||
cmake_path(GET indexDir PARENT_PATH dataDir)
|
||||
cmake_path(GET dataDir PARENT_PATH v1)
|
||||
read_json("${index}" contents)
|
||||
string(JSON hook GET "${contents}" hook)
|
||||
|
||||
# Output is verified by *-stdout.txt files that the HOOK is run
|
||||
message(STATUS ${hook})
|
||||
|
||||
# Not a check-*.cmake script, this is called as an instrumentation CALLBACK
|
||||
set(ERROR_MESSAGE "")
|
||||
function(add_error error)
|
||||
@@ -26,14 +30,17 @@ function(add_error error)
|
||||
endfunction()
|
||||
|
||||
json_has_key("${index}" "${contents}" version)
|
||||
|
||||
string(JSON version_major GET "${contents}" version major)
|
||||
string(JSON version_minor GET "${contents}" version minor)
|
||||
if (NOT version_major EQUAL 1 OR NOT version_minor EQUAL 0)
|
||||
add_error("Version must be 1.0, got: ${version_major}.${version_minor}")
|
||||
endif()
|
||||
|
||||
json_has_key("${index}" "${contents}" buildDir)
|
||||
json_has_key("${index}" "${contents}" dataDir)
|
||||
json_has_key("${index}" "${contents}" snippets)
|
||||
|
||||
if (NOT version EQUAL 1)
|
||||
add_error("Version must be 1, got: ${version}")
|
||||
endif()
|
||||
|
||||
string(JSON n_snippets LENGTH "${snippets}")
|
||||
|
||||
math(EXPR snippets_range "${n_snippets}-1")
|
||||
@@ -159,7 +166,6 @@ if (NOT hasStaticInfo STREQUAL UNEXPECTED)
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
get_filename_component(v1 ${dataDir} DIRECTORY)
|
||||
if (EXISTS ${v1}/${hook}.hook)
|
||||
add_error("Received multiple triggers of the same hook: ${hook}")
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"version": 2
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": {
|
||||
"major": 1,
|
||||
"minor": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": {
|
||||
"major": 1,
|
||||
"patch": 0
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
{
|
||||
"version": 0
|
||||
"version": {
|
||||
"major": 1,
|
||||
"minor": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"version": 1,
|
||||
"version": {
|
||||
"major": 1,
|
||||
"minor": 0
|
||||
},
|
||||
"options": [
|
||||
"staticSystemInformation",
|
||||
"dynamicSystemInformation"
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
cmake_instrumentation(
|
||||
API_VERSION 1
|
||||
DATA_VERSION 1.1
|
||||
)
|
||||
@@ -14,7 +14,7 @@
|
||||
# Query 2
|
||||
cmake_instrumentation(
|
||||
API_VERSION 1
|
||||
DATA_VERSION 1
|
||||
DATA_VERSION 1.0
|
||||
HOOKS postCMakeBuild
|
||||
OPTIONS staticSystemInformation dynamicSystemInformation trace
|
||||
CALLBACK ${CMAKE_COMMAND} -E echo callback2
|
||||
|
||||
@@ -2,5 +2,9 @@
|
||||
"callbacks" : [],
|
||||
"hooks" : [],
|
||||
"options" : [],
|
||||
"version": 1
|
||||
"version" :
|
||||
{
|
||||
"major" : 1,
|
||||
"minor" : 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,5 +11,9 @@
|
||||
"cdashSubmit",
|
||||
"cdashVerbose"
|
||||
],
|
||||
"version" : 1
|
||||
"version" :
|
||||
{
|
||||
"major" : 1,
|
||||
"minor" : 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,9 @@
|
||||
"dynamicSystemInformation",
|
||||
"trace"
|
||||
],
|
||||
"version": 1
|
||||
"version" :
|
||||
{
|
||||
"major" : 1,
|
||||
"minor" : 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": 1,
|
||||
"version": { "major": 1 },
|
||||
"hooks": ["postGenerate", "postCMakeBuild", "postCTest"],
|
||||
"callbacks": ["@GET_HOOK@"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"version": {
|
||||
"major": 1,
|
||||
"minor": 1
|
||||
},
|
||||
"hooks": ["postCMakeBuild"],
|
||||
"callbacks": ["@GET_HOOK@"]
|
||||
}
|
||||
@@ -75,9 +75,10 @@ endfunction()
|
||||
function(verify_snippet_data snippet contents)
|
||||
snippet_has_fields("${snippet}" "${contents}")
|
||||
snippet_valid_timing("${contents}")
|
||||
string(JSON version GET "${contents}" version)
|
||||
if (NOT version EQUAL 1)
|
||||
json_error("${snippet}" "Version must be 1, got: ${version}")
|
||||
string(JSON version_major GET "${contents}" version major)
|
||||
string(JSON version_minor GET "${contents}" version minor)
|
||||
if (NOT version_major EQUAL 1 OR NOT version_minor EQUAL 0)
|
||||
json_error("${snippet}" "Version must be 1.0, got: ${version_major}.${version_minor}")
|
||||
endif()
|
||||
get_filename_component(filename "${snippet}" NAME)
|
||||
string(JSON result GET "${contents}" result)
|
||||
|
||||
Reference in New Issue
Block a user