Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGeneratorExpression.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 "cmGeneratorExpression.h"
4
5
#include <algorithm>
6
#include <cstddef>
7
#include <memory>
8
#include <stack>
9
#include <utility>
10
11
#include <cm/string_view>
12
13
#include "cmsys/RegularExpression.hxx"
14
15
#include "cmGenExContext.h"
16
#include "cmGenExEvaluation.h"
17
#include "cmGeneratorExpressionDAGChecker.h"
18
#include "cmGeneratorExpressionEvaluator.h"
19
#include "cmGeneratorExpressionLexer.h"
20
#include "cmGeneratorExpressionParser.h"
21
#include "cmGeneratorTarget.h"
22
#include "cmList.h"
23
#include "cmLocalGenerator.h"
24
#include "cmMakefile.h"
25
#include "cmMessageType.h"
26
#include "cmStringAlgorithms.h"
27
#include "cmSystemTools.h"
28
#include "cmUnreachable.h"
29
#include "cmake.h"
30
31
cmGeneratorExpression::cmGeneratorExpression(cmake& cmakeInstance,
32
                                             cmListFileBacktrace backtrace)
33
47.1k
  : CMakeInstance(cmakeInstance)
34
47.1k
  , Backtrace(std::move(backtrace))
35
47.1k
{
36
47.1k
}
37
38
56.2k
cmCompiledGeneratorExpression::~cmCompiledGeneratorExpression() = default;
39
40
47.1k
cmGeneratorExpression::~cmGeneratorExpression() = default;
41
42
struct cmGeneratorExpressionParseCache::Entry
43
{
44
  explicit Entry(std::string input)
45
26.6k
    : Input(std::move(input))
46
26.6k
  {
47
26.6k
  }
48
49
  // The evaluators point into Input, so it must not change after parsing.
50
  std::string const Input;
51
  cmGeneratorExpressionEvaluatorVector Evaluators;
52
  bool NeedsEvaluation = false;
53
};
54
55
19.9k
cmGeneratorExpressionParseCache::cmGeneratorExpressionParseCache() = default;
56
57
19.9k
cmGeneratorExpressionParseCache::~cmGeneratorExpressionParseCache() = default;
58
59
std::shared_ptr<cmGeneratorExpressionParseCache::Entry const>
60
cmGeneratorExpressionParseCache::Get(std::string const& input)
61
56.2k
{
62
  // Inputs without generator expressions are cheap to handle; do not
63
  // grow the cache with them.
64
56.2k
  if (cmGeneratorExpression::Find(input) == cm::string_view::npos) {
65
22.5k
    return nullptr;
66
22.5k
  }
67
68
33.6k
  auto it = this->Entries.find(input);
69
33.6k
  if (it != this->Entries.end()) {
70
7.00k
    return it->second;
71
7.00k
  }
72
73
26.6k
  auto entry = std::make_shared<Entry>(input);
74
26.6k
  cmGeneratorExpressionLexer l;
75
26.6k
  std::vector<cmGeneratorExpressionToken> tokens = l.Tokenize(entry->Input);
76
26.6k
  entry->NeedsEvaluation = l.GetSawGeneratorExpression();
77
26.6k
  if (entry->NeedsEvaluation) {
78
26.2k
    cmGeneratorExpressionParser p(tokens);
79
26.2k
    p.Parse(entry->Evaluators);
80
26.2k
  }
81
82
  // The key refers to the input owned by the entry.
83
26.6k
  cm::string_view const key = entry->Input;
84
26.6k
  return this->Entries.emplace(key, std::move(entry)).first->second;
85
33.6k
}
86
87
std::unique_ptr<cmCompiledGeneratorExpression> cmGeneratorExpression::Parse(
88
  std::string input) const
89
47.1k
{
90
47.1k
  return std::unique_ptr<cmCompiledGeneratorExpression>(
91
47.1k
    new cmCompiledGeneratorExpression(this->CMakeInstance, this->Backtrace,
92
47.1k
                                      std::move(input)));
93
47.1k
}
94
95
std::string cmGeneratorExpression::Evaluate(
96
  std::string input, cmLocalGenerator const* lg, std::string const& config,
97
  cmGeneratorTarget const* headTarget,
98
  cmGeneratorExpressionDAGChecker* dagChecker,
99
  cmGeneratorTarget const* currentTarget, std::string const& language)
