Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmInstrumentationCommand.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 "cmInstrumentationCommand.h"
4
5
#include <algorithm>
6
#include <cctype>
7
#include <cstdlib>
8
#include <functional>
9
#include <set>
10
#include <sstream>
11
12
#include <cmext/string_view>
13
14
#include <cm3p/json/reader.h>
15
#include <cm3p/json/value.h>
16
17
#include "cmArgumentParser.h"
18
#include "cmArgumentParserTypes.h"
19
#include "cmExecutionStatus.h"
20
#include "cmInstrumentation.h"
21
#include "cmInstrumentationQuery.h"
22
#include "cmList.h"
23
#include "cmMakefile.h"
24
#include "cmStringAlgorithms.h"
25
#include "cmValue.h"
26
#include "cmake.h"
27
28
namespace {
29
30
bool isCharDigit(char ch)
31
0
{
32
0
  return std::isdigit(static_cast<unsigned char>(ch));
33
0
}
34
bool validateVersion(std::string const& key, std::string const& versionString,
35
                     int& version, cmExecutionStatus& status)
36
0
{
37
0
  if (!std::all_of(versionString.begin(), versionString.end(), isCharDigit)) {
38
0
    status.SetError(cmStrCat("given a non-integer ", key, '.'));
39
0
    return false;
40
0
  }
41
0
  version = std::atoi(versionString.c_str());
42
0
  if (version != 1) {
43
0
    status.SetError(
44
0
      cmStrCat("given an unsupported ", key, " \"", versionString,
45
0
               "\" (the only currently supported version is 1)."));
46
0
    return false;
47
0
  }
48
0
  return true;
49
0
}
50
51
template <typename E>
52
std::function<bool(std::string const&, E&)> EnumParser(
53
  std::vector<std::string> const toString)
54
0
{
55
0
  return [toString](std::string const& value, E& out) -> bool {
56
0
    for (size_t i = 0; i < toString.size(); ++i) {
57
0
      if (value == toString[i]) {
58
0
        out = (E)i;
59
0
        return true;
60
0
      }
61
0
    }
62
0
    return false;
63
0
  };
Unexecuted instantiation: cmInstrumentationCommand.cxx:(anonymous namespace)::EnumParser<cmInstrumentationQuery::Option>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cmInstrumentationQuery::Option&)#1}::operator()(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cmInstrumentationQuery::Option&) const
Unexecuted instantiation: cmInstrumentationCommand.cxx:(anonymous namespace)::EnumParser<cmInstrumentationQuery::Hook>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >)::{lambda(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cmInstrumentationQuery::Hook&)#1}::operator()(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cmInstrumentationQuery::Hook&) const
64
0
}
Unexecuted instantiation: cmInstrumentationCommand.cxx:std::__1::function<bool (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cmInstrumentationQuery::Option&)> (anonymous namespace)::EnumParser<cmInstrumentationQuery::Option>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >)
Unexecuted instantiation: cmInstrumentationCommand.cxx:std::__1::function<bool (std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, cmInstrumentationQuery::Hook&)> (anonymous namespace)::EnumParser<cmInstrumentationQuery::Hook>(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >)
65
}
66
67
bool cmInstrumentationCommand(std::vector<std::string> const& args,
68
                              cmExecutionStatus& status)
