1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst 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,
std::vector<std::string> const& 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 (std::string const& ver : versions) {
char const* 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;
}
int const 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(),
[](std::string const& s) -> bool { return s.empty(); })) {
std::string message("QUERY subcommand was given invalid arguments:");
for (std::string const& 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);
}
|