100
9.56k
{
101
9.56k
  if (Find(input) != std::string::npos) {
102
9.02k
#ifndef CMAKE_BOOTSTRAP
103
9.02k
    auto profilingRAII = lg->GetCMakeInstance()->CreateProfilingEntry(
104
9.02k
      "genex_compile_eval", input);
105
9.02k
#endif
106
107
9.02k
    cm::GenEx::Context context(lg, config, language);
108
9.02k
    cmCompiledGeneratorExpression cge(*lg->GetCMakeInstance(),
109
9.02k
                                      cmListFileBacktrace(), std::move(input));
110
9.02k
    return cge.Evaluate(context, dagChecker, headTarget, currentTarget);
111
9.02k
  }
112
536
  return input;
113
9.56k
}
114
115
std::string const& cmCompiledGeneratorExpression::Evaluate(
116
  cmLocalGenerator const* lg, std::string const& config,
117
  cmGeneratorTarget const* headTarget) const
118
0
{
119
0
  cm::GenEx::Context context(lg, config);
120
0
  return this->Evaluate(context, nullptr, headTarget);
121
0
}
122
123
std::string const& cmCompiledGeneratorExpression::Evaluate(
124
  cm::GenEx::Context const& context,
125
  cmGeneratorExpressionDAGChecker* dagChecker,
126
  cmGeneratorTarget const* headTarget,
127
  cmGeneratorTarget const* currentTarget) const
128
50.8k
{
129
50.8k
  cm::GenEx::Evaluation eval(context, this->Quiet, headTarget,
130
50.8k
                             currentTarget ? currentTarget : headTarget,
131
50.8k
                             this->EvaluateForBuildsystem, this->Backtrace);
132
133
50.8k
  if (!this->Parsed || !this->Parsed->NeedsEvaluation) {
134
23.3k
    return this->Input;
135
23.3k
  }
136
137
27.4k
  this->Output.clear();
138
139
77.2k
  for (auto const& it : this->Parsed->Evaluators) {
140
77.2k
    this->Output += it->Evaluate(&eval, dagChecker);
141
142
77.2k
    this->SeenTargetProperties.insert(eval.SeenTargetProperties.cbegin(),
143
77.2k
                                      eval.SeenTargetProperties.cend());
144
77.2k
    if (eval.HadError) {
145
4.15k
      this->Output.clear();
146
4.15k
      break;
147
4.15k
    }
148
77.2k
  }
149
150
27.4k
  this->MaxLanguageStandard = eval.MaxLanguageStandard;
151
152
27.4k
  if (!eval.HadError) {
153
23.3k
    this->HadContextSensitiveCondition = eval.HadContextSensitiveCondition;
154
23.3k
    this->HadHeadSensitiveCondition = eval.HadHeadSensitiveCondition;
155
23.3k
    this->HadLinkLanguageSensitiveCondition =
156
23.3k
      eval.HadLinkLanguageSensitiveCondition;
157
23.3k
    this->SourceSensitiveTargets = eval.SourceSensitiveTargets;
158
23.3k
  }
159
160
27.4k
  this->DependTargets = eval.DependTargets;
161
27.4k
  this->AllTargetsSeen = eval.AllTargets;
162
27.4k
  return this->Output;
163
50.8k
}
164
165
cmCompiledGeneratorExpression::cmCompiledGeneratorExpression(
166
  cmake& cmakeInstance, cmListFileBacktrace backtrace, std::string input)
167
56.2k
  : Backtrace(std::move(backtrace))
168
56.2k
  , Input(std::move(input))
169
56.2k
{
170
56.2k
#ifndef CMAKE_BOOTSTRAP
171
56.2k
  auto profilingRAII =
172
56.2k
    cmakeInstance.CreateProfilingEntry("genex_compile", this->Input);
173
56.2k
#endif
174
175
56.2k
  this->Parsed =
176
56.2k
    cmakeInstance.GetGeneratorExpressionParseCache().Get(this->Input);
177
56.2k
}
178
179
std::string cmGeneratorExpression::StripEmptyListElements(
180
  std::string const& input)
