Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGeneratorExpressionDAGChecker.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 "cmGeneratorExpressionDAGChecker.h"
4
5
#include <sstream>
6
#include <utility>
7
8
#include <cm/optional>
9
#include <cm/string_view>
10
#include <cmext/string_view>
11
12
#include "cmGenExContext.h"
13
#include "cmGenExEvaluation.h"
14
#include "cmGeneratorExpressionEvaluator.h"
15
#include "cmGeneratorTarget.h"
16
#include "cmLocalGenerator.h"
17
#include "cmMessageType.h"
18
#include "cmStringAlgorithms.h"
19
#include "cmake.h"
20
21
cmGeneratorExpressionDAGChecker::cmGeneratorExpressionDAGChecker(
22
  cmGeneratorTarget const* target, std::string property,
23
  GeneratorExpressionContent const* content,
24
  cmGeneratorExpressionDAGChecker* parent, cm::GenEx::Context const& context,
25
  cmListFileBacktrace backtrace, ComputingLinkLibraries computingLinkLibraries)
26
0
  : Parent(parent)
27
0
  , Top(parent ? parent->Top : this)
28
0
  , Target(target)
29
0
  , Property(std::move(property))
30
0
  , Content(content)
31
0
  , Backtrace(std::move(backtrace))
32
0
  , ComputingLinkLibraries_(computingLinkLibraries)
33
0
{
34
0
  if (parent) {
35
0
    this->TopIsTransitiveProperty = parent->TopIsTransitiveProperty;
36
0
  } else {
37
0
    this->TopIsTransitiveProperty =
38
0
      this->Target->IsTransitiveProperty(this->Property, context, this)
39
0
        .has_value();
40
0
  }
41
42
0
  this->CheckResult = this->CheckGraph();
43
44
0
  if (this->CheckResult == DAG && this->EvaluatingTransitiveProperty()) {
45
0
    auto const* top = this->Top;
46
0
    auto it = top->Seen.find(this->Target);
47
0
    if (it != top->Seen.end()) {
48
0
      std::set<std::string> const& propSet = it->second;
49
0
      if (propSet.find(this->Property) != propSet.end()) {
50
0
        this->CheckResult = ALREADY_SEEN;
51
0
        return;
52
0
      }
53
0
    }
54
0
    top->Seen[this->Target].insert(this->Property);
55
0
  }
56
0
}
57
58
cmGeneratorExpressionDAGChecker::Result
59
cmGeneratorExpressionDAGChecker::Check() const
60
0
{
61
0
  return this->CheckResult;
62
0
}
63
64
void cmGeneratorExpressionDAGChecker::ReportError(cm::GenEx::Evaluation* eval,
65
                                                  std::string const& expr)
66
0
{
67
0
  if (this->CheckResult == DAG) {
68
0
    return;
69
0
  }
70
71
0
  eval->HadError = true;
72
0
  if (eval->Quiet) {
73
0
    return;
74
0
  }
75
76
0
  cmGeneratorExpressionDAGChecker const* parent = this->Parent;
77
78
0
  if (parent && !parent->Parent) {
79
0
    std::ostringstream e;
80
0
    e << "Error evaluating generator expression:\n"
81
0
      << "  " << expr << "\n"
82
0
      << "Self reference on target \"" << eval->HeadTarget->GetName()
83
0
      << "\".\n";
84
0
    eval->Context.LG->GetCMakeInstance()->IssueMessage(
85
0
      MessageType::FATAL_ERROR, e.str(), parent->Backtrace);
86
0
    return;
87
0
  }
88
89
0
  {
90
0
    std::ostringstream e;
91
    /* clang-format off */
92
0
  e << "Error evaluating generator expression:\n"
93
0
    << "  " << expr << "\n"
94
0
    << "Dependency loop found.";
95
    /* clang-format on */
96
0
    eval->Context.LG->GetCMakeInstance()->IssueMessage(
97
0
      MessageType::FATAL_ERROR, e.str(), eval->Backtrace);
98
0
  }
99
100
0
  int loopStep = 1;
101
0
  while (parent) {
102
0
    std::ostringstream e;
103
0
    e << "Loop step " << loopStep << "\n"
104
0
      << "  "
105
0
      << (parent->Content ? parent->Content->GetOriginalExpression() : expr)
106
0
      << "\n";
107
0
    eval->Context.LG->GetCMakeInstance()->IssueMessage(
108
0
      MessageType::FATAL_ERROR, e.str(), parent->Backtrace);
109
0
    parent = parent->Parent;
110
0
    ++loopStep;
111
0
  }
112
0
}
113
114
cmGeneratorExpressionDAGChecker::Result
115
cmGeneratorExpressionDAGChecker::CheckGraph() const
116
0
{
117
0
  cmGeneratorExpressionDAGChecker const* parent = this->Parent;
118
0
  while (parent) {
119
0
    if (this->Target == parent->Target && this->Property == parent->Property) {
120
0
      return (parent == this->Parent) ? SELF_REFERENCE : CYCLIC_REFERENCE;
121
0
    }
122
0
    parent = parent->Parent;
123
0
  }
124
0
  return DAG;
125
0
}
126
127
bool cmGeneratorExpressionDAGChecker::GetTransitivePropertiesOnly() const
128
0
{
129
0
  return this->Top->TransitivePropertiesOnly;
130
0
}
131
132
bool cmGeneratorExpressionDAGChecker::GetTransitivePropertiesOnlyCMP0131()
133
  const
