summaryrefslogtreecommitdiffstats
path: root/Source/cmFileAPICommand.cxx
diff options
context:
space:
mode:
authorCraig Scott <craig.scott@crascit.com>2023-06-02 07:16:57 (GMT)
committerBrad King <brad.king@kitware.com>2023-06-05 10:20:50 (GMT)
commit99b2ccf80dc87ccf6832508cc3f8889a70c2785f (patch)
tree6ff990a8878c0e90ee9c5b88ce876f7893e5c882 /Source/cmFileAPICommand.cxx
parent9a63aa8d57394fbddf913ce35c2d32bbf523f0e6 (diff)
downloadCMake-99b2ccf80dc87ccf6832508cc3f8889a70c2785f.zip
CMake-99b2ccf80dc87ccf6832508cc3f8889a70c2785f.tar.gz
CMake-99b2ccf80dc87ccf6832508cc3f8889a70c2785f.tar.bz2
cmake_file_api: New project command
Projects can use the new command to request file API replies for the current run. No query files are generated, the query is tracked internally. Replies are created in the file system at generation time in the usual way. Fixes: #24951
Diffstat (limited to 'Source/cmFileAPICommand.cxx')
-rw-r--r--Source/cmFileAPICommand.cxx170
1 files changed, 170 insertions, 0 deletions
diff --git a/Source/cmFileAPICommand.cxx b/Source/cmFileAPICommand.cxx
new file mode 100644
index 0000000..d051c9c
--- /dev/null
+++ b/Source/cmFileAPICommand.cxx
@@ -0,0 +1,170 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmFileAPICommand.h"
+
+#include <algorithm>
+#include <array>
+#include <cctype>
+#include <cstdlib>
+
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include "cmArgumentParser.h"
+#include "cmArgumentParserTypes.h"
+#include "cmExecutionStatus.h"
+#include "cmFileAPI.h"
+#include "cmMakefile.h"
+#include "cmRange.h"
+#include "cmStringAlgorithms.h"
+#include "cmSubcommandTable.h"
+#include "cmake.h"
+
+namespace {
+
+bool isCharDigit(char ch)
+{
+ return std::isdigit(static_cast<unsigned char>(ch));
+}
+
+std::string processObjectKindVersions(cmFileAPI& fileApi,
+ cmFileAPI::ObjectKind objectKind,
+ cm::string_view keyword,
+ const std::vector<std::string>& versions)
+{
+ // The "versions" vector is empty only when the keyword was not present.
+ // It is an error to provide the keyword with no versions after it, and that
+ // is enforced by the argument parser before we get here.
+ if (versions.empty()) {
+ return {};
+ }
+
+ // The first supported version listed is what we use
+ for (const std::string& ver : versions) {
+ const char* vStart = ver.c_str();
+ int majorVersion = std::atoi(vStart);
+ int minorVersion = 0;
+ std::string::size_type pos = ver.find('.');
+ if (pos != std::string::npos) {
+ vStart += pos + 1;
+ minorVersion = std::atoi(vStart);
+ }
+ if (majorVersion < 1 || minorVersion < 0) {
+ return cmStrCat("Given a malformed version \"", ver, "\" for ", keyword,
+ ".");
+ }
+ if (fileApi.AddProjectQuery(objectKind,
+ static_cast<unsigned>(majorVersion),
+ static_cast<unsigned>(minorVersion))) {
+ return {};
+ }
+ }
+ return cmStrCat("None of the specified ", keyword,
+ " versions is supported by this version of CMake.");
+}
+
+bool handleQueryCommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.empty()) {
+ status.SetError("QUERY subcommand called without required arguments.");
+ return false;
+ }
+
+ struct Arguments : public ArgumentParser::ParseResult
+ {
+ ArgumentParser::NonEmpty<std::string> ApiVersion;
+ ArgumentParser::NonEmpty<std::vector<std::string>> CodeModelVersions;
+ ArgumentParser::NonEmpty<std::vector<std::string>> CacheVersions;
+ ArgumentParser::NonEmpty<std::vector<std::string>> CMakeFilesVersions;
+ ArgumentParser::NonEmpty<std::vector<std::string>> ToolchainsVersions;
+ };
+
+ static auto const parser =
+ cmArgumentParser<Arguments>{}
+ .Bind("API_VERSION"_s, &Arguments::ApiVersion)
+ .Bind("CODEMODEL"_s, &Arguments::CodeModelVersions)
+ .Bind("CACHE"_s, &Arguments::CacheVersions)
+ .Bind("CMAKEFILES"_s, &Arguments::CMakeFilesVersions)
+ .Bind("TOOLCHAINS"_s, &Arguments::ToolchainsVersions);
+
+ std::vector<std::string> unparsedArguments;
+ Arguments const arguments =
+ parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments);
+
+ if (arguments.MaybeReportError(status.GetMakefile())) {
+ return true;
+ }
+ if (!unparsedArguments.empty()) {
+ status.SetError("QUERY subcommand given unknown argument \"" +
+ unparsedArguments.front() + "\".");
+ return false;
+ }
+
+ if (!std::all_of(arguments.ApiVersion.begin(), arguments.ApiVersion.end(),
+ isCharDigit)) {
+ status.SetError("QUERY subcommand given a non-integer API_VERSION.");
+ return false;
+ }
+ const int apiVersion = std::atoi(arguments.ApiVersion.c_str());
+ if (apiVersion != 1) {
+ status.SetError(
+ cmStrCat("QUERY subcommand given an unsupported API_VERSION \"",
+ arguments.ApiVersion,
+ "\" (the only currently supported version is 1)."));
+ return false;
+ }
+
+ cmMakefile& mf = status.GetMakefile();
+ cmake* cmi = mf.GetCMakeInstance();
+ cmFileAPI* fileApi = cmi->GetFileAPI();
+
+ // We want to check all keywords and report all errors, not just the first.
+ // Record each result rather than short-circuiting on the first error.
+
+ // NOTE: Double braces are needed here for compilers that don't implement the
+ // CWG 1270 revision to C++11.
+ std::array<std::string, 4> errors{
+ { processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::CodeModel,
+ "CODEMODEL"_s, arguments.CodeModelVersions),
+ processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::Cache,
+ "CACHE"_s, arguments.CacheVersions),
+ processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::CMakeFiles,
+ "CMAKEFILES"_s, arguments.CMakeFilesVersions),
+ processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::Toolchains,
+ "TOOLCHAINS"_s, arguments.ToolchainsVersions) }
+ };
+
+ if (!std::all_of(errors.begin(), errors.end(),
+ [](const std::string& s) -> bool { return s.empty(); })) {
+ std::string message("QUERY subcommand was given invalid arguments:");
+ for (const std::string& s : errors) {
+ if (!s.empty()) {
+ message = cmStrCat(message, "\n ", s);
+ }
+ }
+ status.SetError(message);
+ return false;
+ }
+
+ return true;
+}
+
+}
+
+bool cmFileAPICommand(std::vector<std::string> const& args,
+ cmExecutionStatus& status)
+{
+ if (args.empty()) {
+ status.SetError("must be called with arguments.");
+ return false;
+ }
+
+ // clang-format off
+ static cmSubcommandTable const subcommand{
+ { "QUERY"_s, handleQueryCommand }
+ };
+ // clang-format on
+
+ return subcommand(args[0], args, status);
+}