69
0
{
70
0
  if (args.empty()) {
71
0
    status.SetError("must be called with 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::string> DataVersion;
79
0
    ArgumentParser::NonEmpty<std::vector<std::string>> Options;
80
0
    ArgumentParser::NonEmpty<std::vector<std::string>> Hooks;
81
0
    ArgumentParser::NonEmpty<std::vector<std::vector<std::string>>> Callbacks;
82
0
    ArgumentParser::NonEmpty<std::vector<std::vector<std::string>>>
83
0
      CustomContent;
84
0
  };
85
86
0
  static auto const parser =
87
0
    cmArgumentParser<Arguments>{}
88
0
      .Bind("API_VERSION"_s, &Arguments::ApiVersion)
89
0
      .Bind("DATA_VERSION"_s, &Arguments::DataVersion)
90
0
      .Bind("OPTIONS"_s, &Arguments::Options)
91
0
      .Bind("HOOKS"_s, &Arguments::Hooks)
92
0
      .Bind("CALLBACK"_s, &Arguments::Callbacks)
93
0
      .Bind("CUSTOM_CONTENT"_s, &Arguments::CustomContent);
94
95
0
  std::vector<std::string> unparsedArguments;
96
0
  Arguments const arguments = parser.Parse(args, &unparsedArguments);
97
98
0
  if (arguments.MaybeReportError(status.GetMakefile())) {
99
0
    return true;
100
0
  }
101
0
  if (!unparsedArguments.empty()) {
102
0
    status.SetError("given unknown argument \"" + unparsedArguments.front() +
103
0
                    "\".");
104
0
    return false;
105
0
  }
106
0
  int apiVersion;
107
0
  int dataVersion;
108
0
  if (!validateVersion("API_VERSION", arguments.ApiVersion, apiVersion,
109
0
                       status) ||
110
0
      !validateVersion("DATA_VERSION", arguments.DataVersion, dataVersion,
111
0
                       status)) {
112
0
    return false;
113
0
  }
114
115
0
  std::set<cmInstrumentationQuery::Option> options;
116
0
  auto optionParser = EnumParser<cmInstrumentationQuery::Option>(
117
0
    cmInstrumentationQuery::OptionString);
118
0
  for (auto const& arg : arguments.Options) {
119
0
    cmInstrumentationQuery::Option option;
120
0
    if (!optionParser(arg, option)) {
121
0
      status.SetError(
122
0
        cmStrCat("given invalid argument to OPTIONS \"", arg, '"'));
123
0
      return false;
124
0
    }
125
0
    options.insert(option);
126
0
  }
127
128
0
  std::set<cmInstrumentationQuery::Hook> hooks;
129
0
  auto hookParser = EnumParser<cmInstrumentationQuery::Hook>(
130
0
    cmInstrumentationQuery::HookString);
131
0
  for (auto const& arg : arguments.Hooks) {
132
0
    cmInstrumentationQuery::Hook hook;
133
0
    if (!hookParser(arg, hook)) {
134
0
      status.SetError(
135
0
        cmStrCat("given invalid argument to HOOKS \"", arg, '"'));
136
0
      return false;
137
0
    }
138
0
    hooks.insert(hook);
139
0
  }
140
141
  // Generate custom content
142
0
  cmInstrumentation* instrumentation =
143
0
    status.GetMakefile().GetCMakeInstance()->GetInstrumentation();
144
0
  for (auto const& content : arguments.CustomContent) {
145
0
    if (content.size() != 3) {
146
0
      status.SetError("CUSTOM_CONTENT expected 3 arguments");
147
0
      return false;
148
0
    }
149
0
    std::string const label = content[0];
150
0
    std::string const type = content[1];
151
0
    std::string const contentString = content[2];
152
0
    Json::Value value;
153
0
    if (type == "STRING") {
154
0
      value = contentString;
155
0
    } else if (type == "BOOL") {
156
0
      value = !cmValue(contentString).IsOff();
157
0
    } else if (type == "LIST") {
158
0
      value = Json::arrayValue;
159
0
      for (auto const& item : cmList(contentString)) {
160
0
        value.append(item);
161
0
      }
162
0
    } else if (type == "JSON") {
163
0
      Json::CharReaderBuilder builder;
164
0
      std::istringstream iss(contentString);
165
0
      if (!Json::parseFromStream(builder, iss, &value, nullptr)) {
166
0
        status.SetError(
167
0
          cmStrCat("failed to parse custom content as JSON: ", contentString));
168
0
        return false;
169
0
      }
170
0
    } else {
171
0
      status.SetError(
172
0
        cmStrCat("got an invalid type for CUSTOM_CONTENT: ", type));
173
0
      return false;
174
0
    }
175
0
    instrumentation->AddCustomContent(content.front(), value);
176
0
  }
177
178
  // Write query file
179
0
  instrumentation->WriteJSONQuery(options, hooks, arguments.Callbacks);
180
181
0
  return true;
182
0
}