134
0
{
135
0
  return this->Top->CMP0131;
136
0
}
137
138
bool cmGeneratorExpressionDAGChecker::EvaluatingTransitiveProperty() const
139
0
{
140
0
  return this->TopIsTransitiveProperty;
141
0
}
142
143
bool cmGeneratorExpressionDAGChecker::EvaluatingGenexExpression() const
144
0
{
145
  // Corresponds to GenexEvaluator::EvaluateExpression.
146
0
  return cmHasLiteralPrefix(this->Property, "TARGET_GENEX_EVAL:") ||
147
0
    cmHasLiteralPrefix(this->Property, "GENEX_EVAL:");
148
0
}
149
150
bool cmGeneratorExpressionDAGChecker::EvaluatingPICExpression() const
151
0
{
152
  // Corresponds to checkInterfacePropertyCompatibility's special case
153
  // that evaluates the value of POSITION_INDEPENDENT_CODE as a genex.
154
0
  return this->Top->Property == "INTERFACE_POSITION_INDEPENDENT_CODE";
155
0
}
156
157
bool cmGeneratorExpressionDAGChecker::EvaluatingCompileExpression() const
158
0
{
159
0
  cm::string_view property(this->Top->Property);
160
161
0
  return property == "INCLUDE_DIRECTORIES"_s ||
162
0
    property == "COMPILE_DEFINITIONS"_s || property == "COMPILE_OPTIONS"_s;
163
0
}
164
165
bool cmGeneratorExpressionDAGChecker::EvaluatingSources() const
166
0
{
167
0
  return this->Property == "SOURCES"_s ||
168
0
    this->Property == "INTERFACE_SOURCES"_s;
169
0
}
170
171
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkExpression() const
172
0
{
173
0
  cm::string_view property(this->Top->Property);
174
175
0
  return property == "LINK_DIRECTORIES"_s || property == "LINK_OPTIONS"_s ||
176
0
    property == "LINK_DEPENDS"_s || property == "LINK_LIBRARY_OVERRIDE"_s ||
177
0
    property == "LINKER_TYPE"_s;
178
0
}
179
180
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkOptionsExpression() const
181
0
{
182
0
  cm::string_view property(this->Top->Property);
183
184
0
  return property == "LINK_OPTIONS"_s || property == "LINKER_TYPE"_s;
185
0
}
186
187
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkerLauncher() const
188
0
{
189
0
  cm::string_view property(this->Top->Property);
190
191
0
  return property.length() > cmStrLen("_LINKER_LAUNCHER") &&
192
0
    property.substr(property.length() - cmStrLen("_LINKER_LAUNCHER")) ==
193
0
    "_LINKER_LAUNCHER"_s;
194
0
}
195
196
bool cmGeneratorExpressionDAGChecker::IsComputingLinkLibraries() const
197
0
{
198
0
  return this->Top->ComputingLinkLibraries_ == ComputingLinkLibraries::Yes;
199
0
}
200
201
bool cmGeneratorExpressionDAGChecker::EvaluatingLinkLibraries(
202
  cmGeneratorTarget const* tgt, ForGenex genex) const
203
0
{
204
0
  auto const* top = this->Top;
205
206
0
  cm::string_view prop(top->Property);
207
208
0
  if (tgt) {
209
0
    return top->Target == tgt && prop == "LINK_LIBRARIES"_s;
210
0
  }
211
212
0
  auto result = prop == "LINK_LIBRARIES"_s ||
213
0
    prop == "INTERFACE_LINK_LIBRARIES"_s ||
214
0
    prop == "INTERFACE_LINK_LIBRARIES_DIRECT"_s ||
215
0
    prop == "LINK_INTERFACE_LIBRARIES"_s ||
216
0
    prop == "IMPORTED_LINK_INTERFACE_LIBRARIES"_s ||
217
0
    cmHasLiteralPrefix(prop, "LINK_INTERFACE_LIBRARIES_") ||
218
0
    cmHasLiteralPrefix(prop, "IMPORTED_LINK_INTERFACE_LIBRARIES_");
219
220
0
  return genex == ForGenex::LINK_LIBRARY || genex == ForGenex::LINK_GROUP
221
0
    ? result
222
0
    : (result || prop == "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"_s);
223
0
}
224
225
cmGeneratorTarget const* cmGeneratorExpressionDAGChecker::TopTarget() const
226
0
{
227
0
  return this->Top->Target;
228
0
}