Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmFileAPICommand.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmFileAPICommand.h"
4
5
#include <algorithm>
6
#include <array>
7
#include <cctype>
8
#include <cstdlib>
9
#include <utility>
10
11
#include <cm/string_view>
12
#include <cmext/string_view>
13
14
#include "cmArgumentParser.h"
15
#include "cmArgumentParserTypes.h"
16
#include "cmExecutionStatus.h"
17
#include "cmFileAPI.h"
18
#include "cmMakefile.h"
19
#include "cmRange.h"
20
#include "cmStringAlgorithms.h"
21
#include "cmSubcommandTable.h"
22
#include "cmake.h"
23
24
namespace {
25
26
bool isCharDigit(char ch)
27
0
{
28
0
  return std::isdigit(static_cast<unsigned char>(ch));
29
0
}
30
31
std::string processObjectKindVersions(cmFileAPI& fileApi,
32
                                      cmFileAPI::ObjectKind objectKind,
33
                                      cm::string_view keyword,
34
                                      std::vector<std::string> const& versions)
35
0
{
36
  // The "versions" vector is empty only when the keyword was not present.
37
  // It is an error to provide the keyword with no versions after it, and that
38
  // is enforced by the argument parser before we get here.
39
0
  if (versions.empty()) {
40
0
    return {};
41
0
  }
42
43
  // The first supported version listed is what we use
44
0
  for (std::string const& ver : versions) {
45
0
    char const* vStart = ver.c_str();
46
0
    int majorVersion = std::atoi(vStart);
47
0
    int minorVersion = 0;
48
0
    std::string::size_type pos = ver.find('.');
49
0
    if (pos != std::string::npos) {
50
0
      vStart += pos + 1;
51
0
      minorVersion = std::atoi(vStart);
52
0
    }
53
0
    if (majorVersion < 1 || minorVersion < 0) {
54
0
      return cmStrCat("Given a malformed version \"", ver, "\" for ", keyword,
55
0
                      '.');
56
0
    }
57
0
    if (fileApi.AddProjectQuery(objectKind,
58
0
                                static_cast<unsigned>(majorVersion),
59
0
                                static_cast<unsigned>(minorVersion))) {
60
0
      return {};
61
0
    }
62
0
  }
63
0
  return cmStrCat("None of the specified ", keyword,
64
0
                  " versions is supported by this version of CMake.");
65
0
}
66
67
bool handleQueryCommand(std::vector<std::string> const& args,
68
                        cmExecutionStatus& status)
69
0
{
70
0
  if (args.empty()) {
71
0
    status.SetError("QUERY called without required arguments.");
72
0
    return false;
73
0
  }
74
75
0
  struct Arguments : public ArgumentParser::ParseResult
76
0
  {
77
0
    ArgumentParser::NonEmpty<std::string> ApiVersion;
78
0
    ArgumentParser::NonEmpty<std::vector<std::string>> CodeModelVersions;
79
0
    ArgumentParser::NonEmpty<std::vector<std::string>> CacheVersions;
80
0
    ArgumentParser::NonEmpty<std::vector<std::string>> CMakeFilesVersions;
81
0
    ArgumentParser::NonEmpty<std::vector<std::string>> ToolchainsVersions;
82
0
  };
83
84
0
  static auto const parser =
85
0
    cmArgumentParser<Arguments>{}
86
0
      .Bind("API_VERSION"_s, &Arguments::ApiVersion)
87
0
      .Bind("CODEMODEL"_s, &Arguments::CodeModelVersions)
88
0
      .Bind("CACHE"_s, &Arguments::CacheVersions)
89
0
      .Bind("CMAKEFILES"_s, &Arguments::CMakeFilesVersions)
90
0
      .Bind("TOOLCHAINS"_s, &Arguments::ToolchainsVersions);
91
92
0
  std::vector<std::string> unparsedArguments;
93
0
  Arguments const arguments =
94
0
    parser.Parse(cmMakeRange(args).advance(1), &unparsedArguments);
95
96
0
  if (arguments.MaybeReportError(status.GetMakefile())) {
97
0
    return true;
98
0
  }
99
0
  if (!unparsedArguments.empty()) {
100
0
    status.SetError(cmStrCat("QUERY given unknown argument \"",
101
0
                             unparsedArguments.front(), "\"."));
102
0
    return false;
103
0
  }
104
105
0
  if (!std::all_of(arguments.ApiVersion.begin(), arguments.ApiVersion.end(),
106
0
                   isCharDigit)) {
107
0
    status.SetError("QUERY given non-integer API_VERSION.");
108
0
    return false;
109
0
  }
110
0
  int const apiVersion = std::atoi(arguments.ApiVersion.c_str());
111
0
  if (apiVersion != 1) {
112
0
    status.SetError(
113
0
      cmStrCat("QUERY given unsupported API_VERSION \"", arguments.ApiVersion,
114
0
               "\" (the only currently supported version is 1)."));
115
0
    return false;
116
0
  }
117
118
0
  cmMakefile& mf = status.GetMakefile();
119
0
  cmake* cmi = mf.GetCMakeInstance();
120
0
  cmFileAPI* fileApi = cmi->GetFileAPI();
121
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.
124
125
  // NOTE: Double braces are needed here for compilers that don't implement the
126
  // CWG 1270 revision to C++11.
127
0
  std::array<std::string, 4> errors{
128
0
    { processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::CodeModel,
129
0
                                "CODEMODEL"_s, arguments.CodeModelVersions),
130
0
      processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::Cache,
131
0
                                "CACHE"_s, arguments.CacheVersions),
132
0
      processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::CMakeFiles,
133
0
                                "CMAKEFILES"_s, arguments.CMakeFilesVersions),
134
0
      processObjectKindVersions(*fileApi, cmFileAPI::ObjectKind::Toolchains,
135
0
                                "TOOLCHAINS"_s, arguments.ToolchainsVersions) }
136
0
  };
137
138
0
  if (!std::all_of(errors.begin(), errors.end(),
139
0
                   [](std::string const& s) -> bool { return s.empty(); })) {
140
0
    std::string message("QUERY given invalid arguments:");
141
0
    for (std::string const& s : errors) {
142
0
      if (!s.empty()) {
143
0
        message = cmStrCat(message, "\n  ", s);
144
0
      }
145
0
    }
146
0
    status.SetError(message);
147
0
    return false;
148
0
  }
149
150
0
  return true;
151
0
}
152
153
}
154
155
bool cmFileAPICommand(std::vector<std::string> const& args,
156
                      cmExecutionStatus& status)
157
0
{
158
0
  if (args.empty()) {
159
0
    status.SetError("must be called with arguments.");
160
0
    return false;
161
0
  }
162
163
  // clang-format off
164
0
  static cmSubcommandTable const subcommand{
165
0
    { "QUERY"_s, handleQueryCommand }
166
0
  };
167
  // clang-format on
168
169
0
  return subcommand(args[0], args, status);
170
0
}