Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGeneratorExpressionEvaluator.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 "cmGeneratorExpressionEvaluator.h"
4
5
#include <iterator>
6
7
#include <cm/string_view>
8
#include <cmext/string_view>
9
10
#ifndef CMAKE_BOOTSTRAP
11
#  include <cm3p/json/value.h>
12
#endif
13
#include "cmGenExContext.h"
14
#include "cmGenExEvaluation.h"
15
#include "cmGeneratorExpressionNode.h"
16
#include "cmLocalGenerator.h"
17
#include "cmStringAlgorithms.h"
18
#include "cmake.h"
19
20
GeneratorExpressionContent::GeneratorExpressionContent(
21
  char const* startContent, size_t length)
22
46.1k
  : StartContent(startContent)
23
46.1k
  , ContentLength(length)
24
46.1k
{
25
46.1k
}
26
27
46.1k
GeneratorExpressionContent::~GeneratorExpressionContent() = default;
28
29
std::string GeneratorExpressionContent::GetOriginalExpression() const
30
61.9k
{
31
61.9k
  return std::string(this->StartContent, this->ContentLength);
32
61.9k
}
33
34
std::string GeneratorExpressionContent::ProcessArbitraryContent(
35
  cmGeneratorExpressionNode const* node, std::string const& identifier,
36
  cm::GenEx::Evaluation* eval, cmGeneratorExpressionDAGChecker* dagChecker,
37
  std::vector<cmGeneratorExpressionEvaluatorVector>::const_iterator pit) const
38
4.34k
{
39
4.34k
  std::string result;
40
41
4.34k
  auto const pend = this->ParamChildren.end();
42
18.6k
  for (; pit != pend; ++pit) {
43
14.3k
    for (auto const& pExprEval : *pit) {
44
11.0k
      if (node->RequiresLiteralInput()) {
45
191
        if (pExprEval->GetType() != cmGeneratorExpressionEvaluator::Text) {
46
5
          reportError(eval, this->GetOriginalExpression(),
47
5
                      cmStrCat("$<", identifier,
48
5
                               "> expression requires literal input."));
49
5
          return std::string();
50
5
        }
51
191
      }
52
11.0k
      result += pExprEval->Evaluate(eval, dagChecker);
53
11.0k
      if (eval->HadError) {
54
107
        return std::string();
55
107
      }
56
11.0k
    }
57
14.2k
    if ((pit + 1) != pend) {
58
10.0k
      result += ",";
59
10.0k
    }
60
14.2k
  }
61
4.22k
  if (node->RequiresLiteralInput()) {
62
46
    std::vector<std::string> parameters;
63
46
    parameters.push_back(result);
64
46
    return node->Evaluate(parameters, eval, this, dagChecker);
65
46
  }
66
4.18k
  return result;
67
4.22k
}
68
69
std::string GeneratorExpressionContent::Evaluate(
70
  cm::GenEx::Evaluation* eval,
71
  cmGeneratorExpressionDAGChecker* dagChecker) const
72
57.4k
{
73
57.4k
#ifndef CMAKE_BOOTSTRAP
74
57.4k
  auto evalProfilingRAII =
75
57.4k
    eval->Context.LG->GetCMakeInstance()->CreateProfilingEntry(
76
57.4k
      "genex_eval", this->GetOriginalExpression());
77
57.4k
#endif
78
79
57.4k
  std::string identifier;
80
57.4k
  {
81
59.3k
    for (auto const& pExprEval : this->IdentifierChildren) {
82
59.3k
      identifier += pExprEval->Evaluate(eval, dagChecker);
83
59.3k
      if (eval->HadError) {
84
1.82k
        return std::string();
85
1.82k
      }
86
59.3k
    }
87
57.4k
  }
88
89
55.6k
  cmGeneratorExpressionNode const* node =
90
55.6k
    cmGeneratorExpressionNode::GetNode(identifier);
91
92
55.6k
  if (!node) {
93
1.11k
    reportError(eval, this->GetOriginalExpression(),
94
1.11k
                "Expression did not evaluate to a known generator expression");
95
1.11k
    return std::string();
96
1.11k
  }
97
98
54.4k
  if (!node->GeneratesContent()) {
99
3.36k
    if (node->NumExpectedParameters() == 1 &&
100
3.36k
        node->AcceptsArbitraryContentParameter()) {
101
3.36k
      if (this->ParamChildren.empty()) {
102
2
        reportError(
103
2
          eval, this->GetOriginalExpression(),
104
2
          cmStrCat("$<", identifier, "> expression requires a parameter."));
105
2
      }
106
3.36k
    } else {
107
0
      std::vector<std::string> parameters;
108
0
      this->EvaluateParameters(node, identifier, eval, dagChecker, parameters);
109
0
    }
110
3.36k
    return std::string();
111
3.36k
  }
112
113
51.1k
  std::vector<std::string> parameters;
114
51.1k
  this->EvaluateParameters(node, identifier, eval, dagChecker, parameters);
115
51.1k
  if (eval->HadError) {
116
782
    return std::string();
117
782
  }
118
119
50.3k
  if (!parameters.empty()) {
120
47.7k
    cm::string_view ARGS_BEGIN = "<ARGS>"_s;
121
47.7k
    cm::string_view ARGS_END = "</ARGS>"_s;
122
123
47.7k
    std::vector<std::string> args;
124
    // split any parameter with the prefix <ARGS>:
125
72.8k
    for (auto const& param : parameters) {
126
72.8k
      if (cmHasPrefix(param, ARGS_BEGIN) && cmHasSuffix(param, ARGS_END)) {
127
0
        auto values = param.substr(ARGS_BEGIN.length(),
128
0
                                   param.length() - ARGS_BEGIN.length() -
129
0
                                     ARGS_END.length());
130
0
        if (values.empty()) {
131
0
          args.push_back(std::move(values));
132
0
          continue;
133
0
        }
134
135
0
        std::vector<std::string> tokens =
136
0
          cmTokenize(values, ',', cmTokenizerMode::New);
137
0
        args.insert(args.end(), std::make_move_iterator(tokens.begin()),
138
0
                    std::make_move_iterator(tokens.end()));
139
72.8k
      } else {
140
72.8k
        args.push_back(param);
141
72.8k
      }
142
72.8k
    }
143
47.7k
    parameters = std::move(args);
144
47.7k
  }
145
146
50.3k
  {
147
50.3k
#ifndef CMAKE_BOOTSTRAP
148
50.3k
    auto execProfilingRAII =
149
50.3k
      eval->Context.LG->GetCMakeInstance()->CreateProfilingEntry(
150
50.3k
        "genex_exec", identifier, [&parameters]() -> Json::Value {
151
0
          Json::Value args = Json::objectValue;
152
0
          if (!parameters.empty()) {
153
0
            args["genexArgs"] = Json::arrayValue;
154
0
            for (auto const& parameter : parameters) {
155
0
              args["genexArgs"].append(parameter);
156
0
            }
157
0
          }
158
0
          return args;
159
0
        });
160
50.3k
#endif
161
162
50.3k
    return node->Evaluate(parameters, eval, this, dagChecker);
163
51.1k
  }
164
51.1k
}
165
166
std::string GeneratorExpressionContent::EvaluateParameters(
167
  cmGeneratorExpressionNode const* node, std::string const& identifier,
168
  cm::GenEx::Evaluation* eval, cmGeneratorExpressionDAGChecker* dagChecker,
169
  std::vector<std::string>& parameters) const
