Coverage Report

Created: 2026-03-12 06:35

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
#ifndef CMAKE_BOOTSTRAP
6
#  include <cm3p/json/value.h>
7
#endif
8
#include "cmGenExContext.h"
9
#include "cmGenExEvaluation.h"
10
#include "cmGeneratorExpressionNode.h"
11
#include "cmLocalGenerator.h"
12
#include "cmStringAlgorithms.h"
13
#include "cmake.h"
14
15
GeneratorExpressionContent::GeneratorExpressionContent(
16
  char const* startContent, size_t length)
17
0
  : StartContent(startContent)
18
0
  , ContentLength(length)
19
0
{
20
0
}
21
22
0
GeneratorExpressionContent::~GeneratorExpressionContent() = default;
23
24
std::string GeneratorExpressionContent::GetOriginalExpression() const
25
0
{
26
0
  return std::string(this->StartContent, this->ContentLength);
27
0
}
28
29
std::string GeneratorExpressionContent::ProcessArbitraryContent(
30
  cmGeneratorExpressionNode const* node, std::string const& identifier,
31
  cm::GenEx::Evaluation* eval, cmGeneratorExpressionDAGChecker* dagChecker,
32
  std::vector<cmGeneratorExpressionEvaluatorVector>::const_iterator pit) const
33
0
{
34
0
  std::string result;
35
36
0
  auto const pend = this->ParamChildren.end();
37
0
  for (; pit != pend; ++pit) {
38
0
    for (auto const& pExprEval : *pit) {
39
0
      if (node->RequiresLiteralInput()) {
40
0
        if (pExprEval->GetType() != cmGeneratorExpressionEvaluator::Text) {
41
0
          reportError(eval, this->GetOriginalExpression(),
42
0
                      "$<" + identifier +
43
0
                        "> expression requires literal input.");
44
0
          return std::string();
45
0
        }
46
0
      }
47
0
      result += pExprEval->Evaluate(eval, dagChecker);
48
0
      if (eval->HadError) {
49
0
        return std::string();
50
0
      }
51
0
    }
52
0
    if ((pit + 1) != pend) {
53
0
      result += ",";
54
0
    }
55
0
  }
56
0
  if (node->RequiresLiteralInput()) {
57
0
    std::vector<std::string> parameters;
58
0
    parameters.push_back(result);
59
0
    return node->Evaluate(parameters, eval, this, dagChecker);
60
0
  }
61
0
  return result;
62
0
}
63
64
std::string GeneratorExpressionContent::Evaluate(
65
  cm::GenEx::Evaluation* eval,
66
  cmGeneratorExpressionDAGChecker* dagChecker) const
67
0
{
68
0
#ifndef CMAKE_BOOTSTRAP
69
0
  auto evalProfilingRAII =
70
0
    eval->Context.LG->GetCMakeInstance()->CreateProfilingEntry(
71
0
      "genex_eval", this->GetOriginalExpression());
72
0
#endif
73
74
0
  std::string identifier;
75
0
  {
76
0
    for (auto const& pExprEval : this->IdentifierChildren) {
77
0
      identifier += pExprEval->Evaluate(eval, dagChecker);
78
0
      if (eval->HadError) {
79
0
        return std::string();
80
0
      }
81
0
    }
82
0
  }
83
84
0
  cmGeneratorExpressionNode const* node =
85
0
    cmGeneratorExpressionNode::GetNode(identifier);
86
87
0
  if (!node) {
88
0
    reportError(eval, this->GetOriginalExpression(),
89
0
                "Expression did not evaluate to a known generator expression");
90
0
    return std::string();
91
0
  }
92
93
0
  if (!node->GeneratesContent()) {
94
0
    if (node->NumExpectedParameters() == 1 &&
95
0
        node->AcceptsArbitraryContentParameter()) {
96
0
      if (this->ParamChildren.empty()) {
97
0
        reportError(
98
0
          eval, this->GetOriginalExpression(),
99
0
          cmStrCat("$<", identifier, "> expression requires a parameter."));
100
0
      }
101
0
    } else {
102
0
      std::vector<std::string> parameters;
103
0
      this->EvaluateParameters(node, identifier, eval, dagChecker, parameters);
104
0
    }
105
0
    return std::string();
106
0
  }
107
108
0
  std::vector<std::string> parameters;
109
0
  this->EvaluateParameters(node, identifier, eval, dagChecker, parameters);
110
0
  if (eval->HadError) {
111
0
    return std::string();
112
0
  }
113
114
0
  {
115
0
#ifndef CMAKE_BOOTSTRAP
116
0
    auto execProfilingRAII =
117
0
      eval->Context.LG->GetCMakeInstance()->CreateProfilingEntry(
118
0
        "genex_exec", identifier, [&parameters]() -> Json::Value {
119
0
          Json::Value args = Json::objectValue;
120
0
          if (!parameters.empty()) {
121
0
            args["genexArgs"] = Json::arrayValue;
122
0
            for (auto const& parameter : parameters) {
123
0
              args["genexArgs"].append(parameter);
124
0
            }
125
0
          }
126
0
          return args;
127
0
        });
128
0
#endif
129
130
0
    return node->Evaluate(parameters, eval, this, dagChecker);
131
0
  }
132
0
}
133
134
std::string GeneratorExpressionContent::EvaluateParameters(
135
  cmGeneratorExpressionNode const* node, std::string const& identifier,
136
  cm::GenEx::Evaluation* eval, cmGeneratorExpressionDAGChecker* dagChecker,
137
  std::vector<std::string>& parameters) const
