| #include "cmInstrumentationQuery.h" | |
| #include <algorithm> | |
| #include <ctime> | |
| #include <functional> | |
| #include <iterator> | |
| #include <set> | |
| #include <vector> | |
| #include <cmext/string_view> | |
| #include <cm3p/json/value.h> | |
| #include "cmJSONHelpers.h" | |
| #include "cmStringAlgorithms.h" | |
| std::vector<std::string> const cmInstrumentationQuery::OptionString{ | |
| "staticSystemInformation", | |
| "dynamicSystemInformation", | |
| "captureOutput", | |
| "processMetrics", | |
| "compileTrace", | |
| "cdashSubmit", | |
| "cdashVerbose", | |
| "trace" | |
| }; | |
| std::vector<std::string> const cmInstrumentationQuery::HookString{ | |
| "postGenerate", "preBuild", "postBuild", "preCMakeBuild", | |
| "postCMakeBuild", "postCTest", "postCMakeInstall", "postCMakeWorkflow", | |
| "prepareForCDash", "manual" | |
| }; | |
| namespace ErrorMessages { | |
| using ErrorGenerator = | |
| std::function<void(Json::Value const*, cmJSONState* state)>; | |
| ErrorGenerator ErrorGeneratorBuilder(std::string const& errorMessage) | |
| { | |
| return [errorMessage](Json::Value const* value, cmJSONState* state) -> void { | |
| state->AddErrorAtValue(errorMessage, value); | |
| }; | |
| }; | |
| static ErrorGenerator InvalidArray = ErrorGeneratorBuilder("Invalid Array"); | |
| JsonErrors::ErrorGenerator InvalidRootQueryObject( | |
| JsonErrors::ObjectError errorType, Json::Value::Members const& extraFields) | |
| { | |
| return JsonErrors::INVALID_NAMED_OBJECT( | |
| [](Json::Value const*, cmJSONState*) -> std::string { | |
| return "root object"; | |
| })(errorType, extraFields); | |
| } | |
| }; | |
| using JSONHelperBuilder = cmJSONHelperBuilder; | |
| using Version = cmInstrumentationQuery::Version; | |
| template <typename E> | |
| static std::function<bool(E&, Json::Value const*, cmJSONState*)> EnumHelper( | |
| std::vector<std::string> const toString, std::string const& type) | |
| { | |
| return [toString, type](E& out, Json::Value const* value, | |
| cmJSONState* state) -> bool { | |
| for (size_t i = 0; i < toString.size(); ++i) { | |
| if (value->asString() == toString[i]) { | |
| out = (E)i; | |
| return true; | |
| } | |
| } | |
| state->AddErrorAtValue( | |
| cmStrCat("Not a valid ", type, ": \"", value->asString(), '"'), value); | |
| return false; | |
| }; | |
| } | |
| static auto const OptionHelper = EnumHelper<cmInstrumentationQuery::Option>( | |
| cmInstrumentationQuery::OptionString, "option"); | |
| static auto const OptionListHelper = | |
| JSONHelperBuilder::Vector<cmInstrumentationQuery::Option>( | |
| ErrorMessages::InvalidArray, OptionHelper); | |
| static auto const HookHelper = EnumHelper<cmInstrumentationQuery::Hook>( | |
| cmInstrumentationQuery::HookString, "hook"); | |
| static auto const HookListHelper = | |
| JSONHelperBuilder::Vector<cmInstrumentationQuery::Hook>( | |
| ErrorMessages::InvalidArray, HookHelper); | |
| static auto const CallbackHelper = JSONHelperBuilder::String(); | |
| static auto const CallbackListHelper = JSONHelperBuilder::Vector<std::string>( | |
| ErrorMessages::InvalidArray, CallbackHelper); | |
| 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; | |
| static auto const QueryRootHelper = | |
| JSONHelperBuilder::Object<QueryRoot>(ErrorMessages::InvalidRootQueryObject, | |
| false) | |
| .Bind("version"_s, &QueryRoot::version, VersionHelper, true) | |
| .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<Callback>& callbacks) | |
| { | |
| Json::Value root; | |
| this->parseState = cmJSONState(filename, &root); | |
| if (!this->parseState.errors.empty()) { | |
| 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; | |
| } | |
| std::move(this->queryRoot.options.begin(), this->queryRoot.options.end(), | |
| std::inserter(options, options.end())); | |
| std::move(this->queryRoot.hooks.begin(), this->queryRoot.hooks.end(), | |
| std::inserter(hooks, hooks.end())); | |
| 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 = 2; | |
| return latest; | |
| } |