181
21.9k
{
182
21.9k
  if (input.find(';') == std::string::npos) {
183
15.4k
    return input;
184
15.4k
  }
185
6.47k
  std::string result;
186
6.47k
  result.reserve(input.size());
187
188
6.47k
  char const* c = input.c_str();
189
6.47k
  char const* last = c;
190
6.47k
  bool skipSemiColons = true;
191
2.04M
  for (; *c; ++c) {
192
2.04M
    if (*c == ';') {
193
133k
      if (skipSemiColons) {
194
13.2k
        result.append(last, c - last);
195
13.2k
        last = c + 1;
196
13.2k
      }
197
133k
      skipSemiColons = true;
198
1.90M
    } else {
199
1.90M
      skipSemiColons = false;
200
1.90M
    }
201
2.04M
  }
202
6.47k
  result.append(last);
203
204
6.47k
  if (!result.empty() && *(result.end() - 1) == ';') {
205
1.08k
    result.resize(result.size() - 1);
206
1.08k
  }
207
208
6.47k
  return result;
209
21.9k
}
210
211
static std::string extractAllGeneratorExpressions(
212
  cm::string_view input,
213
  std::map<std::string, std::vector<std::string>>* collected)
214
5.16k
{
215
5.16k
  std::string result;
216
5.16k
  std::string::size_type pos = 0;
217
5.16k
  std::string::size_type lastPos = pos;
218
5.16k
  std::stack<char const*> starts; // indices of "$<"
219
5.16k
  std::stack<char const*> colons; // indices of ":"
220
76.6k
  while ((pos = input.find("$<", lastPos)) != std::string::npos) {
221
71.4k
    result += input.substr(lastPos, pos - lastPos);
222
71.4k
    starts.push(input.data() + pos);
223
71.4k
    pos += 2;
224
71.4k
    char const* c = input.data() + pos;
225
71.4k
    char const* const cStart = c;
226
2.28M
    for (; *c; ++c) {
227
2.22M
      if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
228
789k
        starts.push(c);
229
789k
        ++c;
230
789k
        continue;
231
789k
      }
232
1.43M
      if (c[0] == ':') {
233
346k
        if (colons.size() < starts.size()) {
234
283k
          colons.push(c);
235
283k
        }
236
1.08M
      } else if (c[0] == '>') {
237
174k
        if (!colons.empty() && !starts.empty() &&
238
160k
            starts.top() < colons.top()) {
239
144k
          if (collected) {
240
72.4k
            (*collected)[std::string(starts.top() + 2, colons.top())]
241
72.4k
              .push_back(std::string(colons.top() + 1, c));
242
72.4k
          }
243
144k
          colons.pop();
244
144k
        }
245
174k
        if (!starts.empty()) {
246
174k
          starts.pop();
247
174k
        }
248
174k
        if (starts.empty()) {
249
13.2k
          break;
250
13.2k
        }
251
174k
      }
252
1.43M
    }
253
71.4k
    std::string::size_type const traversed = (c - cStart) + 1;
254
71.4k
    if (!*c) {
255
58.2k
      result = cmStrCat(std::move(result), "$<", input.substr(pos, traversed));
256
58.2k
    }
257
71.4k
    pos += traversed;
258
71.4k
    lastPos = pos;
259
71.4k
  }
260
5.16k
  if (starts.empty()) {
261
2.36k
    result += input.substr(lastPos);
262
2.36k
  }
263
5.16k
  return cmGeneratorExpression::StripEmptyListElements(result);
264
5.16k
}
265
266
static std::string stripAllGeneratorExpressions(cm::string_view input)
267
2.58k
{
268
2.58k
  return extractAllGeneratorExpressions(input, nullptr);
269
2.58k
}
270
271
static void prefixItems(std::string const& content, std::string& result,
272
                        cm::string_view prefix)
273
0
{
274
0
  std::vector<std::string> entries;
275
0
  cmGeneratorExpression::Split(content, entries);
276
0
  char const* sep = "";
277
0
  for (std::string const& e : entries) {
278
0
    result += sep;
279
0
    sep = ";";
280
0
    if (!cmSystemTools::FileIsFullPath(e) &&
281
0
        cmGeneratorExpression::Find(e) != 0) {
282
0
      result += prefix;
283
0
    }
284
0
    result += e;
285
0
  }
286
0
}
287
288
static std::string stripExportInterface(
289
  cm::string_view input, cmGeneratorExpression::PreprocessContext context,
290
  cm::string_view importPrefix)
