/* 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 #include #include #include #include #include #include "cmArgumentParser.h" #include "cmArgumentParserTypes.h" #include "cmExecutionStatus.h" #include "cmExperimental.h" #include "cmInstrumentation.h" #include "cmInstrumentationQuery.h" #include "cmMakefile.h" #include "cmStringAlgorithms.h" #include "cmake.h" namespace { bool isCharDigit(char ch) { return std::isdigit(static_cast(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( "QUERY subcommand given an unsupported ", key, " \"", versionString, "\" (the only currently supported version is 1).")); return false; } return true; } template std::function EnumParser( std::vector 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 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 ApiVersion; ArgumentParser::NonEmpty DataVersion; ArgumentParser::NonEmpty> Queries; ArgumentParser::NonEmpty> Hooks; ArgumentParser::NonEmpty>> Callbacks; }; static auto const parser = cmArgumentParser{} .Bind("API_VERSION"_s, &Arguments::ApiVersion) .Bind("DATA_VERSION"_s, &Arguments::DataVersion) .Bind("QUERIES"_s, &Arguments::Queries) .Bind("HOOKS"_s, &Arguments::Hooks) .Bind("CALLBACK"_s, &Arguments::Callbacks); std::vector 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 queries; auto queryParser = EnumParser( cmInstrumentationQuery::QueryString); for (auto const& arg : arguments.Queries) { cmInstrumentationQuery::Query query; if (!queryParser(arg, query)) { status.SetError( cmStrCat("given invalid argument to QUERIES \"", arg, "\"")); return false; } queries.insert(query); } std::set hooks; auto hookParser = EnumParser( 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); } status.GetMakefile() .GetCMakeInstance() ->GetInstrumentation() ->WriteJSONQuery(queries, hooks, arguments.Callbacks); return true; }