mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-01 11:22:21 -06:00
Add a `CUSTOM_CONTENT` argument to `cmake_instrumentation()` for collecting custom content from configure time. Snippet files include a reference to a JSON file containing any `CUSTOM_CONTENT` that was added by this command. Fixes: #26703
192 lines
5.8 KiB
C++
192 lines
5.8 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
#include "cmInstrumentationCommand.h"
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
#include <cstdlib>
|
|
#include <functional>
|
|
#include <set>
|
|
#include <sstream>
|
|
|
|
#include <cmext/string_view>
|
|
|
|
#include <cm3p/json/reader.h>
|
|
#include <cm3p/json/value.h>
|
|
|
|
#include "cmArgumentParser.h"
|
|
#include "cmArgumentParserTypes.h"
|
|
#include "cmExecutionStatus.h"
|
|
#include "cmExperimental.h"
|
|
#include "cmInstrumentation.h"
|
|
#include "cmInstrumentationQuery.h"
|
|
#include "cmList.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmValue.h"
|
|
#include "cmake.h"
|
|
|
|
namespace {
|
|
|
|
bool isCharDigit(char ch)
|
|
{
|
|
return std::isdigit(static_cast<unsigned char>(ch));
|
|
}
|
|
bool validateVersion(std::string const& key, std::string const& versionString,
|
|
int& version, cmExecutionStatus& status)
|
|
{
|
|
if (!std::all_of(versionString.begin(), versionString.end(), isCharDigit)) {
|
|
status.SetError(cmStrCat("given a non-integer ", key, '.'));
|
|
return false;
|
|
}
|
|
version = std::atoi(versionString.c_str());
|
|
if (version != 1) {
|
|
status.SetError(
|
|
cmStrCat("given an unsupported ", key, " \"", versionString,
|
|
"\" (the only currently supported version is 1)."));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <typename E>
|
|
std::function<bool(std::string const&, E&)> EnumParser(
|
|
std::vector<std::string> const toString)
|
|
{
|
|
return [toString](std::string const& value, E& out) -> bool {
|
|
for (size_t i = 0; i < toString.size(); ++i) {
|
|
if (value == toString[i]) {
|
|
out = (E)i;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
}
|
|
}
|
|
|
|
bool cmInstrumentationCommand(std::vector<std::string> const& args,
|
|
cmExecutionStatus& status)
|
|
{
|
|
// if (status->GetMakefile().GetPropertyKeys) {
|
|
if (!cmExperimental::HasSupportEnabled(
|
|
status.GetMakefile(), cmExperimental::Feature::Instrumentation)) {
|
|
status.SetError(
|
|
"requires the experimental Instrumentation flag to be enabled");
|
|
return false;
|
|
}
|
|
|
|
if (args.empty()) {
|
|
status.SetError("must be called with arguments.");
|
|
return false;
|
|
}
|
|
|
|
struct Arguments : public ArgumentParser::ParseResult
|
|
{
|
|
ArgumentParser::NonEmpty<std::string> ApiVersion;
|
|
ArgumentParser::NonEmpty<std::string> DataVersion;
|
|
ArgumentParser::NonEmpty<std::vector<std::string>> Options;
|
|
ArgumentParser::NonEmpty<std::vector<std::string>> Hooks;
|
|
ArgumentParser::NonEmpty<std::vector<std::vector<std::string>>> Callbacks;
|
|
ArgumentParser::NonEmpty<std::vector<std::vector<std::string>>>
|
|
CustomContent;
|
|
};
|
|
|
|
static auto const parser =
|
|
cmArgumentParser<Arguments>{}
|
|
.Bind("API_VERSION"_s, &Arguments::ApiVersion)
|
|
.Bind("DATA_VERSION"_s, &Arguments::DataVersion)
|
|
.Bind("OPTIONS"_s, &Arguments::Options)
|
|
.Bind("HOOKS"_s, &Arguments::Hooks)
|
|
.Bind("CALLBACK"_s, &Arguments::Callbacks)
|
|
.Bind("CUSTOM_CONTENT"_s, &Arguments::CustomContent);
|
|
|
|
std::vector<std::string> unparsedArguments;
|
|
Arguments const arguments = parser.Parse(args, &unparsedArguments);
|
|
|
|
if (arguments.MaybeReportError(status.GetMakefile())) {
|
|
return true;
|
|
}
|
|
if (!unparsedArguments.empty()) {
|
|
status.SetError("given unknown argument \"" + unparsedArguments.front() +
|
|
"\".");
|
|
return false;
|
|
}
|
|
int apiVersion;
|
|
int dataVersion;
|
|
if (!validateVersion("API_VERSION", arguments.ApiVersion, apiVersion,
|
|
status) ||
|
|
!validateVersion("DATA_VERSION", arguments.DataVersion, dataVersion,
|
|
status)) {
|
|
return false;
|
|
}
|
|
|
|
std::set<cmInstrumentationQuery::Option> options;
|
|
auto optionParser = EnumParser<cmInstrumentationQuery::Option>(
|
|
cmInstrumentationQuery::OptionString);
|
|
for (auto const& arg : arguments.Options) {
|
|
cmInstrumentationQuery::Option option;
|
|
if (!optionParser(arg, option)) {
|
|
status.SetError(
|
|
cmStrCat("given invalid argument to OPTIONS \"", arg, '"'));
|
|
return false;
|
|
}
|
|
options.insert(option);
|
|
}
|
|
|
|
std::set<cmInstrumentationQuery::Hook> hooks;
|
|
auto hookParser = EnumParser<cmInstrumentationQuery::Hook>(
|
|
cmInstrumentationQuery::HookString);
|
|
for (auto const& arg : arguments.Hooks) {
|
|
cmInstrumentationQuery::Hook hook;
|
|
if (!hookParser(arg, hook)) {
|
|
status.SetError(
|
|
cmStrCat("given invalid argument to HOOKS \"", arg, '"'));
|
|
return false;
|
|
}
|
|
hooks.insert(hook);
|
|
}
|
|
|
|
// Generate custom content
|
|
cmInstrumentation* instrumentation =
|
|
status.GetMakefile().GetCMakeInstance()->GetInstrumentation();
|
|
for (auto const& content : arguments.CustomContent) {
|
|
if (content.size() != 3) {
|
|
status.SetError("CUSTOM_CONTENT expected 3 arguments");
|
|
return false;
|
|
}
|
|
std::string const label = content[0];
|
|
std::string const type = content[1];
|
|
std::string const contentString = content[2];
|
|
Json::Value value;
|
|
if (type == "STRING") {
|
|
value = contentString;
|
|
} else if (type == "BOOL") {
|
|
value = !cmValue(contentString).IsOff();
|
|
} else if (type == "LIST") {
|
|
value = Json::arrayValue;
|
|
for (auto const& item : cmList(contentString)) {
|
|
value.append(item);
|
|
}
|
|
} else if (type == "JSON") {
|
|
Json::CharReaderBuilder builder;
|
|
std::istringstream iss(contentString);
|
|
if (!Json::parseFromStream(builder, iss, &value, nullptr)) {
|
|
status.SetError(
|
|
cmStrCat("failed to parse custom content as JSON: ", contentString));
|
|
return false;
|
|
}
|
|
} else {
|
|
status.SetError(
|
|
cmStrCat("got an invalid type for CUSTOM_CONTENT: ", type));
|
|
return false;
|
|
}
|
|
instrumentation->AddCustomContent(content.front(), value);
|
|
}
|
|
|
|
// Write query file
|
|
instrumentation->WriteJSONQuery(options, hooks, arguments.Callbacks);
|
|
|
|
return true;
|
|
}
|