summaryrefslogtreecommitdiffstats
path: root/Source/cmFileAPICommand.cxx
diff options
context:
space:
mode:
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);
+}