291
5.16k
{
292
5.16k
  std::string result;
293
294
5.16k
  int nestingLevel = 0;
295
5.16k
  std::string::size_type pos = 0;
296
5.16k
  std::string::size_type lastPos = pos;
297
20.0k
  while (true) {
298
20.0k
    std::string::size_type bPos = input.find("$<BUILD_INTERFACE:", lastPos);
299
20.0k
    std::string::size_type iPos = input.find("$<INSTALL_INTERFACE:", lastPos);
300
20.0k
    std::string::size_type lPos =
301
20.0k
      input.find("$<BUILD_LOCAL_INTERFACE:", lastPos);
302
303
20.0k
    pos = std::min({ bPos, iPos, lPos });
304
20.0k
    if (pos == std::string::npos) {
305
5.16k
      break;
306
5.16k
    }
307
308
14.8k
    result += input.substr(lastPos, pos - lastPos);
309
14.8k
    enum class FoundGenex
310
14.8k
    {
311
14.8k
      BuildInterface,
312
14.8k
      InstallInterface,
313
14.8k
      BuildLocalInterface,
314
14.8k
    } foundGenex = FoundGenex::BuildInterface;
315
14.8k
    if (pos == bPos) {
316
5.87k
      foundGenex = FoundGenex::BuildInterface;
317
5.87k
      pos += cmStrLen("$<BUILD_INTERFACE:");
318
8.97k
    } else if (pos == iPos) {
319
7.18k
      foundGenex = FoundGenex::InstallInterface;
320
7.18k
      pos += cmStrLen("$<INSTALL_INTERFACE:");
321
7.18k
    } else if (pos == lPos) {
322
1.78k
      foundGenex = FoundGenex::BuildLocalInterface;
323
1.78k
      pos += cmStrLen("$<BUILD_LOCAL_INTERFACE:");
324
1.78k
    } else {
325
0
      CM_UNREACHABLE;
326
0
    }
327
14.8k
    nestingLevel = 1;
328
14.8k
    char const* c = input.data() + pos;
329
14.8k
    char const* const cStart = c;
330
495k
    for (; *c; ++c) {
331
489k
      if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
332
107k
        ++nestingLevel;
333
107k
        ++c;
334
107k
        continue;
335
107k
      }
336
381k
      if (c[0] == '>') {
337
23.8k
        --nestingLevel;
338
23.8k
        if (nestingLevel != 0) {
339
15.1k
          continue;
340
15.1k
        }
341
8.78k
        if (context == cmGeneratorExpression::BuildInterface &&
342
4.39k
            foundGenex == FoundGenex::BuildInterface) {
343
1.45k
          result += input.substr(pos, c - cStart);
344
7.33k
        } else if (context == cmGeneratorExpression::InstallInterface &&
345
4.39k
                   foundGenex == FoundGenex::InstallInterface) {
346
2.90k
          std::string const content =
347
2.90k
            static_cast<std::string>(input.substr(pos, c - cStart));
348
2.90k
          if (!importPrefix.empty()) {
349
0
            prefixItems(content, result, importPrefix);
350
2.90k
          } else {
351
2.90k
            result += content;
352
2.90k
          }
353
2.90k
        }
354
8.78k
        break;
355
23.8k
      }
356
381k
    }
357
14.8k
    std::string::size_type const traversed = (c - cStart) + 1;
358
14.8k
    if (!*c) {
359
6.05k
      auto remaining = input.substr(pos, traversed);
360
6.05k
      switch (foundGenex) {
361
2.96k
        case FoundGenex::BuildInterface:
362
2.96k
          result =
363
2.96k
            cmStrCat(std::move(result), "$<BUILD_INTERFACE:", remaining);
364
2.96k
          break;
365
1.37k
        case FoundGenex::InstallInterface:
366
1.37k
          result =
367
1.37k
            cmStrCat(std::move(result), "$<INSTALL_INTERFACE:", remaining);
368
1.37k
          break;
369
1.71k
        case FoundGenex::BuildLocalInterface:
370
1.71k
          result =
371
1.71k
            cmStrCat(std::move(result), "$<BUILD_LOCAL_INTERFACE:", remaining);
372
1.71k
          break;
373
6.05k
      }
374
6.05k
    }
375
14.8k
    pos += traversed;
376
14.8k
    lastPos = pos;
377
14.8k
  }
