CMake Nightly Date Stamp
[kiteware-cmake.git] / Source / cmFileAPICommand.cxx
blobd051c9c1cb26e02c3be4610d4f418a3245f5afa8
1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmFileAPICommand.h"
5 #include <algorithm>
6 #include <array>
7 #include <cctype>
8 #include <cstdlib>
10 #include <cm/string_view>
11 #include <cmext/string_view>
13 #include "cmArgumentParser.h"
14 #include "cmArgumentParserTypes.h"
15 #include "cmExecutionStatus.h"
16 #include "cmFileAPI.h"
17 #include "cmMakefile.h"
18 #include "cmRange.h"
19 #include "cmStringAlgorithms.h"
20 #include "cmSubcommandTable.h"
21 #include "cmake.h"
23 namespace {
25 bool isCharDigit(char ch)
27 return std::isdigit(static_cast<unsigned char>(ch));
30 std::string processObjectKindVersions(cmFileAPI& fileApi,
31 cmFileAPI::ObjectKind objectKind,
32 cm::string_view keyword,
33 const std::vector<std::string>& versions)
35 // The "versions" vector is empty only when the keyword was not present.
36 // It is an error to provide the keyword with no versions after it, and that
37 // is enforced by the argument parser before we get here.
38 if (versions.empty()) {
39 return {};
42 // The first supported version listed is what we use
43 for (const std::string& ver : versions) {
44 const char* vStart = ver.c_str();
45 int majorVersion = std::atoi(vStart);
46 int minorVersion = 0;
47 std::string::size_type pos = ver.find('.');
48 if (pos != std::string::npos) {
49 vStart += pos + 1;
50 minorVersion = std::atoi(vStart);
52 if (majorVersion < 1 || minorVersion < 0) {
53 return cmStrCat("Given a malformed version \"", ver, "\" for ", keyword,
54 ".");
56 if (fileApi.AddProjectQuery(objectKind,
57 static_cast<unsigned>(majorVersion),
58 static_cast<unsigned>(minorVersion))) {
59 return {};
62 return cmStrCat("None of the specified ", keyword,
63 " versions is supported by this version of CMake.");
66 bool handleQueryCommand(std::vector<std::string> const& args,
67 cmExecutionStatus& status)
69 if (args.empty()) {
70 status.SetError("QUERY subcommand called without required arguments.");
71 return false;
74 struct Arguments : public ArgumentParser::ParseResult
76 ArgumentParser::NonEmpty<std::string> ApiVersion;
77 ArgumentParser::NonEmpty<std::vector<std::string>> CodeModelVersions;
78 ArgumentParser::NonEmpty<std::vector<std::string>> CacheVersions;
79 ArgumentParser::NonEmpty<std::vector<std::string>> CMakeFilesVersions;
80 ArgumentParser::NonEmpty<std::vector<std::string>> ToolchainsVersions;
83 static auto const parser =
84 cmArgumentParser<Arguments>{}
85 .Bind("API_VERSION"_s, &Arguments::ApiVersion)
86 .Bind("CODEMODEL"_s, &Arguments::CodeModelVersions)
87 .Bind("CACHE"_s, &Arguments::CacheVersions)
88 .Bind("CMAKEFILES"_s, &Arguments::CMakeFilesVersions)
89 .Bind("TOOLCHAINS"_s, &Arguments::ToolchainsVersions);
91 std::vector<std::string> unparsedArguments;
92 Arguments const arguments =
93 parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments);
95 if (arguments.MaybeReportError(status.GetMakefile())) {
96 return true;
98 if (!unparsedArguments.empty()) {
99 status.SetError("QUERY subcommand given unknown argument \"" +
100 unparsedArguments.front() + "\".");
101 return false;
104 if (!std::all_of(arguments.ApiVersion.begin(), arguments.ApiVersion.end(),
105 isCharDigit)) {
106 status.SetError("QUERY subcommand given a non-integer API_VERSION.");
107 return false;
109 const int apiVersion = std::atoi(arguments.ApiVersion.c_str());
110 if (apiVersion != 1) {
111 status.SetError(
112 cmStrCat("QUERY subcommand given an unsupported API_VERSION \"",
113 arguments.ApiVersion,
114 "\" (the only currently supported version is 1)."));
115 return false;
118 cmMakefile& mf = status.GetMakefile();
119 cmake* cmi = mf.GetCMakeInstance();
120 cmFileAPI* fileApi = cmi->GetFileAPI();
122 // We want to check all keywords and report all errors, not just the first.
123 // Record each result rather than short-circuiting on the first error.
125 // NOTE: Double braces are needed here for compilers that don't implement the
126 // CWG 1270 revision to C++11.
127 std::array<std::string, 4> errors{
128 { processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::CodeModel,
129 "CODEMODEL"_s, arguments.CodeModelVersions),
130 processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::Cache,
131 "CACHE"_s, arguments.CacheVersions),
132 processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::CMakeFiles,
133 "CMAKEFILES"_s, arguments.CMakeFilesVersions),
134 processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::Toolchains,
135 "TOOLCHAINS"_s, arguments.ToolchainsVersions) }
138 if (!std::all_of(errors.begin(), errors.end(),
139 [](const std::string& s) -> bool { return s.empty(); })) {
140 std::string message("QUERY subcommand was given invalid arguments:");
141 for (const std::string& s : errors) {
142 if (!s.empty()) {
143 message = cmStrCat(message, "\n ", s);
146 status.SetError(message);
147 return false;
150 return true;
155 bool cmFileAPICommand(std::vector<std::string> const& args,
156 cmExecutionStatus& status)
158 if (args.empty()) {
159 status.SetError("must be called with arguments.");
160 return false;
163 // clang-format off
164 static cmSubcommandTable const subcommand{
165 { "QUERY"_s, handleQueryCommand }
167 // clang-format on
169 return subcommand(args[0], args, status);