138
0
{
139
0
  int const numExpected = node->NumExpectedParameters();
140
0
  {
141
0
    auto pit = this->ParamChildren.begin();
142
0
    auto const pend = this->ParamChildren.end();
143
0
    bool const acceptsArbitraryContent =
144
0
      node->AcceptsArbitraryContentParameter();
145
0
    int counter = 1;
146
0
    for (; pit != pend; ++pit, ++counter) {
147
0
      if (acceptsArbitraryContent && counter == numExpected) {
148
0
        parameters.push_back(this->ProcessArbitraryContent(
149
0
          node, identifier, eval, dagChecker, pit));
150
0
        return std::string();
151
0
      }
152
0
      std::string parameter;
153
0
      if (node->ShouldEvaluateNextParameter(parameters, parameter)) {
154
0
        for (auto const& pExprEval : *pit) {
155
0
          parameter += pExprEval->Evaluate(eval, dagChecker);
156
0
          if (eval->HadError) {
157
0
            return std::string();
158
0
          }
159
0
        }
160
0
      }
161
0
      parameters.push_back(std::move(parameter));
162
0
    }
163
0
  }
164
165
0
  if ((numExpected > cmGeneratorExpressionNode::DynamicParameters &&
166
0
       static_cast<unsigned int>(numExpected) != parameters.size())) {
167
0
    if (numExpected == 0) {
168
0
      reportError(
169
0
        eval, this->GetOriginalExpression(),
170
0
        cmStrCat("$<", identifier, "> expression requires no parameters."));
171
0
    } else if (numExpected == 1) {
172
0
      reportError(eval, this->GetOriginalExpression(),
173
0
                  cmStrCat("$<", identifier,
174
0
                           "> expression requires "
175
0
                           "exactly one parameter."));
176
0
    } else {
177
0
      std::string e =
178
0
        cmStrCat("$<", identifier, "> expression requires ", numExpected,
179
0
                 " comma separated parameters, but got ", parameters.size(),
180
0
                 " instead.");
181
0
      reportError(eval, this->GetOriginalExpression(), e);
182
0
    }
183
0
    return std::string();
184
0
  }
185
186
0
  if (numExpected == cmGeneratorExpressionNode::OneOrMoreParameters &&
187
0
      parameters.empty()) {
188
0
    reportError(eval, this->GetOriginalExpression(),
189
0
                cmStrCat("$<", identifier,
190
0
                         "> expression requires at least one parameter."));
191
0
  } else if (numExpected == cmGeneratorExpressionNode::TwoOrMoreParameters &&
192
0
             parameters.size() < 2) {
193
0
    reportError(eval, this->GetOriginalExpression(),
194
0
                cmStrCat("$<", identifier,
195
0
                         "> expression requires at least two parameters."));
196
0
  } else if (numExpected == cmGeneratorExpressionNode::OneOrZeroParameters &&
197
0
             parameters.size() > 1) {
198
0
    reportError(eval, this->GetOriginalExpression(),
199
0
                cmStrCat("$<", identifier,
200
0
                         "> expression requires one or zero parameters."));
201
0
  }
202
0
  return std::string();
203
0
}