Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGeneratorExpression.h
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
#pragma once
4
5
#include "cmConfigure.h" // IWYU pragma: keep
6
7
#include <map>
8
#include <memory>
9
#include <set>
10
#include <string>
11
#include <unordered_map>
12
#include <utility>
13
#include <vector>
14
15
#include <cm/string_view>
16
17
#include "cmListFileCache.h"
18
#include "cmLocalGenerator.h"
19
20
namespace cm {
21
namespace GenEx {
22
struct Context;
23
}
24
}
25
26
class cmake;
27
class cmCompiledGeneratorExpression;
28
class cmGeneratorTarget;
29
struct cmGeneratorExpressionDAGChecker;
30
31
/** \class cmGeneratorExpression
32
 * \brief Evaluate generate-time query expression syntax.
33
 *
34
 * cmGeneratorExpression instances are used by build system generator
35
 * implementations to evaluate the $<> generator expression syntax.
36
 * Generator expressions are evaluated just before the generate step
37
 * writes strings into the build system.  They have knowledge of the
38
 * build configuration which is not available at configure time.
39
 */
40
class cmGeneratorExpression
41
{
42
public:
43
  /** Construct. */
44
  cmGeneratorExpression(cmake& cmakeInstance,
45
                        cmListFileBacktrace backtrace = cmListFileBacktrace());
46
  ~cmGeneratorExpression();
47
48
  cmGeneratorExpression(cmGeneratorExpression const&) = delete;
49
  cmGeneratorExpression& operator=(cmGeneratorExpression const&) = delete;
50
51
  std::unique_ptr<cmCompiledGeneratorExpression> Parse(
52
    std::string input) const;
53
54
  static std::string Evaluate(
55
    std::string input, cmLocalGenerator const* lg, std::string const& config,
56
    cmGeneratorTarget const* headTarget = nullptr,
57
    cmGeneratorExpressionDAGChecker* dagChecker = nullptr,
58
    cmGeneratorTarget const* currentTarget = nullptr,
59
    std::string const& language = std::string());
60
61
  static bool ForbidGeneratorExpressions(
62
    cmGeneratorTarget const* target, std::string const& propertyName,
63
    std::string const& propertyValue, std::string& evaluatedValue,
64
    std::map<std::string, std::vector<std::string>>& allowList);
65
66
  static bool ForbidGeneratorExpressions(cmGeneratorTarget const* target,
67
                                         std::string const& propertyName,
68
                                         std::string const& propertyValue);
69
70
  enum PreprocessContext
71
  {
72
    StripAllGeneratorExpressions,
73
    BuildInterface,
74
    InstallInterface
75
  };
76
77
  static std::string Preprocess(cm::string_view input,
78
                                PreprocessContext context,
79
                                cm::string_view importPrefix = {});
80
81
  static std::string Collect(
82
    std::string const& input,
83
    std::map<std::string, std::vector<std::string>>& collected);
84
85
  static void Split(std::string const& input,
86
                    std::vector<std::string>& output);
87
88
  static cm::string_view::size_type Find(cm::string_view input);
89
90
  static bool IsValidTargetName(std::string const& input);
91
92
  static std::string StripEmptyListElements(std::string const& input);
93
94
  static bool StartsWithGeneratorExpression(std::string const& input)
95
26.2k
  {
96
26.2k
    return input.length() >= 2 && input[0] == '$' && input[1] == '<';
97
26.2k
  }
98
  static bool StartsWithGeneratorExpression(char const* input)
99
3.58M
  {
100
3.58M
    return input && input[0] == '$' && input[1] == '<';
101
3.58M
  }
102
103
  static void ReplaceInstallPrefix(std::string& input,
104
                                   std::string const& replacement);
105
106
private:
107
  cmake& CMakeInstance;
108
  cmListFileBacktrace Backtrace;
109
};
110
111
/** \class cmGeneratorExpressionParseCache
112
 * \brief Cache of parsed generator expressions, keyed by their input.
113
 *
114
 * Parsing depends only on the input string, so all
115
 * cmCompiledGeneratorExpression instances compiled from the same input
116
 * share one immutable parse tree.  Each cmCompiledGeneratorExpression
117
 * keeps its own evaluation state.
118
 */