378
5.16k
  if (nestingLevel == 0) {
379
4.37k
    result += input.substr(lastPos);
380
4.37k
  }
381
382
5.16k
  return cmGeneratorExpression::StripEmptyListElements(result);
383
5.16k
}
384
385
void cmGeneratorExpression::Split(std::string const& input,
386
                                  std::vector<std::string>& output)
387
2.58k
{
388
2.58k
  std::string::size_type pos = 0;
389
2.58k
  std::string::size_type lastPos = pos;
390
35.3k
  while ((pos = input.find("$<", lastPos)) != std::string::npos) {
391
32.7k
    std::string part = input.substr(lastPos, pos - lastPos);
392
32.7k
    std::string preGenex;
393
32.7k
    if (!part.empty()) {
394
24.1k
      std::string::size_type startPos = input.rfind(';', pos);
395
24.1k
      if (startPos == std::string::npos) {
396
2.65k
        preGenex = part;
397
2.65k
        part.clear();
398
21.5k
      } else if (startPos != pos - 1 && startPos >= lastPos) {
399
5.35k
        part = input.substr(lastPos, startPos - lastPos);
400
5.35k
        preGenex = input.substr(startPos + 1, pos - startPos - 1);
401
5.35k
      }
402
24.1k
      if (!part.empty()) {
403
19.6k
        cmExpandList(part, output);
404
19.6k
      }
405
24.1k
    }
406
32.7k
    pos += 2;
407
32.7k
    int nestingLevel = 1;
408
32.7k
    char const* c = input.c_str() + pos;
409
32.7k
    char const* const cStart = c;
410
895k
    for (; *c; ++c) {
411
867k
      if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
412
365k
        ++nestingLevel;
413
365k
        ++c;
414
365k
        continue;
415
365k
      }
416
502k
      if (c[0] == '>') {
417
49.5k
        --nestingLevel;
418
49.5k
        if (nestingLevel == 0) {
419
4.53k
          break;
420
4.53k
        }
421
49.5k
      }
422
502k
    }
423
309k
    for (; *c; ++c) {
424
      // Capture the part after the genex and before the next ';'
425
279k
      if (c[0] == ';') {
426
2.85k
        --c;
427
2.85k
        break;
428
2.85k
      }
429
279k
    }
430
32.7k
    std::string::size_type const traversed = (c - cStart) + 1;
431
32.7k
    output.push_back(cmStrCat(preGenex, "$<", input.substr(pos, traversed)));
432
32.7k
    pos += traversed;
433
32.7k
    lastPos = pos;
434
32.7k
  }
435
2.58k
  if (lastPos < input.size()) {
436
727
    cmExpandList(input.substr(lastPos), output);
437
727
  }
438
2.58k
}
439
440
std::string cmGeneratorExpression::Preprocess(cm::string_view input,
441
                                              PreprocessContext context,
442
                                              cm::string_view importPrefix)
443
7.74k
{
444
7.74k
  if (context == StripAllGeneratorExpressions) {
445
2.58k
    return stripAllGeneratorExpressions(input);
446
2.58k
  }
447
5.16k
  if (context == BuildInterface || context == InstallInterface) {
448
5.16k
    return stripExportInterface(input, context, importPrefix);
449
5.16k
  }
450
451
0
  CM_UNREACHABLE;
452
0
  return std::string();
453
5.16k
}
454
455
std::string cmGeneratorExpression::Collect(
456
  std::string const& input,
457
  std::map<std::string, std::vector<std::string>>& collected)
458
2.58k
{
459
2.58k
  return extractAllGeneratorExpressions(input, &collected);
460
2.58k
}
461
462
bool cmGeneratorExpression::ForbidGeneratorExpressions(
463
  cmGeneratorTarget const* target, std::string const& propertyName,
464
  std::string const& propertyValue)
465
0
{
466
0
  std::map<std::string, std::vector<std::string>> allowList;
467
0
  std::string evaluatedValue;
468
0
  return ForbidGeneratorExpressions(target, propertyName, propertyValue,
469
0
                                    evaluatedValue, allowList);
470
0
}
471
472
bool cmGeneratorExpression::ForbidGeneratorExpressions(
473
  cmGeneratorTarget const* target, std::string const& propertyName,
474
  std::string const& propertyValue, std::string& evaluatedValue,
475
  std::map<std::string, std::vector<std::string>>& allowList)