170
51.1k
{
171
51.1k
  int const numExpected = node->NumExpectedParameters();
172
51.1k
  {
173
51.1k
    auto pit = this->ParamChildren.begin();
174
51.1k
    auto const pend = this->ParamChildren.end();
175
51.1k
    bool const acceptsArbitraryContent =
176
51.1k
      node->AcceptsArbitraryContentParameter();
177
51.1k
    int counter = 1;
178
124k
    for (; pit != pend; ++pit, ++counter) {
179
78.0k
      if (acceptsArbitraryContent && counter == numExpected) {
180
4.34k
        parameters.push_back(this->ProcessArbitraryContent(
181
4.34k
          node, identifier, eval, dagChecker, pit));
182
4.34k
        return std::string();
183
4.34k
      }
184
73.6k
      std::string parameter;
185
73.6k
      if (node->ShouldEvaluateNextParameter(parameters, parameter)) {
186
85.6k
        for (auto const& pExprEval : *pit) {
187
85.6k
          parameter += pExprEval->Evaluate(eval, dagChecker);
188
85.6k
          if (eval->HadError) {
189
400
            return std::string();
190
400
          }
191
85.6k
        }
192
73.6k
      }
193
73.2k
      parameters.push_back(std::move(parameter));
194
73.2k
    }
195
51.1k
  }
196
197
46.3k
  if ((numExpected > cmGeneratorExpressionNode::DynamicParameters &&
198
15.3k
       static_cast<unsigned int>(numExpected) != parameters.size())) {
199
206
    if (numExpected == 0) {
200
0
      reportError(
201
0
        eval, this->GetOriginalExpression(),
202
0
        cmStrCat("$<", identifier, "> expression requires no parameters."));
203
206
    } else if (numExpected == 1) {
204
50
      reportError(eval, this->GetOriginalExpression(),
205
50
                  cmStrCat("$<", identifier,
206
50
                           "> expression requires "
207
50
                           "exactly one parameter."));
208
156
    } else {
209
156
      std::string e =
210
156
        cmStrCat("$<", identifier, "> expression requires ", numExpected,
211
156
                 " comma separated parameters, but got ", parameters.size(),
212
156
                 " instead.");
213
156
      reportError(eval, this->GetOriginalExpression(), e);
214
156
    }
215
206
    return std::string();
216
206
  }
217
218
46.1k
  if (numExpected == cmGeneratorExpressionNode::OneOrMoreParameters &&
219
20.7k
      parameters.empty()) {
220
10
    reportError(eval, this->GetOriginalExpression(),
221
10
                cmStrCat("$<", identifier,
222
10
                         "> expression requires at least one parameter."));
223
46.1k
  } else if (numExpected == cmGeneratorExpressionNode::TwoOrMoreParameters &&
224
1.69k
             parameters.size() < 2) {
225
54
    reportError(eval, this->GetOriginalExpression(),
226
54
                cmStrCat("$<", identifier,
227
54
                         "> expression requires at least two parameters."));
228
46.1k
  } else if (numExpected == cmGeneratorExpressionNode::OneOrZeroParameters &&
229
53
             parameters.size() > 1) {
230
0
    reportError(eval, this->GetOriginalExpression(),
231
0
                cmStrCat("$<", identifier,
232
0
                         "> expression requires one or zero parameters."));
233
0
  }
234
46.1k
  return std::string();
235
46.3k
}