119
class cmGeneratorExpressionParseCache
120
{
121
public:
122
  struct Entry;
123
124
  cmGeneratorExpressionParseCache();
125
  ~cmGeneratorExpressionParseCache();
126
127
  cmGeneratorExpressionParseCache(cmGeneratorExpressionParseCache const&) =
128
    delete;
129
  cmGeneratorExpressionParseCache& operator=(
130
    cmGeneratorExpressionParseCache const&) = delete;
131
132
  /** Get the parse tree of the input, or nullptr if the input does not
133
      contain any generator expression.  */
134
  std::shared_ptr<Entry const> Get(std::string const& input);
135
136
  /** Forget all parse trees.  Compiled expressions keep the parse trees
137
      they use alive.  */
138
0
  void Clear() { this->Entries.clear(); }
139
140
private:
141
  // Keys refer to the input owned by the entry, so it is not stored twice.
142
  std::unordered_map<cm::string_view, std::shared_ptr<Entry const>> Entries;
143
};
144
145
class cmCompiledGeneratorExpression
146
{
147
public:
148
  ~cmCompiledGeneratorExpression();
149
150
  cmCompiledGeneratorExpression(cmCompiledGeneratorExpression const&) = delete;
151
  cmCompiledGeneratorExpression& operator=(
152
    cmCompiledGeneratorExpression const&) = delete;
153
154
  std::string const& Evaluate(
155
    cmLocalGenerator const* lg, std::string const& config,
156
    cmGeneratorTarget const* headTarget = nullptr) const;
157
158
  std::string const& Evaluate(
159
    cm::GenEx::Context const& context,
160
    cmGeneratorExpressionDAGChecker* dagChecker,
161
    cmGeneratorTarget const* headTarget = nullptr,
162
    cmGeneratorTarget const* currentTarget = nullptr) const;
163
164
  /** Get set of targets found during evaluations.  */
165
  std::set<cmGeneratorTarget*> const& GetTargets() const
166
552
  {
167
552
    return this->DependTargets;
168
552
  }
169
170
  std::set<std::string> const& GetSeenTargetProperties() const
171
11.3k
  {
172
11.3k
    return this->SeenTargetProperties;
173
11.3k
  }
174
175
  std::set<cmGeneratorTarget const*> const& GetAllTargetsSeen() const
176
552
  {
177
552
    return this->AllTargetsSeen;
178
552
  }
179
180
0
  std::string const& GetInput() const { return this->Input; }
181
182
43.2k
  cmListFileBacktrace GetBacktrace() const { return this->Backtrace; }
183
  bool GetHadContextSensitiveCondition() const
184
40.8k
  {
185
40.8k
    return this->HadContextSensitiveCondition;
186
40.8k
  }
187
  bool GetHadHeadSensitiveCondition() const
188
40.4k
  {
189
40.4k
    return this->HadHeadSensitiveCondition;
190
40.4k
  }
191
  bool GetHadLinkLanguageSensitiveCondition() const
192
40.4k
  {
193
40.4k
    return this->HadLinkLanguageSensitiveCondition;
194
40.4k
  }
195
  std::set<cmGeneratorTarget const*> GetSourceSensitiveTargets() const
196
552
  {
197
552
    return this->SourceSensitiveTargets;
198
552
  }
199
200
  void SetEvaluateForBuildsystem(bool eval)
201
46.7k
  {
202
46.7k
    this->EvaluateForBuildsystem = eval;
203
46.7k
  }
204
205
8.98k
  void SetQuiet(bool quiet) { this->Quiet = quiet; }
206
207
  void GetMaxLanguageStandard(cmGeneratorTarget const* tgt,
208
                              std::map<std::string, std::string>& mapping);
209
210
private:
211
  cmCompiledGeneratorExpression(cmake& cmakeInstance,
212
                                cmListFileBacktrace backtrace,
213
                                std::string input);
214
215
  friend class cmGeneratorExpression;
216
217
  cmListFileBacktrace Backtrace;
218
  std::string const Input;
219
  // Null if the input does not contain any generator expression.
220
  std::shared_ptr<cmGeneratorExpressionParseCache::Entry const> Parsed;
221
  bool EvaluateForBuildsystem = false;
222
  bool Quiet = false;
223
224
  mutable std::set<cmGeneratorTarget*> DependTargets;
225
  mutable std::set<cmGeneratorTarget const*> AllTargetsSeen;
226
  mutable std::set<std::string> SeenTargetProperties;
227
  mutable std::map<cmGeneratorTarget const*,
228
                   std::map<std::string, std::string>>
229
    MaxLanguageStandard;
230
  mutable std::string Output;
231
  mutable bool HadContextSensitiveCondition = false;
232
  mutable bool HadHeadSensitiveCondition = false;
233
  mutable bool HadLinkLanguageSensitiveCondition = false;
234
  mutable std::set<cmGeneratorTarget const*> SourceSensitiveTargets;
235
};
236
237
class cmGeneratorExpressionInterpreter
238
{
239
public:
240
  cmGeneratorExpressionInterpreter(cmLocalGenerator const* localGenerator,
241
                                   std::string config,
242
                                   cmGeneratorTarget const* headTarget,
243
                                   std::string language = std::string())
244
375
    : GeneratorExpression(*localGenerator->GetCMakeInstance())
245
375
    , LocalGenerator(localGenerator)
246
375
    , Config(std::move(config))
247
375
    , HeadTarget(headTarget)
248
375
    , Language(std::move(language))
249
375
  {
250
375
  }
251
252
  cmGeneratorExpressionInterpreter(cmGeneratorExpressionInterpreter const&) =
253
    delete;
254
  cmGeneratorExpressionInterpreter& operator=(
255
    cmGeneratorExpressionInterpreter const&) = delete;
256
257
  std::string const& Evaluate(std::string expression,
258
                              std::string const& property);
259
260
protected:
261
  cmGeneratorExpression GeneratorExpression;
262
  std::unique_ptr<cmCompiledGeneratorExpression> CompiledGeneratorExpression;
263
  cmLocalGenerator const* LocalGenerator = nullptr;
264
  std::string Config;
265
  cmGeneratorTarget const* HeadTarget = nullptr;
266
  std::string Language;
267
};