476
0
{
477
0
  size_t const initialAllowedGenExps = allowList.size();
478
0
  evaluatedValue = Collect(propertyValue, allowList);
479
0
  if (evaluatedValue != propertyValue &&
480
0
      allowList.size() > initialAllowedGenExps) {
481
0
    target->Makefile->IssueMessage(
482
0
      MessageType::FATAL_ERROR,
483
0
      cmStrCat("Property \"", propertyName, "\" of target \"",
484
0
               target->GetName(),
485
0
               "\" contains a generator expression. This is not allowed."));
486
0
    return false;
487
0
  }
488
489
  // Check for nested generator expressions (e.g., $<LINK_ONLY:$<...>>).
490
0
  for (auto const& genexp : allowList) {
491
0
    for (auto const& value : genexp.second) {
492
0
      if (value.find("$<") != std::string::npos) {
493
0
        target->Makefile->IssueMessage(
494
0
          MessageType::FATAL_ERROR,
495
0
          cmStrCat("$<", genexp.first, ":...> expression in \"", propertyName,
496
0
                   "\" of target \"", target->GetName(),
497
0
                   "\" contains a generator expression. This is not "
498
0
                   "allowed."));
499
0
        return false;
500
0
      }
501
0
    }
502
0
  }
503
0
  return true;
504
0
}
505
506
cm::string_view::size_type cmGeneratorExpression::Find(cm::string_view input)
507
163k
{
508
163k
  cm::string_view::size_type const openpos = input.find("$<");
509
163k
  if (openpos != cm::string_view::npos &&
510
62.9k
      input.find('>', openpos) != cm::string_view::npos) {
511
61.6k
    return openpos;
512
61.6k
  }
513
101k
  return cm::string_view::npos;
514
163k
}
515
516
bool cmGeneratorExpression::IsValidTargetName(std::string const& input)
517
112k
{
518
  // The ':' is supported to allow use with IMPORTED targets. At least
519
  // Qt 4 and 5 IMPORTED targets use ':' as the namespace delimiter.
520
112k
  static cmsys::RegularExpression targetNameValidator("^[A-Za-z0-9_.:+-]+$");
521
522
112k
  return targetNameValidator.find(input);
523
112k
}
524
525
void cmGeneratorExpression::ReplaceInstallPrefix(
526
  std::string& input, std::string const& replacement)
527
0
{
528
0
  std::string::size_type pos = 0;
529
0
  std::string::size_type lastPos = pos;
530
531
0
  while ((pos = input.find("$<INSTALL_PREFIX>", lastPos)) !=
532
0
         std::string::npos) {
533
0
    std::string::size_type endPos = pos + cmStrLen("$<INSTALL_PREFIX>");
534
0
    input.replace(pos, endPos - pos, replacement);
535
0
    lastPos = endPos;
536
0
  }
537
0
}
538
539
void cmCompiledGeneratorExpression::GetMaxLanguageStandard(
540
  cmGeneratorTarget const* tgt, std::map<std::string, std::string>& mapping)
541
11.3k
{
542
11.3k
  auto it = this->MaxLanguageStandard.find(tgt);
543
11.3k
  if (it != this->MaxLanguageStandard.end()) {
544
0
    mapping = it->second;
545
0
  }
546
11.3k
}
547
548
std::string const& cmGeneratorExpressionInterpreter::Evaluate(
549
  std::string expression, std::string const& property)
550
375
{
551
375
  this->CompiledGeneratorExpression =
552
375
    this->GeneratorExpression.Parse(std::move(expression));
553
554
375
  cm::GenEx::Context context(this->LocalGenerator, this->Config,
555
375
                             this->Language);
556
557
  // Specify COMPILE_OPTIONS to DAGchecker, same semantic as COMPILE_FLAGS
558
375
  cmGeneratorExpressionDAGChecker dagChecker{
559
375
    this->HeadTarget,
560
375
    property == "COMPILE_FLAGS" ? "COMPILE_OPTIONS" : property,
561
375
    nullptr,
562
375
    nullptr,
563
375
    context,
564
375
  };
565
375
  return this->CompiledGeneratorExpression->Evaluate(context, &dagChecker,
566
375
                                                     this->HeadTarget);
567
375
}