resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmGeneratorExpressionNode.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmGeneratorExpressionNode.h"
4
5 #include <algorithm>
6 #include <cassert>
7 #include <cerrno>
8 #include <cstdlib>
9 #include <cstring>
10 #include <functional>
11 #include <map>
12 #include <memory>
13 #include <set>
14 #include <sstream>
15 #include <unordered_map>
16 #include <utility>
17
18 #include <cm/iterator>
19 #include <cm/optional>
20 #include <cm/string_view>
21 #include <cm/vector>
22 #include <cmext/algorithm>
23 #include <cmext/string_view>
24
25 #include "cmsys/RegularExpression.hxx"
26 #include "cmsys/String.h"
27
28 #include "cmAlgorithms.h"
29 #include "cmCMakePath.h"
30 #include "cmComputeLinkInformation.h"
31 #include "cmGeneratorExpression.h"
32 #include "cmGeneratorExpressionContext.h"
33 #include "cmGeneratorExpressionDAGChecker.h"
34 #include "cmGeneratorExpressionEvaluator.h"
35 #include "cmGeneratorTarget.h"
36 #include "cmGlobalGenerator.h"
37 #include "cmLinkItem.h"
38 #include "cmLocalGenerator.h"
39 #include "cmMakefile.h"
40 #include "cmMessageType.h"
41 #include "cmOutputConverter.h"
42 #include "cmPolicies.h"
43 #include "cmRange.h"
44 #include "cmStandardLevelResolver.h"
45 #include "cmState.h"
46 #include "cmStateSnapshot.h"
47 #include "cmStateTypes.h"
48 #include "cmStringAlgorithms.h"
49 #include "cmSystemTools.h"
50 #include "cmTarget.h"
51 #include "cmValue.h"
52 #include "cmake.h"
53
54 std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
55   std::string const& prop, cmLocalGenerator* lg,
56   cmGeneratorExpressionContext* context, cmGeneratorTarget const* headTarget,
57   cmGeneratorExpressionDAGChecker* dagChecker,
58   cmGeneratorTarget const* currentTarget)
59 {
60   cmGeneratorExpression ge(context->Backtrace);
61   std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop);
62   cge->SetEvaluateForBuildsystem(context->EvaluateForBuildsystem);
63   cge->SetQuiet(context->Quiet);
64   std::string result =
65     cge->Evaluate(lg, context->Config, headTarget, dagChecker, currentTarget,
66                   context->Language);
67   if (cge->GetHadContextSensitiveCondition()) {
68     context->HadContextSensitiveCondition = true;
69   }
70   if (cge->GetHadHeadSensitiveCondition()) {
71     context->HadHeadSensitiveCondition = true;
72   }
73   if (cge->GetHadLinkLanguageSensitiveCondition()) {
74     context->HadLinkLanguageSensitiveCondition = true;
75   }
76   return result;
77 }
78
79 static const struct ZeroNode : public cmGeneratorExpressionNode
80 {
81   ZeroNode() {} // NOLINT(modernize-use-equals-default)
82
83   bool GeneratesContent() const override { return false; }
84
85   bool AcceptsArbitraryContentParameter() const override { return true; }
86
87   std::string Evaluate(
88     const std::vector<std::string>& /*parameters*/,
89     cmGeneratorExpressionContext* /*context*/,
90     const GeneratorExpressionContent* /*content*/,
91     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
92   {
93     return std::string();
94   }
95 } zeroNode;
96
97 static const struct OneNode : public cmGeneratorExpressionNode
98 {
99   OneNode() {} // NOLINT(modernize-use-equals-default)
100
101   bool AcceptsArbitraryContentParameter() const override { return true; }
102
103   std::string Evaluate(
104     const std::vector<std::string>& parameters,
105     cmGeneratorExpressionContext* /*context*/,
106     const GeneratorExpressionContent* /*content*/,
107     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
108   {
109     return parameters.front();
110   }
111 } oneNode;
112
113 static const struct OneNode buildInterfaceNode;
114
115 static const struct ZeroNode installInterfaceNode;
116
117 struct BooleanOpNode : public cmGeneratorExpressionNode
118 {
119   BooleanOpNode(const char* op_, const char* successVal_,
120                 const char* failureVal_)
121     : op(op_)
122     , successVal(successVal_)
123     , failureVal(failureVal_)
124   {
125   }
126
127   int NumExpectedParameters() const override { return OneOrMoreParameters; }
128
129   std::string Evaluate(const std::vector<std::string>& parameters,
130                        cmGeneratorExpressionContext* context,
131                        const GeneratorExpressionContent* content,
132                        cmGeneratorExpressionDAGChecker*) const override
133   {
134     for (std::string const& param : parameters) {
135       if (param == this->failureVal) {
136         return this->failureVal;
137       }
138       if (param != this->successVal) {
139         std::ostringstream e;
140         e << "Parameters to $<" << this->op;
141         e << "> must resolve to either '0' or '1'.";
142         reportError(context, content->GetOriginalExpression(), e.str());
143         return std::string();
144       }
145     }
146     return this->successVal;
147   }
148
149   const char *const op, *const successVal, *const failureVal;
150 };
151
152 static const BooleanOpNode andNode("AND", "1", "0"), orNode("OR", "0", "1");
153
154 static const struct NotNode : public cmGeneratorExpressionNode
155 {
156   NotNode() {} // NOLINT(modernize-use-equals-default)
157
158   std::string Evaluate(
159     const std::vector<std::string>& parameters,
160     cmGeneratorExpressionContext* context,
161     const GeneratorExpressionContent* content,
162     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
163   {
164     if (parameters.front() != "0" && parameters.front() != "1") {
165       reportError(
166         context, content->GetOriginalExpression(),
167         "$<NOT> parameter must resolve to exactly one '0' or '1' value.");
168       return std::string();
169     }
170     return parameters.front() == "0" ? "1" : "0";
171   }
172 } notNode;
173
174 static const struct BoolNode : public cmGeneratorExpressionNode
175 {
176   BoolNode() {} // NOLINT(modernize-use-equals-default)
177
178   int NumExpectedParameters() const override { return 1; }
179
180   std::string Evaluate(
181     const std::vector<std::string>& parameters,
182     cmGeneratorExpressionContext* /*context*/,
183     const GeneratorExpressionContent* /*content*/,
184     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
185   {
186     return !cmIsOff(parameters.front()) ? "1" : "0";
187   }
188 } boolNode;
189
190 static const struct IfNode : public cmGeneratorExpressionNode
191 {
192   IfNode() {} // NOLINT(modernize-use-equals-default)
193
194   int NumExpectedParameters() const override { return 3; }
195
196   std::string Evaluate(const std::vector<std::string>& parameters,
197                        cmGeneratorExpressionContext* context,
198                        const GeneratorExpressionContent* content,
199                        cmGeneratorExpressionDAGChecker*) const override
200   {
201     if (parameters[0] != "1" && parameters[0] != "0") {
202       reportError(context, content->GetOriginalExpression(),
203                   "First parameter to $<IF> must resolve to exactly one '0' "
204                   "or '1' value.");
205       return std::string();
206     }
207     return parameters[0] == "1" ? parameters[1] : parameters[2];
208   }
209 } ifNode;
210
211 static const struct StrEqualNode : public cmGeneratorExpressionNode
212 {
213   StrEqualNode() {} // NOLINT(modernize-use-equals-default)
214
215   int NumExpectedParameters() const override { return 2; }
216
217   std::string Evaluate(
218     const std::vector<std::string>& parameters,
219     cmGeneratorExpressionContext* /*context*/,
220     const GeneratorExpressionContent* /*content*/,
221     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
222   {
223     return parameters.front() == parameters[1] ? "1" : "0";
224   }
225 } strEqualNode;
226
227 static const struct EqualNode : public cmGeneratorExpressionNode
228 {
229   EqualNode() {} // NOLINT(modernize-use-equals-default)
230
231   int NumExpectedParameters() const override { return 2; }
232
233   std::string Evaluate(
234     const std::vector<std::string>& parameters,
235     cmGeneratorExpressionContext* context,
236     const GeneratorExpressionContent* content,
237     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
238   {
239     long numbers[2];
240     for (int i = 0; i < 2; ++i) {
241       if (!ParameterToLong(parameters[i].c_str(), &numbers[i])) {
242         reportError(context, content->GetOriginalExpression(),
243                     "$<EQUAL> parameter " + parameters[i] +
244                       " is not a valid integer.");
245         return {};
246       }
247     }
248     return numbers[0] == numbers[1] ? "1" : "0";
249   }
250
251   static bool ParameterToLong(const char* param, long* outResult)
252   {
253     const char isNegative = param[0] == '-';
254
255     int base = 0;
256     if (cmHasLiteralPrefix(param, "0b") || cmHasLiteralPrefix(param, "0B")) {
257       base = 2;
258       param += 2;
259     } else if (cmHasLiteralPrefix(param, "-0b") ||
260                cmHasLiteralPrefix(param, "-0B") ||
261                cmHasLiteralPrefix(param, "+0b") ||
262                cmHasLiteralPrefix(param, "+0B")) {
263       base = 2;
264       param += 3;
265     }
266
267     char* pEnd;
268     long result = strtol(param, &pEnd, base);
269     if (pEnd == param || *pEnd != '\0' || errno == ERANGE) {
270       return false;
271     }
272     if (isNegative && result > 0) {
273       result *= -1;
274     }
275     *outResult = result;
276     return true;
277   }
278 } equalNode;
279
280 static const struct InListNode : public cmGeneratorExpressionNode
281 {
282   InListNode() {} // NOLINT(modernize-use-equals-default)
283
284   int NumExpectedParameters() const override { return 2; }
285
286   std::string Evaluate(
287     const std::vector<std::string>& parameters,
288     cmGeneratorExpressionContext* context,
289     const GeneratorExpressionContent* /*content*/,
290     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
291   {
292     std::vector<std::string> values;
293     std::vector<std::string> checkValues;
294     bool check = false;
295     switch (context->LG->GetPolicyStatus(cmPolicies::CMP0085)) {
296       case cmPolicies::WARN:
297         if (parameters.front().empty()) {
298           check = true;
299           cmExpandList(parameters[1], checkValues, true);
300         }
301         CM_FALLTHROUGH;
302       case cmPolicies::OLD:
303         cmExpandList(parameters[1], values);
304         if (check && values != checkValues) {
305           std::ostringstream e;
306           e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085)
307             << "\nSearch Item:\n  \"" << parameters.front()
308             << "\"\nList:\n  \"" << parameters[1] << "\"\n";
309           context->LG->GetCMakeInstance()->IssueMessage(
310             MessageType ::AUTHOR_WARNING, e.str(), context->Backtrace);
311           return "0";
312         }
313         if (values.empty()) {
314           return "0";
315         }
316         break;
317       case cmPolicies::REQUIRED_IF_USED:
318       case cmPolicies::REQUIRED_ALWAYS:
319       case cmPolicies::NEW:
320         cmExpandList(parameters[1], values, true);
321         break;
322     }
323
324     return cm::contains(values, parameters.front()) ? "1" : "0";
325   }
326 } inListNode;
327
328 static const struct FilterNode : public cmGeneratorExpressionNode
329 {
330   FilterNode() {} // NOLINT(modernize-use-equals-default)
331
332   int NumExpectedParameters() const override { return 3; }
333
334   std::string Evaluate(
335     const std::vector<std::string>& parameters,
336     cmGeneratorExpressionContext* context,
337     const GeneratorExpressionContent* content,
338     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
339   {
340     if (parameters.size() != 3) {
341       reportError(context, content->GetOriginalExpression(),
342                   "$<FILTER:...> expression requires three parameters");
343       return {};
344     }
345
346     if (parameters[1] != "INCLUDE" && parameters[1] != "EXCLUDE") {
347       reportError(
348         context, content->GetOriginalExpression(),
349         "$<FILTER:...> second parameter must be either INCLUDE or EXCLUDE");
350       return {};
351     }
352
353     const bool exclude = parameters[1] == "EXCLUDE";
354
355     cmsys::RegularExpression re;
356     if (!re.compile(parameters[2])) {
357       reportError(context, content->GetOriginalExpression(),
358                   "$<FILTER:...> failed to compile regex");
359       return {};
360     }
361
362     std::vector<std::string> values;
363     std::vector<std::string> result;
364     cmExpandList(parameters.front(), values, true);
365
366     std::copy_if(values.cbegin(), values.cend(), std::back_inserter(result),
367                  [&re, exclude](std::string const& input) {
368                    return exclude ^ re.find(input);
369                  });
370     return cmJoin(cmMakeRange(result.cbegin(), result.cend()), ";");
371   }
372 } filterNode;
373
374 static const struct RemoveDuplicatesNode : public cmGeneratorExpressionNode
375 {
376   RemoveDuplicatesNode() {} // NOLINT(modernize-use-equals-default)
377
378   int NumExpectedParameters() const override { return 1; }
379
380   std::string Evaluate(
381     const std::vector<std::string>& parameters,
382     cmGeneratorExpressionContext* context,
383     const GeneratorExpressionContent* content,
384     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
385   {
386     if (parameters.size() != 1) {
387       reportError(
388         context, content->GetOriginalExpression(),
389         "$<REMOVE_DUPLICATES:...> expression requires one parameter");
390     }
391
392     std::vector<std::string> values = cmExpandedList(parameters.front(), true);
393
394     auto valuesEnd = cmRemoveDuplicates(values);
395     auto valuesBegin = values.cbegin();
396     return cmJoin(cmMakeRange(valuesBegin, valuesEnd), ";");
397   }
398
399 } removeDuplicatesNode;
400
401 static const struct TargetExistsNode : public cmGeneratorExpressionNode
402 {
403   TargetExistsNode() {} // NOLINT(modernize-use-equals-default)
404
405   int NumExpectedParameters() const override { return 1; }
406
407   std::string Evaluate(
408     const std::vector<std::string>& parameters,
409     cmGeneratorExpressionContext* context,
410     const GeneratorExpressionContent* content,
411     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
412   {
413     if (parameters.size() != 1) {
414       reportError(context, content->GetOriginalExpression(),
415                   "$<TARGET_EXISTS:...> expression requires one parameter");
416       return std::string();
417     }
418
419     std::string const& targetName = parameters.front();
420     if (targetName.empty() ||
421         !cmGeneratorExpression::IsValidTargetName(targetName)) {
422       reportError(context, content->GetOriginalExpression(),
423                   "$<TARGET_EXISTS:tgt> expression requires a non-empty "
424                   "valid target name.");
425       return std::string();
426     }
427
428     return context->LG->GetMakefile()->FindTargetToUse(targetName) ? "1" : "0";
429   }
430 } targetExistsNode;
431
432 static const struct TargetNameIfExistsNode : public cmGeneratorExpressionNode
433 {
434   TargetNameIfExistsNode() {} // NOLINT(modernize-use-equals-default)
435
436   int NumExpectedParameters() const override { return 1; }
437
438   std::string Evaluate(
439     const std::vector<std::string>& parameters,
440     cmGeneratorExpressionContext* context,
441     const GeneratorExpressionContent* content,
442     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
443   {
444     if (parameters.size() != 1) {
445       reportError(context, content->GetOriginalExpression(),
446                   "$<TARGET_NAME_IF_EXISTS:...> expression requires one "
447                   "parameter");
448       return std::string();
449     }
450
451     std::string const& targetName = parameters.front();
452     if (targetName.empty() ||
453         !cmGeneratorExpression::IsValidTargetName(targetName)) {
454       reportError(context, content->GetOriginalExpression(),
455                   "$<TARGET_NAME_IF_EXISTS:tgt> expression requires a "
456                   "non-empty valid target name.");
457       return std::string();
458     }
459
460     return context->LG->GetMakefile()->FindTargetToUse(targetName)
461       ? targetName
462       : std::string();
463   }
464 } targetNameIfExistsNode;
465
466 struct GenexEvaluator : public cmGeneratorExpressionNode
467 {
468   GenexEvaluator() {} // NOLINT(modernize-use-equals-default)
469
470 protected:
471   std::string EvaluateExpression(
472     const std::string& genexOperator, const std::string& expression,
473     cmGeneratorExpressionContext* context,
474     const GeneratorExpressionContent* content,
475     cmGeneratorExpressionDAGChecker* dagCheckerParent) const
476   {
477     if (context->HeadTarget) {
478       cmGeneratorExpressionDAGChecker dagChecker(
479         context->Backtrace, context->HeadTarget,
480         genexOperator + ":" + expression, content, dagCheckerParent);
481       switch (dagChecker.Check()) {
482         case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
483         case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: {
484           dagChecker.ReportError(context, content->GetOriginalExpression());
485           return std::string();
486         }
487         case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
488         case cmGeneratorExpressionDAGChecker::DAG:
489           break;
490       }
491
492       return this->EvaluateDependentExpression(
493         expression, context->LG, context, context->HeadTarget, &dagChecker,
494         context->CurrentTarget);
495     }
496
497     return this->EvaluateDependentExpression(
498       expression, context->LG, context, context->HeadTarget, dagCheckerParent,
499       context->CurrentTarget);
500   }
501 };
502
503 static const struct TargetGenexEvalNode : public GenexEvaluator
504 {
505   TargetGenexEvalNode() {} // NOLINT(modernize-use-equals-default)
506
507   int NumExpectedParameters() const override { return 2; }
508
509   bool AcceptsArbitraryContentParameter() const override { return true; }
510
511   std::string Evaluate(
512     const std::vector<std::string>& parameters,
513     cmGeneratorExpressionContext* context,
514     const GeneratorExpressionContent* content,
515     cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
516   {
517     const std::string& targetName = parameters.front();
518     if (targetName.empty() ||
519         !cmGeneratorExpression::IsValidTargetName(targetName)) {
520       reportError(context, content->GetOriginalExpression(),
521                   "$<TARGET_GENEX_EVAL:tgt, ...> expression requires a "
522                   "non-empty valid target name.");
523       return std::string();
524     }
525
526     const auto* target = context->LG->FindGeneratorTargetToUse(targetName);
527     if (!target) {
528       std::ostringstream e;
529       e << "$<TARGET_GENEX_EVAL:tgt, ...> target \"" << targetName
530         << "\" not found.";
531       reportError(context, content->GetOriginalExpression(), e.str());
532       return std::string();
533     }
534
535     const std::string& expression = parameters[1];
536     if (expression.empty()) {
537       return expression;
538     }
539
540     cmGeneratorExpressionContext targetContext(
541       context->LG, context->Config, context->Quiet, target, target,
542       context->EvaluateForBuildsystem, context->Backtrace, context->Language);
543
544     return this->EvaluateExpression("TARGET_GENEX_EVAL", expression,
545                                     &targetContext, content, dagCheckerParent);
546   }
547 } targetGenexEvalNode;
548
549 static const struct GenexEvalNode : public GenexEvaluator
550 {
551   GenexEvalNode() {} // NOLINT(modernize-use-equals-default)
552
553   int NumExpectedParameters() const override { return 1; }
554
555   bool AcceptsArbitraryContentParameter() const override { return true; }
556
557   std::string Evaluate(
558     const std::vector<std::string>& parameters,
559     cmGeneratorExpressionContext* context,
560     const GeneratorExpressionContent* content,
561     cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
562   {
563     const std::string& expression = parameters[0];
564     if (expression.empty()) {
565       return expression;
566     }
567
568     return this->EvaluateExpression("GENEX_EVAL", expression, context, content,
569                                     dagCheckerParent);
570   }
571 } genexEvalNode;
572
573 static const struct LowerCaseNode : public cmGeneratorExpressionNode
574 {
575   LowerCaseNode() {} // NOLINT(modernize-use-equals-default)
576
577   bool AcceptsArbitraryContentParameter() const override { return true; }
578
579   std::string Evaluate(
580     const std::vector<std::string>& parameters,
581     cmGeneratorExpressionContext* /*context*/,
582     const GeneratorExpressionContent* /*content*/,
583     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
584   {
585     return cmSystemTools::LowerCase(parameters.front());
586   }
587 } lowerCaseNode;
588
589 static const struct UpperCaseNode : public cmGeneratorExpressionNode
590 {
591   UpperCaseNode() {} // NOLINT(modernize-use-equals-default)
592
593   bool AcceptsArbitraryContentParameter() const override { return true; }
594
595   std::string Evaluate(
596     const std::vector<std::string>& parameters,
597     cmGeneratorExpressionContext* /*context*/,
598     const GeneratorExpressionContent* /*content*/,
599     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
600   {
601     return cmSystemTools::UpperCase(parameters.front());
602   }
603 } upperCaseNode;
604
605 namespace {
606 template <typename Container>
607 class Range : public cmRange<typename Container::const_iterator>
608 {
609 private:
610   using Base = cmRange<typename Container::const_iterator>;
611
612 public:
613   using const_iterator = typename Container::const_iterator;
614   using value_type = typename Container::value_type;
615   using size_type = typename Container::size_type;
616   using difference_type = typename Container::difference_type;
617   using const_reference = typename Container::const_reference;
618
619   Range(const Container& container)
620     : Base(container.begin(), container.end())
621   {
622   }
623
624   const_reference operator[](size_type pos) const
625   {
626     return *(this->begin() + pos);
627   }
628
629   const_reference front() const { return *this->begin(); }
630   const_reference back() const { return *std::prev(this->end()); }
631
632   Range& advance(difference_type amount) &
633   {
634     Base::advance(amount);
635     return *this;
636   }
637   Range advance(difference_type amount) &&
638   {
639     Base::advance(amount);
640     return std::move(*this);
641   }
642 };
643
644 using Arguments = Range<std::vector<std::string>>;
645
646 bool CheckPathParametersEx(cmGeneratorExpressionContext* ctx,
647                            const GeneratorExpressionContent* cnt,
648                            cm::string_view option, std::size_t count,
649                            int required = 1, bool exactly = true)
650 {
651   if (static_cast<int>(count) < required ||
652       (exactly && static_cast<int>(count) > required)) {
653     reportError(ctx, cnt->GetOriginalExpression(),
654                 cmStrCat("$<PATH:", option, "> expression requires ",
655                          (exactly ? "exactly" : "at least"), ' ',
656                          (required == 1 ? "one parameter" : "two parameters"),
657                          '.'));
658     return false;
659   }
660   return true;
661 };
662 bool CheckPathParameters(cmGeneratorExpressionContext* ctx,
663                          const GeneratorExpressionContent* cnt,
664                          cm::string_view option, const Arguments& args,
665                          int required = 1)
666 {
667   return CheckPathParametersEx(ctx, cnt, option, args.size(), required);
668 };
669 std::string ToString(bool isTrue)
670 {
671   return isTrue ? "1" : "0";
672 };
673 }
674
675 static const struct PathNode : public cmGeneratorExpressionNode
676 {
677   PathNode() {} // NOLINT(modernize-use-equals-default)
678
679   int NumExpectedParameters() const override { return TwoOrMoreParameters; }
680
681   bool AcceptsArbitraryContentParameter() const override { return true; }
682
683   std::string Evaluate(
684     const std::vector<std::string>& parameters,
685     cmGeneratorExpressionContext* context,
686     const GeneratorExpressionContent* content,
687     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
688   {
689     static std::unordered_map<
690       cm::string_view,
691       std::function<std::string(cmGeneratorExpressionContext*,
692                                 const GeneratorExpressionContent*,
693                                 Arguments&)>>
694       pathCommands{
695         { "GET_ROOT_NAME"_s,
696           [](cmGeneratorExpressionContext* ctx,
697              const GeneratorExpressionContent* cnt,
698              Arguments& args) -> std::string {
699             return CheckPathParameters(ctx, cnt, "GET_ROOT_NAME"_s, args) &&
700                 !args.front().empty()
701               ? cmCMakePath{ args.front() }.GetRootName().String()
702               : std::string{};
703           } },
704         { "GET_ROOT_DIRECTORY"_s,
705           [](cmGeneratorExpressionContext* ctx,
706              const GeneratorExpressionContent* cnt,
707              Arguments& args) -> std::string {
708             return CheckPathParameters(ctx, cnt, "GET_ROOT_DIRECTORY"_s,
709                                        args) &&
710                 !args.front().empty()
711               ? cmCMakePath{ args.front() }.GetRootDirectory().String()
712               : std::string{};
713           } },
714         { "GET_ROOT_PATH"_s,
715           [](cmGeneratorExpressionContext* ctx,
716              const GeneratorExpressionContent* cnt,
717              Arguments& args) -> std::string {
718             return CheckPathParameters(ctx, cnt, "GET_ROOT_PATH"_s, args) &&
719                 !args.front().empty()
720               ? cmCMakePath{ args.front() }.GetRootPath().String()
721               : std::string{};
722           } },
723         { "GET_FILENAME"_s,
724           [](cmGeneratorExpressionContext* ctx,
725              const GeneratorExpressionContent* cnt,
726              Arguments& args) -> std::string {
727             return CheckPathParameters(ctx, cnt, "GET_FILENAME"_s, args) &&
728                 !args.front().empty()
729               ? cmCMakePath{ args.front() }.GetFileName().String()
730               : std::string{};
731           } },
732         { "GET_EXTENSION"_s,
733           [](cmGeneratorExpressionContext* ctx,
734              const GeneratorExpressionContent* cnt,
735              Arguments& args) -> std::string {
736             bool lastOnly = args.front() == "LAST_ONLY"_s;
737             if (lastOnly) {
738               args.advance(1);
739             }
740             if (CheckPathParametersEx(ctx, cnt,
741                                       lastOnly ? "GET_EXTENSION,LAST_ONLY"_s
742                                                : "GET_EXTENSION"_s,
743                                       args.size())) {
744               if (args.front().empty()) {
745                 return std::string{};
746               }
747               return lastOnly
748                 ? cmCMakePath{ args.front() }.GetExtension().String()
749                 : cmCMakePath{ args.front() }.GetWideExtension().String();
750             }
751             return std::string{};
752           } },
753         { "GET_STEM"_s,
754           [](cmGeneratorExpressionContext* ctx,
755              const GeneratorExpressionContent* cnt,
756              Arguments& args) -> std::string {
757             bool lastOnly = args.front() == "LAST_ONLY"_s;
758             if (lastOnly) {
759               args.advance(1);
760             }
761             if (CheckPathParametersEx(
762                   ctx, cnt, lastOnly ? "GET_STEM,LAST_ONLY"_s : "GET_STEM"_s,
763                   args.size())) {
764               if (args.front().empty()) {
765                 return std::string{};
766               }
767               return lastOnly
768                 ? cmCMakePath{ args.front() }.GetStem().String()
769                 : cmCMakePath{ args.front() }.GetNarrowStem().String();
770             }
771             return std::string{};
772           } },
773         { "GET_RELATIVE_PART"_s,
774           [](cmGeneratorExpressionContext* ctx,
775              const GeneratorExpressionContent* cnt,
776              Arguments& args) -> std::string {
777             return CheckPathParameters(ctx, cnt, "GET_RELATIVE_PART"_s,
778                                        args) &&
779                 !args.front().empty()
780               ? cmCMakePath{ args.front() }.GetRelativePath().String()
781               : std::string{};
782           } },
783         { "GET_PARENT_PATH"_s,
784           [](cmGeneratorExpressionContext* ctx,
785              const GeneratorExpressionContent* cnt,
786              Arguments& args) -> std::string {
787             return CheckPathParameters(ctx, cnt, "GET_PARENT_PATH"_s, args)
788               ? cmCMakePath{ args.front() }.GetParentPath().String()
789               : std::string{};
790           } },
791         { "HAS_ROOT_NAME"_s,
792           [](cmGeneratorExpressionContext* ctx,
793              const GeneratorExpressionContent* cnt,
794              Arguments& args) -> std::string {
795             return CheckPathParameters(ctx, cnt, "HAS_ROOT_NAME"_s, args)
796               ? ToString(cmCMakePath{ args.front() }.HasRootName())
797               : std::string{ "0" };
798           } },
799         { "HAS_ROOT_DIRECTORY"_s,
800           [](cmGeneratorExpressionContext* ctx,
801              const GeneratorExpressionContent* cnt,
802              Arguments& args) -> std::string {
803             return CheckPathParameters(ctx, cnt, "HAS_ROOT_DIRECTORY"_s, args)
804               ? ToString(cmCMakePath{ args.front() }.HasRootDirectory())
805               : std::string{ "0" };
806           } },
807         { "HAS_ROOT_PATH"_s,
808           [](cmGeneratorExpressionContext* ctx,
809              const GeneratorExpressionContent* cnt,
810              Arguments& args) -> std::string {
811             return CheckPathParameters(ctx, cnt, "HAS_ROOT_PATH"_s, args)
812               ? ToString(cmCMakePath{ args.front() }.HasRootPath())
813               : std::string{ "0" };
814           } },
815         { "HAS_FILENAME"_s,
816           [](cmGeneratorExpressionContext* ctx,
817              const GeneratorExpressionContent* cnt,
818              Arguments& args) -> std::string {
819             return CheckPathParameters(ctx, cnt, "HAS_FILENAME"_s, args)
820               ? ToString(cmCMakePath{ args.front() }.HasFileName())
821               : std::string{ "0" };
822           } },
823         { "HAS_EXTENSION"_s,
824           [](cmGeneratorExpressionContext* ctx,
825              const GeneratorExpressionContent* cnt,
826              Arguments& args) -> std::string {
827             return CheckPathParameters(ctx, cnt, "HAS_EXTENSION"_s, args) &&
828                 !args.front().empty()
829               ? ToString(cmCMakePath{ args.front() }.HasExtension())
830               : std::string{ "0" };
831           } },
832         { "HAS_STEM"_s,
833           [](cmGeneratorExpressionContext* ctx,
834              const GeneratorExpressionContent* cnt,
835              Arguments& args) -> std::string {
836             return CheckPathParameters(ctx, cnt, "HAS_STEM"_s, args)
837               ? ToString(cmCMakePath{ args.front() }.HasStem())
838               : std::string{ "0" };
839           } },
840         { "HAS_RELATIVE_PART"_s,
841           [](cmGeneratorExpressionContext* ctx,
842              const GeneratorExpressionContent* cnt,
843              Arguments& args) -> std::string {
844             return CheckPathParameters(ctx, cnt, "HAS_RELATIVE_PART"_s, args)
845               ? ToString(cmCMakePath{ args.front() }.HasRelativePath())
846               : std::string{ "0" };
847           } },
848         { "HAS_PARENT_PATH"_s,
849           [](cmGeneratorExpressionContext* ctx,
850              const GeneratorExpressionContent* cnt,
851              Arguments& args) -> std::string {
852             return CheckPathParameters(ctx, cnt, "HAS_PARENT_PATH"_s, args)
853               ? ToString(cmCMakePath{ args.front() }.HasParentPath())
854               : std::string{ "0" };
855           } },
856         { "IS_ABSOLUTE"_s,
857           [](cmGeneratorExpressionContext* ctx,
858              const GeneratorExpressionContent* cnt,
859              Arguments& args) -> std::string {
860             return CheckPathParameters(ctx, cnt, "IS_ABSOLUTE"_s, args)
861               ? ToString(cmCMakePath{ args.front() }.IsAbsolute())
862               : std::string{ "0" };
863           } },
864         { "IS_RELATIVE"_s,
865           [](cmGeneratorExpressionContext* ctx,
866              const GeneratorExpressionContent* cnt,
867              Arguments& args) -> std::string {
868             return CheckPathParameters(ctx, cnt, "IS_RELATIVE"_s, args)
869               ? ToString(cmCMakePath{ args.front() }.IsRelative())
870               : std::string{ "0" };
871           } },
872         { "IS_PREFIX"_s,
873           [](cmGeneratorExpressionContext* ctx,
874              const GeneratorExpressionContent* cnt,
875              Arguments& args) -> std::string {
876             bool normalize = args.front() == "NORMALIZE"_s;
877             if (normalize) {
878               args.advance(1);
879             }
880             if (CheckPathParametersEx(ctx, cnt,
881                                       normalize ? "IS_PREFIX,NORMALIZE"_s
882                                                 : "IS_PREFIX"_s,
883                                       args.size(), 2)) {
884               if (normalize) {
885                 return ToString(cmCMakePath{ args[0] }.Normal().IsPrefix(
886                   cmCMakePath{ args[1] }.Normal()));
887               }
888               return ToString(
889                 cmCMakePath{ args[0] }.IsPrefix(cmCMakePath{ args[1] }));
890             }
891             return std::string{};
892           } },
893         { "CMAKE_PATH"_s,
894           [](cmGeneratorExpressionContext* ctx,
895              const GeneratorExpressionContent* cnt,
896              Arguments& args) -> std::string {
897             bool normalize = args.front() == "NORMALIZE"_s;
898             if (normalize) {
899               args.advance(1);
900             }
901             if (CheckPathParametersEx(ctx, cnt,
902                                       normalize ? "CMAKE_PATH,NORMALIZE"_s
903                                                 : "CMAKE_PATH"_s,
904                                       args.size(), 1)) {
905               auto path =
906                 cmCMakePath{ args.front(), cmCMakePath::auto_format };
907               return normalize ? path.Normal().GenericString()
908                                : path.GenericString();
909             }
910             return std::string{};
911           } },
912         { "APPEND"_s,
913           [](cmGeneratorExpressionContext* ctx,
914              const GeneratorExpressionContent* cnt,
915              Arguments& args) -> std::string {
916             if (CheckPathParametersEx(ctx, cnt, "APPEND"_s, args.size(), 1,
917                                       false)) {
918               cmCMakePath path;
919               for (const auto& p : args) {
920                 path /= p;
921               }
922               return path.String();
923             }
924             return std::string{};
925           } },
926         { "REMOVE_FILENAME"_s,
927           [](cmGeneratorExpressionContext* ctx,
928              const GeneratorExpressionContent* cnt,
929              Arguments& args) -> std::string {
930             return CheckPathParameters(ctx, cnt, "REMOVE_FILENAME"_s, args) &&
931                 !args.front().empty()
932               ? cmCMakePath{ args.front() }.RemoveFileName().String()
933               : std::string{};
934           } },
935         { "REPLACE_FILENAME"_s,
936           [](cmGeneratorExpressionContext* ctx,
937              const GeneratorExpressionContent* cnt,
938              Arguments& args) -> std::string {
939             return CheckPathParameters(ctx, cnt, "REPLACE_FILENAME"_s, args, 2)
940               ? cmCMakePath{ args[0] }
941                   .ReplaceFileName(cmCMakePath{ args[1] })
942                   .String()
943               : std::string{};
944           } },
945         { "REMOVE_EXTENSION"_s,
946           [](cmGeneratorExpressionContext* ctx,
947              const GeneratorExpressionContent* cnt,
948              Arguments& args) -> std::string {
949             bool lastOnly = args.front() == "LAST_ONLY"_s;
950             if (lastOnly) {
951               args.advance(1);
952             }
953             if (CheckPathParametersEx(ctx, cnt,
954                                       lastOnly ? "REMOVE_EXTENSION,LAST_ONLY"_s
955                                                : "REMOVE_EXTENSION"_s,
956                                       args.size())) {
957               if (args.front().empty()) {
958                 return std::string{};
959               }
960               return lastOnly
961                 ? cmCMakePath{ args.front() }.RemoveExtension().String()
962                 : cmCMakePath{ args.front() }.RemoveWideExtension().String();
963             }
964             return std::string{};
965           } },
966         { "REPLACE_EXTENSION"_s,
967           [](cmGeneratorExpressionContext* ctx,
968              const GeneratorExpressionContent* cnt,
969              Arguments& args) -> std::string {
970             bool lastOnly = args.front() == "LAST_ONLY"_s;
971             if (lastOnly) {
972               args.advance(1);
973             }
974             if (CheckPathParametersEx(ctx, cnt,
975                                       lastOnly
976                                         ? "REPLACE_EXTENSION,LAST_ONLY"_s
977                                         : "REPLACE_EXTENSION"_s,
978                                       args.size(), 2)) {
979               if (lastOnly) {
980                 return cmCMakePath{ args[0] }
981                   .ReplaceExtension(cmCMakePath{ args[1] })
982                   .String();
983               }
984               return cmCMakePath{ args[0] }
985                 .ReplaceWideExtension(cmCMakePath{ args[1] })
986                 .String();
987             }
988             return std::string{};
989           } },
990         { "NORMAL_PATH"_s,
991           [](cmGeneratorExpressionContext* ctx,
992              const GeneratorExpressionContent* cnt,
993              Arguments& args) -> std::string {
994             return CheckPathParameters(ctx, cnt, "NORMAL_PATH"_s, args) &&
995                 !args.front().empty()
996               ? cmCMakePath{ args.front() }.Normal().String()
997               : std::string{};
998           } },
999         { "RELATIVE_PATH"_s,
1000           [](cmGeneratorExpressionContext* ctx,
1001              const GeneratorExpressionContent* cnt,
1002              Arguments& args) -> std::string {
1003             return CheckPathParameters(ctx, cnt, "RELATIVE_PATH"_s, args, 2)
1004               ? cmCMakePath{ args[0] }.Relative(args[1]).String()
1005               : std::string{};
1006           } },
1007         { "ABSOLUTE_PATH"_s,
1008           [](cmGeneratorExpressionContext* ctx,
1009              const GeneratorExpressionContent* cnt,
1010              Arguments& args) -> std::string {
1011             bool normalize = args.front() == "NORMALIZE"_s;
1012             if (normalize) {
1013               args.advance(1);
1014             }
1015             if (CheckPathParametersEx(ctx, cnt,
1016                                       normalize ? "ABSOLUTE_PATH,NORMALIZE"_s
1017                                                 : "ABSOLUTE_PATH"_s,
1018                                       args.size(), 2)) {
1019               auto path = cmCMakePath{ args[0] }.Absolute(args[1]);
1020               return normalize ? path.Normal().String() : path.String();
1021             }
1022             return std::string{};
1023           } }
1024       };
1025
1026     if (cm::contains(pathCommands, parameters.front())) {
1027       auto args = Arguments{ parameters }.advance(1);
1028       return pathCommands[parameters.front()](context, content, args);
1029     }
1030
1031     reportError(context, content->GetOriginalExpression(),
1032                 cmStrCat(parameters.front(), ": invalid option."));
1033     return std::string{};
1034   }
1035 } pathNode;
1036
1037 static const struct PathEqualNode : public cmGeneratorExpressionNode
1038 {
1039   PathEqualNode() {} // NOLINT(modernize-use-equals-default)
1040
1041   int NumExpectedParameters() const override { return 2; }
1042
1043   std::string Evaluate(
1044     const std::vector<std::string>& parameters,
1045     cmGeneratorExpressionContext* /*context*/,
1046     const GeneratorExpressionContent* /*content*/,
1047     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1048   {
1049     return cmCMakePath{ parameters[0] } == cmCMakePath{ parameters[1] } ? "1"
1050                                                                         : "0";
1051   }
1052 } pathEqualNode;
1053
1054 static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode
1055 {
1056   MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default)
1057
1058   bool AcceptsArbitraryContentParameter() const override { return true; }
1059
1060   std::string Evaluate(
1061     const std::vector<std::string>& parameters,
1062     cmGeneratorExpressionContext* /*context*/,
1063     const GeneratorExpressionContent* /*content*/,
1064     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1065   {
1066     return cmSystemTools::MakeCidentifier(parameters.front());
1067   }
1068 } makeCIdentifierNode;
1069
1070 template <char C>
1071 struct CharacterNode : public cmGeneratorExpressionNode
1072 {
1073   CharacterNode() {} // NOLINT(modernize-use-equals-default)
1074
1075   int NumExpectedParameters() const override { return 0; }
1076
1077   std::string Evaluate(
1078     const std::vector<std::string>& /*parameters*/,
1079     cmGeneratorExpressionContext* /*context*/,
1080     const GeneratorExpressionContent* /*content*/,
1081     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1082   {
1083     return { C };
1084   }
1085 };
1086 static const CharacterNode<'>'> angle_rNode;
1087 static const CharacterNode<','> commaNode;
1088 static const CharacterNode<';'> semicolonNode;
1089
1090 struct CompilerIdNode : public cmGeneratorExpressionNode
1091 {
1092   CompilerIdNode(const char* compilerLang)
1093     : CompilerLanguage(compilerLang)
1094   {
1095   }
1096
1097   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1098
1099   std::string Evaluate(
1100     const std::vector<std::string>& parameters,
1101     cmGeneratorExpressionContext* context,
1102     const GeneratorExpressionContent* content,
1103     cmGeneratorExpressionDAGChecker* dagChecker) const override
1104   {
1105     if (!context->HeadTarget) {
1106       std::ostringstream e;
1107       e << "$<" << this->CompilerLanguage
1108         << "_COMPILER_ID> may only be used with binary targets.  It may "
1109            "not be used with add_custom_command or add_custom_target.";
1110       reportError(context, content->GetOriginalExpression(), e.str());
1111       return {};
1112     }
1113     return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
1114                                       this->CompilerLanguage);
1115   }
1116
1117   std::string EvaluateWithLanguage(const std::vector<std::string>& parameters,
1118                                    cmGeneratorExpressionContext* context,
1119                                    const GeneratorExpressionContent* content,
1120                                    cmGeneratorExpressionDAGChecker* /*unused*/,
1121                                    const std::string& lang) const
1122   {
1123     std::string const& compilerId =
1124       context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
1125                                                     "_COMPILER_ID");
1126     if (parameters.empty()) {
1127       return compilerId;
1128     }
1129     if (compilerId.empty()) {
1130       return parameters.front().empty() ? "1" : "0";
1131     }
1132     static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$");
1133
1134     for (auto const& param : parameters) {
1135
1136       if (!compilerIdValidator.find(param)) {
1137         reportError(context, content->GetOriginalExpression(),
1138                     "Expression syntax not recognized.");
1139         return std::string();
1140       }
1141
1142       if (strcmp(param.c_str(), compilerId.c_str()) == 0) {
1143         return "1";
1144       }
1145
1146       if (cmsysString_strcasecmp(param.c_str(), compilerId.c_str()) == 0) {
1147         switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) {
1148           case cmPolicies::WARN: {
1149             context->LG->GetCMakeInstance()->IssueMessage(
1150               MessageType::AUTHOR_WARNING,
1151               cmPolicies::GetPolicyWarning(cmPolicies::CMP0044),
1152               context->Backtrace);
1153             CM_FALLTHROUGH;
1154           }
1155           case cmPolicies::OLD:
1156             return "1";
1157           case cmPolicies::NEW:
1158           case cmPolicies::REQUIRED_ALWAYS:
1159           case cmPolicies::REQUIRED_IF_USED:
1160             break;
1161         }
1162       }
1163     }
1164     return "0";
1165   }
1166
1167   const char* const CompilerLanguage;
1168 };
1169
1170 static const CompilerIdNode cCompilerIdNode("C"), cxxCompilerIdNode("CXX"),
1171   cudaCompilerIdNode("CUDA"), objcCompilerIdNode("OBJC"),
1172   objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran"),
1173   hipCompilerIdNode("HIP"), ispcCompilerIdNode("ISPC");
1174
1175 struct CompilerVersionNode : public cmGeneratorExpressionNode
1176 {
1177   CompilerVersionNode(const char* compilerLang)
1178     : CompilerLanguage(compilerLang)
1179   {
1180   }
1181
1182   int NumExpectedParameters() const override { return OneOrZeroParameters; }
1183
1184   std::string Evaluate(
1185     const std::vector<std::string>& parameters,
1186     cmGeneratorExpressionContext* context,
1187     const GeneratorExpressionContent* content,
1188     cmGeneratorExpressionDAGChecker* dagChecker) const override
1189   {
1190     if (!context->HeadTarget) {
1191       std::ostringstream e;
1192       e << "$<" << this->CompilerLanguage
1193         << "_COMPILER_VERSION> may only be used with binary targets.  It "
1194            "may not be used with add_custom_command or add_custom_target.";
1195       reportError(context, content->GetOriginalExpression(), e.str());
1196       return {};
1197     }
1198     return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
1199                                       this->CompilerLanguage);
1200   }
1201
1202   std::string EvaluateWithLanguage(const std::vector<std::string>& parameters,
1203                                    cmGeneratorExpressionContext* context,
1204                                    const GeneratorExpressionContent* content,
1205                                    cmGeneratorExpressionDAGChecker* /*unused*/,
1206                                    const std::string& lang) const
1207   {
1208     std::string const& compilerVersion =
1209       context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
1210                                                     "_COMPILER_VERSION");
1211     if (parameters.empty()) {
1212       return compilerVersion;
1213     }
1214
1215     static cmsys::RegularExpression compilerIdValidator("^[0-9\\.]*$");
1216     if (!compilerIdValidator.find(parameters.front())) {
1217       reportError(context, content->GetOriginalExpression(),
1218                   "Expression syntax not recognized.");
1219       return {};
1220     }
1221     if (compilerVersion.empty()) {
1222       return parameters.front().empty() ? "1" : "0";
1223     }
1224
1225     return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL,
1226                                          parameters.front(), compilerVersion)
1227       ? "1"
1228       : "0";
1229   }
1230
1231   const char* const CompilerLanguage;
1232 };
1233
1234 static const CompilerVersionNode cCompilerVersionNode("C"),
1235   cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"),
1236   objcCompilerVersionNode("OBJC"), objcxxCompilerVersionNode("OBJCXX"),
1237   fortranCompilerVersionNode("Fortran"), ispcCompilerVersionNode("ISPC"),
1238   hipCompilerVersionNode("HIP");
1239
1240 struct PlatformIdNode : public cmGeneratorExpressionNode
1241 {
1242   PlatformIdNode() {} // NOLINT(modernize-use-equals-default)
1243
1244   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1245
1246   std::string Evaluate(
1247     const std::vector<std::string>& parameters,
1248     cmGeneratorExpressionContext* context,
1249     const GeneratorExpressionContent* /*content*/,
1250     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1251   {
1252     std::string const& platformId =
1253       context->LG->GetMakefile()->GetSafeDefinition("CMAKE_SYSTEM_NAME");
1254     if (parameters.empty()) {
1255       return platformId;
1256     }
1257
1258     if (platformId.empty()) {
1259       return parameters.front().empty() ? "1" : "0";
1260     }
1261
1262     for (auto const& param : parameters) {
1263       if (param == platformId) {
1264         return "1";
1265       }
1266     }
1267     return "0";
1268   }
1269 };
1270 static struct PlatformIdNode platformIdNode;
1271
1272 template <cmSystemTools::CompareOp Op>
1273 struct VersionNode : public cmGeneratorExpressionNode
1274 {
1275   VersionNode() {} // NOLINT(modernize-use-equals-default)
1276
1277   int NumExpectedParameters() const override { return 2; }
1278
1279   std::string Evaluate(
1280     const std::vector<std::string>& parameters,
1281     cmGeneratorExpressionContext* /*context*/,
1282     const GeneratorExpressionContent* /*content*/,
1283     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1284   {
1285     return cmSystemTools::VersionCompare(Op, parameters.front(), parameters[1])
1286       ? "1"
1287       : "0";
1288   }
1289 };
1290
1291 static const VersionNode<cmSystemTools::OP_GREATER> versionGreaterNode;
1292 static const VersionNode<cmSystemTools::OP_GREATER_EQUAL> versionGreaterEqNode;
1293 static const VersionNode<cmSystemTools::OP_LESS> versionLessNode;
1294 static const VersionNode<cmSystemTools::OP_LESS_EQUAL> versionLessEqNode;
1295 static const VersionNode<cmSystemTools::OP_EQUAL> versionEqualNode;
1296
1297 static const struct LinkOnlyNode : public cmGeneratorExpressionNode
1298 {
1299   LinkOnlyNode() {} // NOLINT(modernize-use-equals-default)
1300
1301   std::string Evaluate(
1302     const std::vector<std::string>& parameters,
1303     cmGeneratorExpressionContext* context,
1304     const GeneratorExpressionContent* content,
1305     cmGeneratorExpressionDAGChecker* dagChecker) const override
1306   {
1307     if (!dagChecker) {
1308       reportError(context, content->GetOriginalExpression(),
1309                   "$<LINK_ONLY:...> may only be used for linking");
1310       return std::string();
1311     }
1312     if (!dagChecker->GetTransitivePropertiesOnly()) {
1313       return parameters.front();
1314     }
1315     return std::string();
1316   }
1317 } linkOnlyNode;
1318
1319 static const struct ConfigurationNode : public cmGeneratorExpressionNode
1320 {
1321   ConfigurationNode() {} // NOLINT(modernize-use-equals-default)
1322
1323   int NumExpectedParameters() const override { return 0; }
1324
1325   std::string Evaluate(
1326     const std::vector<std::string>& /*parameters*/,
1327     cmGeneratorExpressionContext* context,
1328     const GeneratorExpressionContent* /*content*/,
1329     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1330   {
1331     context->HadContextSensitiveCondition = true;
1332     return context->Config;
1333   }
1334 } configurationNode;
1335
1336 static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
1337 {
1338   ConfigurationTestNode() {} // NOLINT(modernize-use-equals-default)
1339
1340   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1341
1342   std::string Evaluate(
1343     const std::vector<std::string>& parameters,
1344     cmGeneratorExpressionContext* context,
1345     const GeneratorExpressionContent* content,
1346     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1347   {
1348     if (parameters.empty()) {
1349       return configurationNode.Evaluate(parameters, context, content, nullptr);
1350     }
1351     static cmsys::RegularExpression configValidator("^[A-Za-z0-9_]*$");
1352     if (!configValidator.find(parameters.front())) {
1353       reportError(context, content->GetOriginalExpression(),
1354                   "Expression syntax not recognized.");
1355       return std::string();
1356     }
1357     context->HadContextSensitiveCondition = true;
1358     for (auto const& param : parameters) {
1359       if (context->Config.empty()) {
1360         if (param.empty()) {
1361           return "1";
1362         }
1363       } else if (cmsysString_strcasecmp(param.c_str(),
1364                                         context->Config.c_str()) == 0) {
1365         return "1";
1366       }
1367     }
1368
1369     if (context->CurrentTarget && context->CurrentTarget->IsImported()) {
1370       cmValue loc = nullptr;
1371       cmValue imp = nullptr;
1372       std::string suffix;
1373       if (context->CurrentTarget->Target->GetMappedConfig(context->Config, loc,
1374                                                           imp, suffix)) {
1375         // This imported target has an appropriate location
1376         // for this (possibly mapped) config.
1377         // Check if there is a proper config mapping for the tested config.
1378         std::vector<std::string> mappedConfigs;
1379         std::string mapProp = cmStrCat(
1380           "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(context->Config));
1381         if (cmValue mapValue = context->CurrentTarget->GetProperty(mapProp)) {
1382           cmExpandList(cmSystemTools::UpperCase(*mapValue), mappedConfigs);
1383
1384           for (auto const& param : parameters) {
1385             if (cm::contains(mappedConfigs, cmSystemTools::UpperCase(param))) {
1386               return "1";
1387             }
1388           }
1389         }
1390       }
1391     }
1392     return "0";
1393   }
1394 } configurationTestNode;
1395
1396 static const struct JoinNode : public cmGeneratorExpressionNode
1397 {
1398   JoinNode() {} // NOLINT(modernize-use-equals-default)
1399
1400   int NumExpectedParameters() const override { return 2; }
1401
1402   bool AcceptsArbitraryContentParameter() const override { return true; }
1403
1404   std::string Evaluate(
1405     const std::vector<std::string>& parameters,
1406     cmGeneratorExpressionContext* /*context*/,
1407     const GeneratorExpressionContent* /*content*/,
1408     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1409   {
1410     std::vector<std::string> list = cmExpandedList(parameters.front());
1411     return cmJoin(list, parameters[1]);
1412   }
1413 } joinNode;
1414
1415 static const struct CompileLanguageNode : public cmGeneratorExpressionNode
1416 {
1417   CompileLanguageNode() {} // NOLINT(modernize-use-equals-default)
1418
1419   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1420
1421   std::string Evaluate(
1422     const std::vector<std::string>& parameters,
1423     cmGeneratorExpressionContext* context,
1424     const GeneratorExpressionContent* content,
1425     cmGeneratorExpressionDAGChecker* dagChecker) const override
1426   {
1427     if (context->Language.empty() &&
1428         (!dagChecker || !dagChecker->EvaluatingCompileExpression())) {
1429       reportError(
1430         context, content->GetOriginalExpression(),
1431         "$<COMPILE_LANGUAGE:...> may only be used to specify include "
1432         "directories, compile definitions, compile options, and to evaluate "
1433         "components of the file(GENERATE) command.");
1434       return std::string();
1435     }
1436
1437     cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1438     std::string genName = gg->GetName();
1439     if (genName.find("Makefiles") == std::string::npos &&
1440         genName.find("Ninja") == std::string::npos &&
1441         genName.find("Visual Studio") == std::string::npos &&
1442         genName.find("Xcode") == std::string::npos &&
1443         genName.find("Watcom WMake") == std::string::npos) {
1444       reportError(context, content->GetOriginalExpression(),
1445                   "$<COMPILE_LANGUAGE:...> not supported for this generator.");
1446       return std::string();
1447     }
1448     if (parameters.empty()) {
1449       return context->Language;
1450     }
1451
1452     for (auto const& param : parameters) {
1453       if (context->Language == param) {
1454         return "1";
1455       }
1456     }
1457     return "0";
1458   }
1459 } languageNode;
1460
1461 static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode
1462 {
1463   CompileLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default)
1464
1465   int NumExpectedParameters() const override { return TwoOrMoreParameters; }
1466
1467   std::string Evaluate(
1468     const std::vector<std::string>& parameters,
1469     cmGeneratorExpressionContext* context,
1470     const GeneratorExpressionContent* content,
1471     cmGeneratorExpressionDAGChecker* dagChecker) const override
1472   {
1473     if (!context->HeadTarget ||
1474         (context->Language.empty() &&
1475          (!dagChecker || !dagChecker->EvaluatingCompileExpression()))) {
1476       // reportError(context, content->GetOriginalExpression(), "");
1477       reportError(
1478         context, content->GetOriginalExpression(),
1479         "$<COMPILE_LANG_AND_ID:lang,id> may only be used with binary targets "
1480         "to specify include directories, compile definitions, and compile "
1481         "options.  It may not be used with the add_custom_command, "
1482         "add_custom_target, or file(GENERATE) commands.");
1483       return std::string();
1484     }
1485     cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1486     std::string genName = gg->GetName();
1487     if (genName.find("Makefiles") == std::string::npos &&
1488         genName.find("Ninja") == std::string::npos &&
1489         genName.find("Visual Studio") == std::string::npos &&
1490         genName.find("Xcode") == std::string::npos &&
1491         genName.find("Watcom WMake") == std::string::npos) {
1492       reportError(
1493         context, content->GetOriginalExpression(),
1494         "$<COMPILE_LANG_AND_ID:lang,id> not supported for this generator.");
1495       return std::string();
1496     }
1497
1498     const std::string& lang = context->Language;
1499     if (lang == parameters.front()) {
1500       std::vector<std::string> idParameter((parameters.cbegin() + 1),
1501                                            parameters.cend());
1502       return CompilerIdNode{ lang.c_str() }.EvaluateWithLanguage(
1503         idParameter, context, content, dagChecker, lang);
1504     }
1505     return "0";
1506   }
1507 } languageAndIdNode;
1508
1509 static const struct LinkLanguageNode : public cmGeneratorExpressionNode
1510 {
1511   LinkLanguageNode() {} // NOLINT(modernize-use-equals-default)
1512
1513   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1514
1515   std::string Evaluate(
1516     const std::vector<std::string>& parameters,
1517     cmGeneratorExpressionContext* context,
1518     const GeneratorExpressionContent* content,
1519     cmGeneratorExpressionDAGChecker* dagChecker) const override
1520   {
1521     if (!context->HeadTarget || !dagChecker ||
1522         !(dagChecker->EvaluatingLinkExpression() ||
1523           dagChecker->EvaluatingLinkLibraries())) {
1524       reportError(context, content->GetOriginalExpression(),
1525                   "$<LINK_LANGUAGE:...> may only be used with binary targets "
1526                   "to specify link libraries, link directories, link options "
1527                   "and link depends.");
1528       return std::string();
1529     }
1530     if (dagChecker->EvaluatingLinkLibraries() && parameters.empty()) {
1531       reportError(
1532         context, content->GetOriginalExpression(),
1533         "$<LINK_LANGUAGE> is not supported in link libraries expression.");
1534       return std::string();
1535     }
1536
1537     cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1538     std::string genName = gg->GetName();
1539     if (genName.find("Makefiles") == std::string::npos &&
1540         genName.find("Ninja") == std::string::npos &&
1541         genName.find("Visual Studio") == std::string::npos &&
1542         genName.find("Xcode") == std::string::npos &&
1543         genName.find("Watcom WMake") == std::string::npos) {
1544       reportError(context, content->GetOriginalExpression(),
1545                   "$<LINK_LANGUAGE:...> not supported for this generator.");
1546       return std::string();
1547     }
1548
1549     if (dagChecker->EvaluatingLinkLibraries()) {
1550       context->HadHeadSensitiveCondition = true;
1551       context->HadLinkLanguageSensitiveCondition = true;
1552     }
1553
1554     if (parameters.empty()) {
1555       return context->Language;
1556     }
1557
1558     for (auto const& param : parameters) {
1559       if (context->Language == param) {
1560         return "1";
1561       }
1562     }
1563     return "0";
1564   }
1565 } linkLanguageNode;
1566
1567 namespace {
1568 struct LinkerId
1569 {
1570   static std::string Evaluate(const std::vector<std::string>& parameters,
1571                               cmGeneratorExpressionContext* context,
1572                               const GeneratorExpressionContent* content,
1573                               const std::string& lang)
1574   {
1575     std::string const& linkerId =
1576       context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
1577                                                     "_COMPILER_ID");
1578     if (parameters.empty()) {
1579       return linkerId;
1580     }
1581     if (linkerId.empty()) {
1582       return parameters.front().empty() ? "1" : "0";
1583     }
1584     static cmsys::RegularExpression linkerIdValidator("^[A-Za-z0-9_]*$");
1585
1586     for (auto const& param : parameters) {
1587       if (!linkerIdValidator.find(param)) {
1588         reportError(context, content->GetOriginalExpression(),
1589                     "Expression syntax not recognized.");
1590         return std::string();
1591       }
1592
1593       if (param == linkerId) {
1594         return "1";
1595       }
1596     }
1597     return "0";
1598   }
1599 };
1600 }
1601
1602 static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
1603 {
1604   LinkLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default)
1605
1606   int NumExpectedParameters() const override { return TwoOrMoreParameters; }
1607
1608   std::string Evaluate(
1609     const std::vector<std::string>& parameters,
1610     cmGeneratorExpressionContext* context,
1611     const GeneratorExpressionContent* content,
1612     cmGeneratorExpressionDAGChecker* dagChecker) const override
1613   {
1614     if (!context->HeadTarget || !dagChecker ||
1615         !(dagChecker->EvaluatingLinkExpression() ||
1616           dagChecker->EvaluatingLinkLibraries())) {
1617       reportError(
1618         context, content->GetOriginalExpression(),
1619         "$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets "
1620         "to specify link libraries, link directories, link options, and link "
1621         "depends.");
1622       return std::string();
1623     }
1624
1625     cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1626     std::string genName = gg->GetName();
1627     if (genName.find("Makefiles") == std::string::npos &&
1628         genName.find("Ninja") == std::string::npos &&
1629         genName.find("Visual Studio") == std::string::npos &&
1630         genName.find("Xcode") == std::string::npos &&
1631         genName.find("Watcom WMake") == std::string::npos) {
1632       reportError(
1633         context, content->GetOriginalExpression(),
1634         "$<LINK_LANG_AND_ID:lang,id> not supported for this generator.");
1635       return std::string();
1636     }
1637
1638     if (dagChecker->EvaluatingLinkLibraries()) {
1639       context->HadHeadSensitiveCondition = true;
1640       context->HadLinkLanguageSensitiveCondition = true;
1641     }
1642
1643     const std::string& lang = context->Language;
1644     if (lang == parameters.front()) {
1645       std::vector<std::string> idParameter((parameters.cbegin() + 1),
1646                                            parameters.cend());
1647       return LinkerId::Evaluate(idParameter, context, content, lang);
1648     }
1649     return "0";
1650   }
1651 } linkLanguageAndIdNode;
1652
1653 static const struct LinkLibraryNode : public cmGeneratorExpressionNode
1654 {
1655   LinkLibraryNode() {} // NOLINT(modernize-use-equals-default)
1656
1657   int NumExpectedParameters() const override { return OneOrMoreParameters; }
1658
1659   std::string Evaluate(
1660     const std::vector<std::string>& parameters,
1661     cmGeneratorExpressionContext* context,
1662     const GeneratorExpressionContent* content,
1663     cmGeneratorExpressionDAGChecker* dagChecker) const override
1664   {
1665     using ForGenex = cmGeneratorExpressionDAGChecker::ForGenex;
1666
1667     if (!context->HeadTarget || !dagChecker ||
1668         !dagChecker->EvaluatingLinkLibraries(nullptr,
1669                                              ForGenex::LINK_LIBRARY)) {
1670       reportError(context, content->GetOriginalExpression(),
1671                   "$<LINK_LIBRARY:...> may only be used with binary targets "
1672                   "to specify link libraries through 'LINK_LIBRARIES', "
1673                   "'INTERFACE_LINK_LIBRARIES', and "
1674                   "'INTERFACE_LINK_LIBRARIES_DIRECT' properties.");
1675       return std::string();
1676     }
1677
1678     std::vector<std::string> list;
1679     cmExpandLists(parameters.begin(), parameters.end(), list);
1680     if (list.empty()) {
1681       reportError(
1682         context, content->GetOriginalExpression(),
1683         "$<LINK_LIBRARY:...> expects a feature name as first argument.");
1684       return std::string();
1685     }
1686     if (list.size() == 1) {
1687       // no libraries specified, ignore this genex
1688       return std::string();
1689     }
1690
1691     static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$");
1692     auto const& feature = list.front();
1693     if (!featureNameValidator.find(feature)) {
1694       reportError(context, content->GetOriginalExpression(),
1695                   cmStrCat("The feature name '", feature,
1696                            "' contains invalid characters."));
1697       return std::string();
1698     }
1699
1700     const auto LL_BEGIN = cmStrCat("<LINK_LIBRARY:", feature, '>');
1701     const auto LL_END = cmStrCat("</LINK_LIBRARY:", feature, '>');
1702
1703     // filter out $<LINK_LIBRARY:..> tags with same feature
1704     // and raise an error for any different feature
1705     cm::erase_if(list, [&](const std::string& item) -> bool {
1706       return item == LL_BEGIN || item == LL_END;
1707     });
1708     auto it =
1709       std::find_if(list.cbegin() + 1, list.cend(),
1710                    [&feature](const std::string& item) -> bool {
1711                      return cmHasPrefix(item, "<LINK_LIBRARY:"_s) &&
1712                        item.substr(14, item.find('>', 14) - 14) != feature;
1713                    });
1714     if (it != list.cend()) {
1715       reportError(
1716         context, content->GetOriginalExpression(),
1717         "$<LINK_LIBRARY:...> with different features cannot be nested.");
1718       return std::string();
1719     }
1720     // $<LINK_GROUP:...> must not appear as part of $<LINK_LIBRARY:...>
1721     it = std::find_if(list.cbegin() + 1, list.cend(),
1722                       [](const std::string& item) -> bool {
1723                         return cmHasPrefix(item, "<LINK_GROUP:"_s);
1724                       });
1725     if (it != list.cend()) {
1726       reportError(context, content->GetOriginalExpression(),
1727                   "$<LINK_GROUP:...> cannot be nested inside a "
1728                   "$<LINK_LIBRARY:...> expression.");
1729       return std::string();
1730     }
1731
1732     list.front() = LL_BEGIN;
1733     list.push_back(LL_END);
1734
1735     return cmJoin(list, ";"_s);
1736   }
1737 } linkLibraryNode;
1738
1739 static const struct LinkGroupNode : public cmGeneratorExpressionNode
1740 {
1741   LinkGroupNode() {} // NOLINT(modernize-use-equals-default)
1742
1743   int NumExpectedParameters() const override { return OneOrMoreParameters; }
1744
1745   std::string Evaluate(
1746     const std::vector<std::string>& parameters,
1747     cmGeneratorExpressionContext* context,
1748     const GeneratorExpressionContent* content,
1749     cmGeneratorExpressionDAGChecker* dagChecker) const override
1750   {
1751     using ForGenex = cmGeneratorExpressionDAGChecker::ForGenex;
1752
1753     if (!context->HeadTarget || !dagChecker ||
1754         !dagChecker->EvaluatingLinkLibraries(nullptr, ForGenex::LINK_GROUP)) {
1755       reportError(
1756         context, content->GetOriginalExpression(),
1757         "$<LINK_GROUP:...> may only be used with binary targets "
1758         "to specify group of link libraries through 'LINK_LIBRARIES', "
1759         "'INTERFACE_LINK_LIBRARIES', and "
1760         "'INTERFACE_LINK_LIBRARIES_DIRECT' properties.");
1761       return std::string();
1762     }
1763
1764     std::vector<std::string> list;
1765     cmExpandLists(parameters.begin(), parameters.end(), list);
1766     if (list.empty()) {
1767       reportError(
1768         context, content->GetOriginalExpression(),
1769         "$<LINK_GROUP:...> expects a feature name as first argument.");
1770       return std::string();
1771     }
1772     // $<LINK_GROUP:..> cannot be nested
1773     if (std::find_if(list.cbegin(), list.cend(),
1774                      [](const std::string& item) -> bool {
1775                        return cmHasPrefix(item, "<LINK_GROUP"_s);
1776                      }) != list.cend()) {
1777       reportError(context, content->GetOriginalExpression(),
1778                   "$<LINK_GROUP:...> cannot be nested.");
1779       return std::string();
1780     }
1781     if (list.size() == 1) {
1782       // no libraries specified, ignore this genex
1783       return std::string();
1784     }
1785
1786     static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$");
1787     auto const& feature = list.front();
1788     if (!featureNameValidator.find(feature)) {
1789       reportError(context, content->GetOriginalExpression(),
1790                   cmStrCat("The feature name '", feature,
1791                            "' contains invalid characters."));
1792       return std::string();
1793     }
1794
1795     const auto LG_BEGIN = cmStrCat(
1796       "<LINK_GROUP:", feature, ':',
1797       cmJoin(cmRange<decltype(list.cbegin())>(list.cbegin() + 1, list.cend()),
1798              "|"_s),
1799       '>');
1800     const auto LG_END = cmStrCat("</LINK_GROUP:", feature, '>');
1801
1802     list.front() = LG_BEGIN;
1803     list.push_back(LG_END);
1804
1805     return cmJoin(list, ";"_s);
1806   }
1807 } linkGroupNode;
1808
1809 static const struct HostLinkNode : public cmGeneratorExpressionNode
1810 {
1811   HostLinkNode() {} // NOLINT(modernize-use-equals-default)
1812
1813   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1814
1815   std::string Evaluate(
1816     const std::vector<std::string>& parameters,
1817     cmGeneratorExpressionContext* context,
1818     const GeneratorExpressionContent* content,
1819     cmGeneratorExpressionDAGChecker* dagChecker) const override
1820   {
1821     if (!context->HeadTarget || !dagChecker ||
1822         !dagChecker->EvaluatingLinkOptionsExpression()) {
1823       reportError(context, content->GetOriginalExpression(),
1824                   "$<HOST_LINK:...> may only be used with binary targets "
1825                   "to specify link options.");
1826       return std::string();
1827     }
1828
1829     return context->HeadTarget->IsDeviceLink() ? std::string()
1830                                                : cmJoin(parameters, ";");
1831   }
1832 } hostLinkNode;
1833
1834 static const struct DeviceLinkNode : public cmGeneratorExpressionNode
1835 {
1836   DeviceLinkNode() {} // NOLINT(modernize-use-equals-default)
1837
1838   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1839
1840   std::string Evaluate(
1841     const std::vector<std::string>& parameters,
1842     cmGeneratorExpressionContext* context,
1843     const GeneratorExpressionContent* content,
1844     cmGeneratorExpressionDAGChecker* dagChecker) const override
1845   {
1846     if (!context->HeadTarget || !dagChecker ||
1847         !dagChecker->EvaluatingLinkOptionsExpression()) {
1848       reportError(context, content->GetOriginalExpression(),
1849                   "$<DEVICE_LINK:...> may only be used with binary targets "
1850                   "to specify link options.");
1851       return std::string();
1852     }
1853
1854     if (context->HeadTarget->IsDeviceLink()) {
1855       std::vector<std::string> list;
1856       cmExpandLists(parameters.begin(), parameters.end(), list);
1857       const auto DL_BEGIN = "<DEVICE_LINK>"_s;
1858       const auto DL_END = "</DEVICE_LINK>"_s;
1859       cm::erase_if(list, [&](const std::string& item) {
1860         return item == DL_BEGIN || item == DL_END;
1861       });
1862
1863       list.insert(list.begin(), static_cast<std::string>(DL_BEGIN));
1864       list.push_back(static_cast<std::string>(DL_END));
1865
1866       return cmJoin(list, ";");
1867     }
1868
1869     return std::string();
1870   }
1871 } deviceLinkNode;
1872
1873 static std::string getLinkedTargetsContent(
1874   cmGeneratorTarget const* target, std::string const& prop,
1875   cmGeneratorExpressionContext* context,
1876   cmGeneratorExpressionDAGChecker* dagChecker)
1877 {
1878   std::string result;
1879   if (cmLinkImplementationLibraries const* impl =
1880         target->GetLinkImplementationLibraries(
1881           context->Config, cmGeneratorTarget::LinkInterfaceFor::Usage)) {
1882     for (cmLinkImplItem const& lib : impl->Libraries) {
1883       if (lib.Target) {
1884         // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
1885         // caller's property and hand-evaluate it as if it were compiled.
1886         // Create a context as cmCompiledGeneratorExpression::Evaluate does.
1887         cmGeneratorExpressionContext libContext(
1888           target->GetLocalGenerator(), context->Config, context->Quiet, target,
1889           target, context->EvaluateForBuildsystem, lib.Backtrace,
1890           context->Language);
1891         std::string libResult =
1892           lib.Target->EvaluateInterfaceProperty(prop, &libContext, dagChecker);
1893         if (!libResult.empty()) {
1894           if (result.empty()) {
1895             result = std::move(libResult);
1896           } else {
1897             result.reserve(result.size() + 1 + libResult.size());
1898             result += ";";
1899             result += libResult;
1900           }
1901         }
1902       }
1903     }
1904   }
1905   return result;
1906 }
1907
1908 static const struct TargetPropertyNode : public cmGeneratorExpressionNode
1909 {
1910   TargetPropertyNode() {} // NOLINT(modernize-use-equals-default)
1911
1912   // This node handles errors on parameter count itself.
1913   int NumExpectedParameters() const override { return OneOrMoreParameters; }
1914
1915   static const char* GetErrorText(std::string const& targetName,
1916                                   std::string const& propertyName)
1917   {
1918     static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
1919     if (targetName.empty() && propertyName.empty()) {
1920       return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty "
1921              "target name and property name.";
1922     }
1923     if (targetName.empty()) {
1924       return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty "
1925              "target name.";
1926     }
1927     if (!cmGeneratorExpression::IsValidTargetName(targetName)) {
1928       if (!propertyNameValidator.find(propertyName)) {
1929         return "Target name and property name not supported.";
1930       }
1931       return "Target name not supported.";
1932     }
1933     return nullptr;
1934   }
1935
1936   std::string Evaluate(
1937     const std::vector<std::string>& parameters,
1938     cmGeneratorExpressionContext* context,
1939     const GeneratorExpressionContent* content,
1940     cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
1941   {
1942     static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
1943
1944     cmGeneratorTarget const* target = nullptr;
1945     std::string targetName;
1946     std::string propertyName;
1947
1948     if (parameters.size() == 2) {
1949       targetName = parameters[0];
1950       propertyName = parameters[1];
1951
1952       if (const char* e = GetErrorText(targetName, propertyName)) {
1953         reportError(context, content->GetOriginalExpression(), e);
1954         return std::string();
1955       }
1956       if (propertyName == "ALIASED_TARGET"_s) {
1957         if (context->LG->GetMakefile()->IsAlias(targetName)) {
1958           if (cmGeneratorTarget* tgt =
1959                 context->LG->FindGeneratorTargetToUse(targetName)) {
1960             return tgt->GetName();
1961           }
1962         }
1963         return std::string();
1964       }
1965       if (propertyName == "ALIAS_GLOBAL"_s) {
1966         if (context->LG->GetMakefile()->IsAlias(targetName)) {
1967           return context->LG->GetGlobalGenerator()->IsAlias(targetName)
1968             ? "TRUE"
1969             : "FALSE";
1970         }
1971         return std::string();
1972       }
1973       target = context->LG->FindGeneratorTargetToUse(targetName);
1974
1975       if (!target) {
1976         std::ostringstream e;
1977         e << "Target \"" << targetName << "\" not found.";
1978         reportError(context, content->GetOriginalExpression(), e.str());
1979         return std::string();
1980       }
1981       context->AllTargets.insert(target);
1982
1983     } else if (parameters.size() == 1) {
1984       target = context->HeadTarget;
1985       propertyName = parameters[0];
1986
1987       // Keep track of the properties seen while processing.
1988       // The evaluation of the LINK_LIBRARIES generator expressions
1989       // will check this to ensure that properties have one consistent
1990       // value for all evaluations.
1991       context->SeenTargetProperties.insert(propertyName);
1992
1993       context->HadHeadSensitiveCondition = true;
1994       if (!target) {
1995         reportError(
1996           context, content->GetOriginalExpression(),
1997           "$<TARGET_PROPERTY:prop>  may only be used with binary targets.  "
1998           "It may not be used with add_custom_command or add_custom_target.  "
1999           " "
2000           "Specify the target to read a property from using the "
2001           "$<TARGET_PROPERTY:tgt,prop> signature instead.");
2002         return std::string();
2003       }
2004     } else {
2005       reportError(
2006         context, content->GetOriginalExpression(),
2007         "$<TARGET_PROPERTY:...> expression requires one or two parameters");
2008       return std::string();
2009     }
2010
2011     if (propertyName == "SOURCES") {
2012       context->SourceSensitiveTargets.insert(target);
2013     }
2014
2015     if (propertyName.empty()) {
2016       reportError(
2017         context, content->GetOriginalExpression(),
2018         "$<TARGET_PROPERTY:...> expression requires a non-empty property "
2019         "name.");
2020       return std::string();
2021     }
2022
2023     if (!propertyNameValidator.find(propertyName)) {
2024       ::reportError(context, content->GetOriginalExpression(),
2025                     "Property name not supported.");
2026       return std::string();
2027     }
2028
2029     assert(target);
2030
2031     if (propertyName == "LINKER_LANGUAGE") {
2032       if (target->LinkLanguagePropagatesToDependents() && dagCheckerParent &&
2033           (dagCheckerParent->EvaluatingLinkLibraries() ||
2034            dagCheckerParent->EvaluatingSources())) {
2035         reportError(
2036           context, content->GetOriginalExpression(),
2037           "LINKER_LANGUAGE target property can not be used while evaluating "
2038           "link libraries for a static library");
2039         return std::string();
2040       }
2041       return target->GetLinkerLanguage(context->Config);
2042     }
2043
2044     std::string interfacePropertyName;
2045     bool isInterfaceProperty = false;
2046
2047 #define POPULATE_INTERFACE_PROPERTY_NAME(prop)                                \
2048   if (propertyName == #prop) {                                                \
2049     interfacePropertyName = "INTERFACE_" #prop;                               \
2050   } else if (propertyName == "INTERFACE_" #prop) {                            \
2051     interfacePropertyName = "INTERFACE_" #prop;                               \
2052     isInterfaceProperty = true;                                               \
2053   } else
2054
2055     CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(POPULATE_INTERFACE_PROPERTY_NAME)
2056     // Note that the above macro terminates with an else
2057     /* else */ if (cmHasLiteralPrefix(propertyName, "COMPILE_DEFINITIONS_")) {
2058       cmPolicies::PolicyStatus polSt =
2059         context->LG->GetPolicyStatus(cmPolicies::CMP0043);
2060       if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
2061         interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS";
2062       }
2063     }
2064 #undef POPULATE_INTERFACE_PROPERTY_NAME
2065
2066     bool evaluatingLinkLibraries = false;
2067
2068     if (dagCheckerParent) {
2069       if (dagCheckerParent->EvaluatingGenexExpression() ||
2070           dagCheckerParent->EvaluatingPICExpression()) {
2071         // No check required.
2072       } else if (dagCheckerParent->EvaluatingLinkLibraries()) {
2073         evaluatingLinkLibraries = true;
2074         if (!interfacePropertyName.empty()) {
2075           reportError(
2076             context, content->GetOriginalExpression(),
2077             "$<TARGET_PROPERTY:...> expression in link libraries "
2078             "evaluation depends on target property which is transitive "
2079             "over the link libraries, creating a recursion.");
2080           return std::string();
2081         }
2082       } else {
2083 #define ASSERT_TRANSITIVE_PROPERTY_METHOD(METHOD) dagCheckerParent->METHOD() ||
2084         assert(CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(
2085           ASSERT_TRANSITIVE_PROPERTY_METHOD) false); // NOLINT(clang-tidy)
2086 #undef ASSERT_TRANSITIVE_PROPERTY_METHOD
2087       }
2088     }
2089
2090     if (isInterfaceProperty) {
2091       return cmGeneratorExpression::StripEmptyListElements(
2092         target->EvaluateInterfaceProperty(propertyName, context,
2093                                           dagCheckerParent));
2094     }
2095
2096     cmGeneratorExpressionDAGChecker dagChecker(
2097       context->Backtrace, target, propertyName, content, dagCheckerParent);
2098
2099     switch (dagChecker.Check()) {
2100       case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
2101         dagChecker.ReportError(context, content->GetOriginalExpression());
2102         return std::string();
2103       case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
2104         // No error. We just skip cyclic references.
2105         return std::string();
2106       case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
2107         // We handle transitive properties above.  For non-transitive
2108         // properties we accept repeats anyway.
2109       case cmGeneratorExpressionDAGChecker::DAG:
2110         break;
2111     }
2112
2113     std::string result;
2114     bool haveProp = false;
2115     if (cmValue p = target->GetProperty(propertyName)) {
2116       result = *p;
2117       haveProp = true;
2118     } else if (evaluatingLinkLibraries) {
2119       return std::string();
2120     }
2121
2122     if (!haveProp && !target->IsImported() &&
2123         target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
2124       if (target->IsLinkInterfaceDependentBoolProperty(propertyName,
2125                                                        context->Config)) {
2126         context->HadContextSensitiveCondition = true;
2127         return target->GetLinkInterfaceDependentBoolProperty(propertyName,
2128                                                              context->Config)
2129           ? "1"
2130           : "0";
2131       }
2132       if (target->IsLinkInterfaceDependentStringProperty(propertyName,
2133                                                          context->Config)) {
2134         context->HadContextSensitiveCondition = true;
2135         const char* propContent =
2136           target->GetLinkInterfaceDependentStringProperty(propertyName,
2137                                                           context->Config);
2138         return propContent ? propContent : "";
2139       }
2140       if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName,
2141                                                             context->Config)) {
2142         context->HadContextSensitiveCondition = true;
2143         const char* propContent =
2144           target->GetLinkInterfaceDependentNumberMinProperty(propertyName,
2145                                                              context->Config);
2146         return propContent ? propContent : "";
2147       }
2148       if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName,
2149                                                             context->Config)) {
2150         context->HadContextSensitiveCondition = true;
2151         const char* propContent =
2152           target->GetLinkInterfaceDependentNumberMaxProperty(propertyName,
2153                                                              context->Config);
2154         return propContent ? propContent : "";
2155       }
2156     }
2157
2158     if (!target->IsImported() && dagCheckerParent &&
2159         !dagCheckerParent->EvaluatingLinkLibraries()) {
2160       if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName,
2161                                                             context->Config)) {
2162         context->HadContextSensitiveCondition = true;
2163         const char* propContent =
2164           target->GetLinkInterfaceDependentNumberMinProperty(propertyName,
2165                                                              context->Config);
2166         return propContent ? propContent : "";
2167       }
2168       if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName,
2169                                                             context->Config)) {
2170         context->HadContextSensitiveCondition = true;
2171         const char* propContent =
2172           target->GetLinkInterfaceDependentNumberMaxProperty(propertyName,
2173                                                              context->Config);
2174         return propContent ? propContent : "";
2175       }
2176     }
2177
2178     if (!interfacePropertyName.empty()) {
2179       result = cmGeneratorExpression::StripEmptyListElements(
2180         this->EvaluateDependentExpression(result, context->LG, context, target,
2181                                           &dagChecker, target));
2182       std::string linkedTargetsContent = getLinkedTargetsContent(
2183         target, interfacePropertyName, context, &dagChecker);
2184       if (!linkedTargetsContent.empty()) {
2185         result += (result.empty() ? "" : ";") + linkedTargetsContent;
2186       }
2187     }
2188     return result;
2189   }
2190 } targetPropertyNode;
2191
2192 static const struct TargetNameNode : public cmGeneratorExpressionNode
2193 {
2194   TargetNameNode() {} // NOLINT(modernize-use-equals-default)
2195
2196   bool GeneratesContent() const override { return true; }
2197
2198   bool AcceptsArbitraryContentParameter() const override { return true; }
2199   bool RequiresLiteralInput() const override { return true; }
2200
2201   std::string Evaluate(
2202     const std::vector<std::string>& parameters,
2203     cmGeneratorExpressionContext* /*context*/,
2204     const GeneratorExpressionContent* /*content*/,
2205     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2206   {
2207     return parameters.front();
2208   }
2209
2210   int NumExpectedParameters() const override { return 1; }
2211
2212 } targetNameNode;
2213
2214 static const struct TargetObjectsNode : public cmGeneratorExpressionNode
2215 {
2216   TargetObjectsNode() {} // NOLINT(modernize-use-equals-default)
2217
2218   std::string Evaluate(
2219     const std::vector<std::string>& parameters,
2220     cmGeneratorExpressionContext* context,
2221     const GeneratorExpressionContent* content,
2222     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2223   {
2224     std::string const& tgtName = parameters.front();
2225     cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
2226     if (!gt) {
2227       std::ostringstream e;
2228       e << "Objects of target \"" << tgtName
2229         << "\" referenced but no such target exists.";
2230       reportError(context, content->GetOriginalExpression(), e.str());
2231       return std::string();
2232     }
2233     cmStateEnums::TargetType type = gt->GetType();
2234     if (type != cmStateEnums::EXECUTABLE &&
2235         type != cmStateEnums::STATIC_LIBRARY &&
2236         type != cmStateEnums::SHARED_LIBRARY &&
2237         type != cmStateEnums::MODULE_LIBRARY &&
2238         type != cmStateEnums::OBJECT_LIBRARY) {
2239       std::ostringstream e;
2240       e << "Objects of target \"" << tgtName
2241         << "\" referenced but is not one of the allowed target types "
2242         << "(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT).";
2243       reportError(context, content->GetOriginalExpression(), e.str());
2244       return std::string();
2245     }
2246     cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
2247     {
2248       std::string reason;
2249       if (!context->EvaluateForBuildsystem &&
2250           !gt->Target->HasKnownObjectFileLocation(&reason)) {
2251         std::ostringstream e;
2252         e << "The evaluation of the TARGET_OBJECTS generator expression "
2253              "is only suitable for consumption by CMake (limited"
2254           << reason
2255           << ").  "
2256              "It is not suitable for writing out elsewhere.";
2257         reportError(context, content->GetOriginalExpression(), e.str());
2258         return std::string();
2259       }
2260     }
2261
2262     std::vector<std::string> objects;
2263
2264     if (gt->IsImported()) {
2265       cmValue loc = nullptr;
2266       cmValue imp = nullptr;
2267       std::string suffix;
2268       if (gt->Target->GetMappedConfig(context->Config, loc, imp, suffix)) {
2269         cmExpandList(*loc, objects);
2270       }
2271       context->HadContextSensitiveCondition = true;
2272     } else {
2273       gt->GetTargetObjectNames(context->Config, objects);
2274
2275       std::string obj_dir;
2276       if (context->EvaluateForBuildsystem && !gg->SupportsCrossConfigs()) {
2277         // Use object file directory with buildsystem placeholder.
2278         obj_dir = gt->ObjectDirectory;
2279         context->HadContextSensitiveCondition =
2280           gt->HasContextDependentSources();
2281       } else {
2282         // Use object file directory with per-config location.
2283         obj_dir = gt->GetObjectDirectory(context->Config);
2284         context->HadContextSensitiveCondition = true;
2285       }
2286
2287       for (std::string& o : objects) {
2288         o = cmStrCat(obj_dir, o);
2289       }
2290     }
2291
2292     // Create the cmSourceFile instances in the referencing directory.
2293     cmMakefile* mf = context->LG->GetMakefile();
2294     for (std::string const& o : objects) {
2295       mf->AddTargetObject(tgtName, o);
2296     }
2297
2298     return cmJoin(objects, ";");
2299   }
2300 } targetObjectsNode;
2301
2302 static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
2303 {
2304   TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default)
2305
2306   std::string Evaluate(
2307     const std::vector<std::string>& parameters,
2308     cmGeneratorExpressionContext* context,
2309     const GeneratorExpressionContent* content,
2310     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2311   {
2312     std::string const& tgtName = parameters.front();
2313     cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
2314     if (!gt) {
2315       std::ostringstream e;
2316       e << "Objects of target \"" << tgtName
2317         << "\" referenced but no such target exists.";
2318       reportError(context, content->GetOriginalExpression(), e.str());
2319       return std::string();
2320     }
2321     cmStateEnums::TargetType type = gt->GetType();
2322     if (type != cmStateEnums::EXECUTABLE &&
2323         type != cmStateEnums::SHARED_LIBRARY &&
2324         type != cmStateEnums::MODULE_LIBRARY) {
2325       std::ostringstream e;
2326       e << "Objects of target \"" << tgtName
2327         << "\" referenced but is not one of the allowed target types "
2328         << "(EXECUTABLE, SHARED, MODULE).";
2329       reportError(context, content->GetOriginalExpression(), e.str());
2330       return std::string();
2331     }
2332
2333     if (auto* cli = gt->GetLinkInformation(context->Config)) {
2334       std::vector<std::string> dllPaths;
2335       auto const& dlls = cli->GetRuntimeDLLs();
2336
2337       for (auto const& dll : dlls) {
2338         if (auto loc = dll->MaybeGetLocation(context->Config)) {
2339           dllPaths.emplace_back(*loc);
2340         }
2341       }
2342
2343       return cmJoin(dllPaths, ";");
2344     }
2345
2346     return "";
2347   }
2348 } targetRuntimeDllsNode;
2349
2350 static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
2351 {
2352   CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default)
2353
2354   int NumExpectedParameters() const override { return OneOrMoreParameters; }
2355
2356   std::string Evaluate(
2357     const std::vector<std::string>& parameters,
2358     cmGeneratorExpressionContext* context,
2359     const GeneratorExpressionContent* content,
2360     cmGeneratorExpressionDAGChecker* dagChecker) const override
2361   {
2362     cmGeneratorTarget const* target = context->HeadTarget;
2363     if (!target) {
2364       reportError(
2365         context, content->GetOriginalExpression(),
2366         "$<COMPILE_FEATURE> may only be used with binary targets.  It may "
2367         "not be used with add_custom_command or add_custom_target.");
2368       return std::string();
2369     }
2370     context->HadHeadSensitiveCondition = true;
2371
2372     using LangMap = std::map<std::string, std::vector<std::string>>;
2373     static LangMap availableFeatures;
2374
2375     LangMap testedFeatures;
2376     cmStandardLevelResolver standardResolver(context->LG->GetMakefile());
2377     for (std::string const& p : parameters) {
2378       std::string error;
2379       std::string lang;
2380       if (!standardResolver.CompileFeatureKnown(
2381             context->HeadTarget->Target->GetName(), p, lang, &error)) {
2382         reportError(context, content->GetOriginalExpression(), error);
2383         return std::string();
2384       }
2385       testedFeatures[lang].push_back(p);
2386
2387       if (availableFeatures.find(lang) == availableFeatures.end()) {
2388         cmValue featuresKnown =
2389           standardResolver.CompileFeaturesAvailable(lang, &error);
2390         if (!featuresKnown) {
2391           reportError(context, content->GetOriginalExpression(), error);
2392           return std::string();
2393         }
2394         cmExpandList(featuresKnown, availableFeatures[lang]);
2395       }
2396     }
2397
2398     bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries();
2399
2400     for (auto const& lit : testedFeatures) {
2401       std::vector<std::string> const& langAvailable =
2402         availableFeatures[lit.first];
2403       cmValue standardDefault = context->LG->GetMakefile()->GetDefinition(
2404         "CMAKE_" + lit.first + "_STANDARD_DEFAULT");
2405       for (std::string const& it : lit.second) {
2406         if (!cm::contains(langAvailable, it)) {
2407           return "0";
2408         }
2409         if (standardDefault && standardDefault->empty()) {
2410           // This compiler has no notion of language standard levels.
2411           // All features known for the language are always available.
2412           continue;
2413         }
2414         if (!standardResolver.HaveStandardAvailable(target, lit.first,
2415                                                     context->Config, it)) {
2416           if (evalLL) {
2417             cmValue l =
2418               target->GetLanguageStandard(lit.first, context->Config);
2419             if (!l) {
2420               l = standardDefault;
2421             }
2422             assert(l);
2423             context->MaxLanguageStandard[target][lit.first] = *l;
2424           } else {
2425             return "0";
2426           }
2427         }
2428       }
2429     }
2430     return "1";
2431   }
2432 } compileFeaturesNode;
2433
2434 static const char* targetPolicyWhitelist[] = {
2435   nullptr
2436 #define TARGET_POLICY_STRING(POLICY) , #POLICY
2437
2438   CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_STRING)
2439
2440 #undef TARGET_POLICY_STRING
2441 };
2442
2443 static cmPolicies::PolicyStatus statusForTarget(cmGeneratorTarget const* tgt,
2444                                                 const char* policy)
2445 {
2446 #define RETURN_POLICY(POLICY)                                                 \
2447   if (strcmp(policy, #POLICY) == 0) {                                         \
2448     return tgt->GetPolicyStatus##POLICY();                                    \
2449   }
2450
2451   CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY)
2452
2453 #undef RETURN_POLICY
2454
2455   assert(false && "Unreachable code. Not a valid policy");
2456   return cmPolicies::WARN;
2457 }
2458
2459 static cmPolicies::PolicyID policyForString(const char* policy_id)
2460 {
2461 #define RETURN_POLICY_ID(POLICY_ID)                                           \
2462   if (strcmp(policy_id, #POLICY_ID) == 0) {                                   \
2463     return cmPolicies::POLICY_ID;                                             \
2464   }
2465
2466   CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY_ID)
2467
2468 #undef RETURN_POLICY_ID
2469
2470   assert(false && "Unreachable code. Not a valid policy");
2471   return cmPolicies::CMP0002;
2472 }
2473
2474 static const struct TargetPolicyNode : public cmGeneratorExpressionNode
2475 {
2476   TargetPolicyNode() {} // NOLINT(modernize-use-equals-default)
2477
2478   int NumExpectedParameters() const override { return 1; }
2479
2480   std::string Evaluate(
2481     const std::vector<std::string>& parameters,
2482     cmGeneratorExpressionContext* context,
2483     const GeneratorExpressionContent* content,
2484     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2485   {
2486     if (!context->HeadTarget) {
2487       reportError(
2488         context, content->GetOriginalExpression(),
2489         "$<TARGET_POLICY:prop> may only be used with binary targets.  It "
2490         "may not be used with add_custom_command or add_custom_target.");
2491       return std::string();
2492     }
2493
2494     context->HadContextSensitiveCondition = true;
2495     context->HadHeadSensitiveCondition = true;
2496
2497     for (size_t i = 1; i < cm::size(targetPolicyWhitelist); ++i) {
2498       const char* policy = targetPolicyWhitelist[i];
2499       if (parameters.front() == policy) {
2500         cmLocalGenerator* lg = context->HeadTarget->GetLocalGenerator();
2501         switch (statusForTarget(context->HeadTarget, policy)) {
2502           case cmPolicies::WARN:
2503             lg->IssueMessage(
2504               MessageType::AUTHOR_WARNING,
2505               cmPolicies::GetPolicyWarning(policyForString(policy)));
2506             CM_FALLTHROUGH;
2507           case cmPolicies::REQUIRED_IF_USED:
2508           case cmPolicies::REQUIRED_ALWAYS:
2509           case cmPolicies::OLD:
2510             return "0";
2511           case cmPolicies::NEW:
2512             return "1";
2513         }
2514       }
2515     }
2516     reportError(
2517       context, content->GetOriginalExpression(),
2518       "$<TARGET_POLICY:prop> may only be used with a limited number of "
2519       "policies.  Currently it may be used with the following policies:\n"
2520
2521 #define STRINGIFY_HELPER(X) #X
2522 #define STRINGIFY(X) STRINGIFY_HELPER(X)
2523
2524 #define TARGET_POLICY_LIST_ITEM(POLICY) " * " STRINGIFY(POLICY) "\n"
2525
2526       CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_LIST_ITEM)
2527
2528 #undef TARGET_POLICY_LIST_ITEM
2529     );
2530     return std::string();
2531   }
2532
2533 } targetPolicyNode;
2534
2535 static const struct InstallPrefixNode : public cmGeneratorExpressionNode
2536 {
2537   InstallPrefixNode() {} // NOLINT(modernize-use-equals-default)
2538
2539   bool GeneratesContent() const override { return true; }
2540   int NumExpectedParameters() const override { return 0; }
2541
2542   std::string Evaluate(
2543     const std::vector<std::string>& /*parameters*/,
2544     cmGeneratorExpressionContext* context,
2545     const GeneratorExpressionContent* content,
2546     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2547   {
2548     reportError(context, content->GetOriginalExpression(),
2549                 "INSTALL_PREFIX is a marker for install(EXPORT) only.  It "
2550                 "should never be evaluated.");
2551     return std::string();
2552   }
2553
2554 } installPrefixNode;
2555
2556 class ArtifactDirTag;
2557 class ArtifactLinkerTag;
2558 class ArtifactNameTag;
2559 class ArtifactPathTag;
2560 class ArtifactPdbTag;
2561 class ArtifactSonameTag;
2562 class ArtifactBundleDirTag;
2563 class ArtifactBundleDirNameTag;
2564 class ArtifactBundleContentDirTag;
2565
2566 template <typename ArtifactT, typename ComponentT>
2567 struct TargetFilesystemArtifactDependency
2568 {
2569   static void AddDependency(cmGeneratorTarget* target,
2570                             cmGeneratorExpressionContext* context)
2571   {
2572     context->DependTargets.insert(target);
2573     context->AllTargets.insert(target);
2574   }
2575 };
2576
2577 struct TargetFilesystemArtifactDependencyCMP0112
2578 {
2579   static void AddDependency(cmGeneratorTarget* target,
2580                             cmGeneratorExpressionContext* context)
2581   {
2582     context->AllTargets.insert(target);
2583     cmLocalGenerator* lg = context->LG;
2584     switch (target->GetPolicyStatusCMP0112()) {
2585       case cmPolicies::WARN:
2586         if (lg->GetMakefile()->PolicyOptionalWarningEnabled(
2587               "CMAKE_POLICY_WARNING_CMP0112")) {
2588           std::string err =
2589             cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0112),
2590                      "\nDependency being added to target:\n  \"",
2591                      target->GetName(), "\"\n");
2592           lg->GetCMakeInstance()->IssueMessage(MessageType ::AUTHOR_WARNING,
2593                                                err, context->Backtrace);
2594         }
2595         CM_FALLTHROUGH;
2596       case cmPolicies::OLD:
2597         context->DependTargets.insert(target);
2598         break;
2599       case cmPolicies::REQUIRED_IF_USED:
2600       case cmPolicies::REQUIRED_ALWAYS:
2601       case cmPolicies::NEW:
2602         break;
2603     }
2604   }
2605 };
2606
2607 template <typename ArtifactT>
2608 struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactNameTag>
2609   : TargetFilesystemArtifactDependencyCMP0112
2610 {
2611 };
2612 template <typename ArtifactT>
2613 struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactDirTag>
2614   : TargetFilesystemArtifactDependencyCMP0112
2615 {
2616 };
2617 template <>
2618 struct TargetFilesystemArtifactDependency<ArtifactBundleDirTag,
2619                                           ArtifactPathTag>
2620   : TargetFilesystemArtifactDependencyCMP0112
2621 {
2622 };
2623 template <>
2624 struct TargetFilesystemArtifactDependency<ArtifactBundleDirNameTag,
2625                                           ArtifactPathTag>
2626   : TargetFilesystemArtifactDependencyCMP0112
2627 {
2628 };
2629 template <>
2630 struct TargetFilesystemArtifactDependency<ArtifactBundleContentDirTag,
2631                                           ArtifactPathTag>
2632   : TargetFilesystemArtifactDependencyCMP0112
2633 {
2634 };
2635
2636 template <typename ArtifactT>
2637 struct TargetFilesystemArtifactResultCreator
2638 {
2639   static std::string Create(cmGeneratorTarget* target,
2640                             cmGeneratorExpressionContext* context,
2641                             const GeneratorExpressionContent* content);
2642 };
2643
2644 template <>
2645 struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag>
2646 {
2647   static std::string Create(cmGeneratorTarget* target,
2648                             cmGeneratorExpressionContext* context,
2649                             const GeneratorExpressionContent* content)
2650   {
2651     // The target soname file (.so.1).
2652     if (target->IsDLLPlatform()) {
2653       ::reportError(context, content->GetOriginalExpression(),
2654                     "TARGET_SONAME_FILE is not allowed "
2655                     "for DLL target platforms.");
2656       return std::string();
2657     }
2658     if (target->GetType() != cmStateEnums::SHARED_LIBRARY) {
2659       ::reportError(context, content->GetOriginalExpression(),
2660                     "TARGET_SONAME_FILE is allowed only for "
2661                     "SHARED libraries.");
2662       return std::string();
2663     }
2664     std::string result = cmStrCat(target->GetDirectory(context->Config), '/',
2665                                   target->GetSOName(context->Config));
2666     return result;
2667   }
2668 };
2669
2670 template <>
2671 struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag>
2672 {
2673   static std::string Create(cmGeneratorTarget* target,
2674                             cmGeneratorExpressionContext* context,
2675                             const GeneratorExpressionContent* content)
2676   {
2677     if (target->IsImported()) {
2678       ::reportError(context, content->GetOriginalExpression(),
2679                     "TARGET_PDB_FILE not allowed for IMPORTED targets.");
2680       return std::string();
2681     }
2682
2683     std::string language = target->GetLinkerLanguage(context->Config);
2684
2685     std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB";
2686
2687     if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) {
2688       ::reportError(context, content->GetOriginalExpression(),
2689                     "TARGET_PDB_FILE is not supported by the target linker.");
2690       return std::string();
2691     }
2692
2693     cmStateEnums::TargetType targetType = target->GetType();
2694
2695     if (targetType != cmStateEnums::SHARED_LIBRARY &&
2696         targetType != cmStateEnums::MODULE_LIBRARY &&
2697         targetType != cmStateEnums::EXECUTABLE) {
2698       ::reportError(context, content->GetOriginalExpression(),
2699                     "TARGET_PDB_FILE is allowed only for "
2700                     "targets with linker created artifacts.");
2701       return std::string();
2702     }
2703
2704     std::string result = cmStrCat(target->GetPDBDirectory(context->Config),
2705                                   '/', target->GetPDBName(context->Config));
2706     return result;
2707   }
2708 };
2709
2710 template <>
2711 struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag>
2712 {
2713   static std::string Create(cmGeneratorTarget* target,
2714                             cmGeneratorExpressionContext* context,
2715                             const GeneratorExpressionContent* content)
2716   {
2717     // The file used to link to the target (.so, .lib, .a).
2718     if (!target->IsLinkable()) {
2719       ::reportError(context, content->GetOriginalExpression(),
2720                     "TARGET_LINKER_FILE is allowed only for libraries and "
2721                     "executables with ENABLE_EXPORTS.");
2722       return std::string();
2723     }
2724     cmStateEnums::ArtifactType artifact =
2725       target->HasImportLibrary(context->Config)
2726       ? cmStateEnums::ImportLibraryArtifact
2727       : cmStateEnums::RuntimeBinaryArtifact;
2728     return target->GetFullPath(context->Config, artifact);
2729   }
2730 };
2731
2732 template <>
2733 struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag>
2734 {
2735   static std::string Create(cmGeneratorTarget* target,
2736                             cmGeneratorExpressionContext* context,
2737                             const GeneratorExpressionContent* content)
2738   {
2739     if (target->IsImported()) {
2740       ::reportError(context, content->GetOriginalExpression(),
2741                     "TARGET_BUNDLE_DIR not allowed for IMPORTED targets.");
2742       return std::string();
2743     }
2744     if (!target->IsBundleOnApple()) {
2745       ::reportError(context, content->GetOriginalExpression(),
2746                     "TARGET_BUNDLE_DIR is allowed only for Bundle targets.");
2747       return std::string();
2748     }
2749
2750     std::string outpath = target->GetDirectory(context->Config) + '/';
2751     return target->BuildBundleDirectory(outpath, context->Config,
2752                                         cmGeneratorTarget::BundleDirLevel);
2753   }
2754 };
2755
2756 template <>
2757 struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirNameTag>
2758 {
2759   static std::string Create(cmGeneratorTarget* target,
2760                             cmGeneratorExpressionContext* context,
2761                             const GeneratorExpressionContent* content)
2762   {
2763     if (target->IsImported()) {
2764       ::reportError(
2765         context, content->GetOriginalExpression(),
2766         "TARGET_BUNDLE_DIR_NAME not allowed for IMPORTED targets.");
2767       return std::string();
2768     }
2769     if (!target->IsBundleOnApple()) {
2770       ::reportError(
2771         context, content->GetOriginalExpression(),
2772         "TARGET_BUNDLE_DIR_NAME is allowed only for Bundle targets.");
2773       return std::string();
2774     }
2775
2776     auto level = cmGeneratorTarget::BundleDirLevel;
2777     auto config = context->Config;
2778     if (target->IsAppBundleOnApple()) {
2779       return target->GetAppBundleDirectory(config, level);
2780     }
2781     if (target->IsFrameworkOnApple()) {
2782       return target->GetFrameworkDirectory(config, level);
2783     }
2784     if (target->IsCFBundleOnApple()) {
2785       return target->GetCFBundleDirectory(config, level);
2786     }
2787     return std::string();
2788   }
2789 };
2790
2791 template <>
2792 struct TargetFilesystemArtifactResultCreator<ArtifactBundleContentDirTag>
2793 {
2794   static std::string Create(cmGeneratorTarget* target,
2795                             cmGeneratorExpressionContext* context,
2796                             const GeneratorExpressionContent* content)
2797   {
2798     if (target->IsImported()) {
2799       ::reportError(
2800         context, content->GetOriginalExpression(),
2801         "TARGET_BUNDLE_CONTENT_DIR not allowed for IMPORTED targets.");
2802       return std::string();
2803     }
2804     if (!target->IsBundleOnApple()) {
2805       ::reportError(
2806         context, content->GetOriginalExpression(),
2807         "TARGET_BUNDLE_CONTENT_DIR is allowed only for Bundle targets.");
2808       return std::string();
2809     }
2810
2811     std::string outpath = target->GetDirectory(context->Config) + '/';
2812     return target->BuildBundleDirectory(outpath, context->Config,
2813                                         cmGeneratorTarget::ContentLevel);
2814   }
2815 };
2816
2817 template <>
2818 struct TargetFilesystemArtifactResultCreator<ArtifactNameTag>
2819 {
2820   static std::string Create(cmGeneratorTarget* target,
2821                             cmGeneratorExpressionContext* context,
2822                             const GeneratorExpressionContent* /*unused*/)
2823   {
2824     return target->GetFullPath(context->Config,
2825                                cmStateEnums::RuntimeBinaryArtifact, true);
2826   }
2827 };
2828
2829 template <typename ArtifactT>
2830 struct TargetFilesystemArtifactResultGetter
2831 {
2832   static std::string Get(const std::string& result);
2833 };
2834
2835 template <>
2836 struct TargetFilesystemArtifactResultGetter<ArtifactNameTag>
2837 {
2838   static std::string Get(const std::string& result)
2839   {
2840     return cmSystemTools::GetFilenameName(result);
2841   }
2842 };
2843
2844 template <>
2845 struct TargetFilesystemArtifactResultGetter<ArtifactDirTag>
2846 {
2847   static std::string Get(const std::string& result)
2848   {
2849     return cmSystemTools::GetFilenamePath(result);
2850   }
2851 };
2852
2853 template <>
2854 struct TargetFilesystemArtifactResultGetter<ArtifactPathTag>
2855 {
2856   static std::string Get(const std::string& result) { return result; }
2857 };
2858
2859 struct TargetArtifactBase : public cmGeneratorExpressionNode
2860 {
2861   TargetArtifactBase() {} // NOLINT(modernize-use-equals-default)
2862
2863 protected:
2864   cmGeneratorTarget* GetTarget(
2865     const std::vector<std::string>& parameters,
2866     cmGeneratorExpressionContext* context,
2867     const GeneratorExpressionContent* content,
2868     cmGeneratorExpressionDAGChecker* dagChecker) const
2869   {
2870     // Lookup the referenced target.
2871     std::string const& name = parameters.front();
2872
2873     if (!cmGeneratorExpression::IsValidTargetName(name)) {
2874       ::reportError(context, content->GetOriginalExpression(),
2875                     "Expression syntax not recognized.");
2876       return nullptr;
2877     }
2878     cmGeneratorTarget* target = context->LG->FindGeneratorTargetToUse(name);
2879     if (!target) {
2880       ::reportError(context, content->GetOriginalExpression(),
2881                     "No target \"" + name + "\"");
2882       return nullptr;
2883     }
2884     if (target->GetType() >= cmStateEnums::OBJECT_LIBRARY &&
2885         target->GetType() != cmStateEnums::UNKNOWN_LIBRARY) {
2886       ::reportError(context, content->GetOriginalExpression(),
2887                     "Target \"" + name +
2888                       "\" is not an executable or library.");
2889       return nullptr;
2890     }
2891     if (dagChecker &&
2892         (dagChecker->EvaluatingLinkLibraries(target) ||
2893          (dagChecker->EvaluatingSources() &&
2894           target == dagChecker->TopTarget()))) {
2895       ::reportError(context, content->GetOriginalExpression(),
2896                     "Expressions which require the linker language may not "
2897                     "be used while evaluating link libraries");
2898       return nullptr;
2899     }
2900
2901     return target;
2902   }
2903 };
2904
2905 template <typename ArtifactT, typename ComponentT>
2906 struct TargetFilesystemArtifact : public TargetArtifactBase
2907 {
2908   TargetFilesystemArtifact() {} // NOLINT(modernize-use-equals-default)
2909
2910   int NumExpectedParameters() const override { return 1; }
2911
2912   std::string Evaluate(
2913     const std::vector<std::string>& parameters,
2914     cmGeneratorExpressionContext* context,
2915     const GeneratorExpressionContent* content,
2916     cmGeneratorExpressionDAGChecker* dagChecker) const override
2917   {
2918     cmGeneratorTarget* target =
2919       this->GetTarget(parameters, context, content, dagChecker);
2920     if (!target) {
2921       return std::string();
2922     }
2923     // Not a dependent target if we are querying for ArtifactDirTag,
2924     // ArtifactNameTag, ArtifactBundleDirTag, ArtifactBundleDirNameTag,
2925     // and ArtifactBundleContentDirTag
2926     TargetFilesystemArtifactDependency<ArtifactT, ComponentT>::AddDependency(
2927       target, context);
2928
2929     std::string result =
2930       TargetFilesystemArtifactResultCreator<ArtifactT>::Create(target, context,
2931                                                                content);
2932     if (context->HadError) {
2933       return std::string();
2934     }
2935     return TargetFilesystemArtifactResultGetter<ComponentT>::Get(result);
2936   }
2937 };
2938
2939 template <typename ArtifactT>
2940 struct TargetFilesystemArtifactNodeGroup
2941 {
2942   TargetFilesystemArtifactNodeGroup() // NOLINT(modernize-use-equals-default)
2943   {
2944   }
2945
2946   TargetFilesystemArtifact<ArtifactT, ArtifactPathTag> File;
2947   TargetFilesystemArtifact<ArtifactT, ArtifactNameTag> FileName;
2948   TargetFilesystemArtifact<ArtifactT, ArtifactDirTag> FileDir;
2949 };
2950
2951 static const TargetFilesystemArtifactNodeGroup<ArtifactNameTag>
2952   targetNodeGroup;
2953
2954 static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag>
2955   targetLinkerNodeGroup;
2956
2957 static const TargetFilesystemArtifactNodeGroup<ArtifactSonameTag>
2958   targetSoNameNodeGroup;
2959
2960 static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag>
2961   targetPdbNodeGroup;
2962
2963 static const TargetFilesystemArtifact<ArtifactBundleDirTag, ArtifactPathTag>
2964   targetBundleDirNode;
2965
2966 static const TargetFilesystemArtifact<ArtifactBundleDirNameTag,
2967                                       ArtifactNameTag>
2968   targetBundleDirNameNode;
2969
2970 static const TargetFilesystemArtifact<ArtifactBundleContentDirTag,
2971                                       ArtifactPathTag>
2972   targetBundleContentDirNode;
2973
2974 //
2975 // To retrieve base name for various artifacts
2976 //
2977 template <typename ArtifactT>
2978 struct TargetOutputNameArtifactResultGetter
2979 {
2980   static std::string Get(cmGeneratorTarget* target,
2981                          cmGeneratorExpressionContext* context,
2982                          const GeneratorExpressionContent* content);
2983 };
2984
2985 template <>
2986 struct TargetOutputNameArtifactResultGetter<ArtifactNameTag>
2987 {
2988   static std::string Get(cmGeneratorTarget* target,
2989                          cmGeneratorExpressionContext* context,
2990                          const GeneratorExpressionContent* /*unused*/)
2991   {
2992     return target->GetOutputName(context->Config,
2993                                  cmStateEnums::RuntimeBinaryArtifact) +
2994       target->GetFilePostfix(context->Config);
2995   }
2996 };
2997
2998 template <>
2999 struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
3000 {
3001   static std::string Get(cmGeneratorTarget* target,
3002                          cmGeneratorExpressionContext* context,
3003                          const GeneratorExpressionContent* content)
3004   {
3005     // The file used to link to the target (.so, .lib, .a).
3006     if (!target->IsLinkable()) {
3007       ::reportError(context, content->GetOriginalExpression(),
3008                     "TARGET_LINKER_FILE_BASE_NAME is allowed only for "
3009                     "libraries and executables with ENABLE_EXPORTS.");
3010       return std::string();
3011     }
3012     cmStateEnums::ArtifactType artifact =
3013       target->HasImportLibrary(context->Config)
3014       ? cmStateEnums::ImportLibraryArtifact
3015       : cmStateEnums::RuntimeBinaryArtifact;
3016     return target->GetOutputName(context->Config, artifact) +
3017       target->GetFilePostfix(context->Config);
3018   }
3019 };
3020
3021 template <>
3022 struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag>
3023 {
3024   static std::string Get(cmGeneratorTarget* target,
3025                          cmGeneratorExpressionContext* context,
3026                          const GeneratorExpressionContent* content)
3027   {
3028     if (target->IsImported()) {
3029       ::reportError(
3030         context, content->GetOriginalExpression(),
3031         "TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets.");
3032       return std::string();
3033     }
3034
3035     std::string language = target->GetLinkerLanguage(context->Config);
3036
3037     std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB";
3038
3039     if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) {
3040       ::reportError(
3041         context, content->GetOriginalExpression(),
3042         "TARGET_PDB_FILE_BASE_NAME is not supported by the target linker.");
3043       return std::string();
3044     }
3045
3046     cmStateEnums::TargetType targetType = target->GetType();
3047
3048     if (targetType != cmStateEnums::SHARED_LIBRARY &&
3049         targetType != cmStateEnums::MODULE_LIBRARY &&
3050         targetType != cmStateEnums::EXECUTABLE) {
3051       ::reportError(context, content->GetOriginalExpression(),
3052                     "TARGET_PDB_FILE_BASE_NAME is allowed only for "
3053                     "targets with linker created artifacts.");
3054       return std::string();
3055     }
3056
3057     return target->GetPDBOutputName(context->Config) +
3058       target->GetFilePostfix(context->Config);
3059   }
3060 };
3061
3062 template <typename ArtifactT>
3063 struct TargetFileBaseNameArtifact : public TargetArtifactBase
3064 {
3065   TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default)
3066
3067   int NumExpectedParameters() const override { return 1; }
3068
3069   std::string Evaluate(
3070     const std::vector<std::string>& parameters,
3071     cmGeneratorExpressionContext* context,
3072     const GeneratorExpressionContent* content,
3073     cmGeneratorExpressionDAGChecker* dagChecker) const override
3074   {
3075     cmGeneratorTarget* target =
3076       this->GetTarget(parameters, context, content, dagChecker);
3077     if (!target) {
3078       return std::string();
3079     }
3080
3081     std::string result = TargetOutputNameArtifactResultGetter<ArtifactT>::Get(
3082       target, context, content);
3083     if (context->HadError) {
3084       return std::string();
3085     }
3086     return result;
3087   }
3088 };
3089
3090 static const TargetFileBaseNameArtifact<ArtifactNameTag>
3091   targetFileBaseNameNode;
3092 static const TargetFileBaseNameArtifact<ArtifactLinkerTag>
3093   targetLinkerFileBaseNameNode;
3094 static const TargetFileBaseNameArtifact<ArtifactPdbTag>
3095   targetPdbFileBaseNameNode;
3096
3097 class ArtifactFilePrefixTag;
3098 class ArtifactLinkerFilePrefixTag;
3099 class ArtifactFileSuffixTag;
3100 class ArtifactLinkerFileSuffixTag;
3101
3102 template <typename ArtifactT>
3103 struct TargetFileArtifactResultGetter
3104 {
3105   static std::string Get(cmGeneratorTarget* target,
3106                          cmGeneratorExpressionContext* context,
3107                          const GeneratorExpressionContent* content);
3108 };
3109
3110 template <>
3111 struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag>
3112 {
3113   static std::string Get(cmGeneratorTarget* target,
3114                          cmGeneratorExpressionContext* context,
3115                          const GeneratorExpressionContent*)
3116   {
3117     return target->GetFilePrefix(context->Config);
3118   }
3119 };
3120 template <>
3121 struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
3122 {
3123   static std::string Get(cmGeneratorTarget* target,
3124                          cmGeneratorExpressionContext* context,
3125                          const GeneratorExpressionContent* content)
3126   {
3127     if (!target->IsLinkable()) {
3128       ::reportError(context, content->GetOriginalExpression(),
3129                     "TARGET_LINKER_PREFIX is allowed only for libraries and "
3130                     "executables with ENABLE_EXPORTS.");
3131       return std::string();
3132     }
3133
3134     cmStateEnums::ArtifactType artifact =
3135       target->HasImportLibrary(context->Config)
3136       ? cmStateEnums::ImportLibraryArtifact
3137       : cmStateEnums::RuntimeBinaryArtifact;
3138
3139     return target->GetFilePrefix(context->Config, artifact);
3140   }
3141 };
3142 template <>
3143 struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag>
3144 {
3145   static std::string Get(cmGeneratorTarget* target,
3146                          cmGeneratorExpressionContext* context,
3147                          const GeneratorExpressionContent*)
3148   {
3149     return target->GetFileSuffix(context->Config);
3150   }
3151 };
3152 template <>
3153 struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
3154 {
3155   static std::string Get(cmGeneratorTarget* target,
3156                          cmGeneratorExpressionContext* context,
3157                          const GeneratorExpressionContent* content)
3158   {
3159     if (!target->IsLinkable()) {
3160       ::reportError(context, content->GetOriginalExpression(),
3161                     "TARGET_LINKER_SUFFIX is allowed only for libraries and "
3162                     "executables with ENABLE_EXPORTS.");
3163       return std::string();
3164     }
3165
3166     cmStateEnums::ArtifactType artifact =
3167       target->HasImportLibrary(context->Config)
3168       ? cmStateEnums::ImportLibraryArtifact
3169       : cmStateEnums::RuntimeBinaryArtifact;
3170
3171     return target->GetFileSuffix(context->Config, artifact);
3172   }
3173 };
3174
3175 template <typename ArtifactT>
3176 struct TargetFileArtifact : public TargetArtifactBase
3177 {
3178   TargetFileArtifact() {} // NOLINT(modernize-use-equals-default)
3179
3180   int NumExpectedParameters() const override { return 1; }
3181
3182   std::string Evaluate(
3183     const std::vector<std::string>& parameters,
3184     cmGeneratorExpressionContext* context,
3185     const GeneratorExpressionContent* content,
3186     cmGeneratorExpressionDAGChecker* dagChecker) const override
3187   {
3188     cmGeneratorTarget* target =
3189       this->GetTarget(parameters, context, content, dagChecker);
3190     if (!target) {
3191       return std::string();
3192     }
3193
3194     std::string result =
3195       TargetFileArtifactResultGetter<ArtifactT>::Get(target, context, content);
3196     if (context->HadError) {
3197       return std::string();
3198     }
3199     return result;
3200   }
3201 };
3202
3203 static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode;
3204 static const TargetFileArtifact<ArtifactLinkerFilePrefixTag>
3205   targetLinkerFilePrefixNode;
3206 static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode;
3207 static const TargetFileArtifact<ArtifactLinkerFileSuffixTag>
3208   targetLinkerFileSuffixNode;
3209
3210 static const struct ShellPathNode : public cmGeneratorExpressionNode
3211 {
3212   ShellPathNode() {} // NOLINT(modernize-use-equals-default)
3213
3214   std::string Evaluate(
3215     const std::vector<std::string>& parameters,
3216     cmGeneratorExpressionContext* context,
3217     const GeneratorExpressionContent* content,
3218     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
3219   {
3220     std::vector<std::string> listIn = cmExpandedList(parameters.front());
3221     if (listIn.empty()) {
3222       reportError(context, content->GetOriginalExpression(),
3223                   "\"\" is not an absolute path.");
3224       return std::string();
3225     }
3226     cmStateSnapshot snapshot = context->LG->GetStateSnapshot();
3227     cmOutputConverter converter(snapshot);
3228     const char* separator = snapshot.GetState()->UseWindowsShell() ? ";" : ":";
3229     std::vector<std::string> listOut;
3230     listOut.reserve(listIn.size());
3231     for (auto const& in : listIn) {
3232       if (!cmSystemTools::FileIsFullPath(in)) {
3233         reportError(context, content->GetOriginalExpression(),
3234                     "\"" + in + "\" is not an absolute path.");
3235         return std::string();
3236       }
3237       listOut.emplace_back(converter.ConvertDirectorySeparatorsForShell(in));
3238     }
3239     return cmJoin(listOut, separator);
3240   }
3241 } shellPathNode;
3242
3243 const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
3244   const std::string& identifier)
3245 {
3246   static std::map<std::string, cmGeneratorExpressionNode const*> const nodeMap{
3247     { "0", &zeroNode },
3248     { "1", &oneNode },
3249     { "AND", &andNode },
3250     { "OR", &orNode },
3251     { "NOT", &notNode },
3252     { "C_COMPILER_ID", &cCompilerIdNode },
3253     { "CXX_COMPILER_ID", &cxxCompilerIdNode },
3254     { "OBJC_COMPILER_ID", &objcCompilerIdNode },
3255     { "OBJCXX_COMPILER_ID", &objcxxCompilerIdNode },
3256     { "CUDA_COMPILER_ID", &cudaCompilerIdNode },
3257     { "Fortran_COMPILER_ID", &fortranCompilerIdNode },
3258     { "HIP_COMPILER_ID", &hipCompilerIdNode },
3259     { "VERSION_GREATER", &versionGreaterNode },
3260     { "VERSION_GREATER_EQUAL", &versionGreaterEqNode },
3261     { "VERSION_LESS", &versionLessNode },
3262     { "VERSION_LESS_EQUAL", &versionLessEqNode },
3263     { "VERSION_EQUAL", &versionEqualNode },
3264     { "C_COMPILER_VERSION", &cCompilerVersionNode },
3265     { "CXX_COMPILER_VERSION", &cxxCompilerVersionNode },
3266     { "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode },
3267     { "OBJC_COMPILER_VERSION", &objcCompilerVersionNode },
3268     { "OBJCXX_COMPILER_VERSION", &objcxxCompilerVersionNode },
3269     { "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode },
3270     { "HIP_COMPILER_VERSION", &hipCompilerVersionNode },
3271     { "PLATFORM_ID", &platformIdNode },
3272     { "COMPILE_FEATURES", &compileFeaturesNode },
3273     { "CONFIGURATION", &configurationNode },
3274     { "CONFIG", &configurationTestNode },
3275     { "TARGET_FILE", &targetNodeGroup.File },
3276     { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File },
3277     { "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File },
3278     { "TARGET_PDB_FILE", &targetPdbNodeGroup.File },
3279     { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode },
3280     { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode },
3281     { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode },
3282     { "TARGET_FILE_PREFIX", &targetFilePrefixNode },
3283     { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode },
3284     { "TARGET_FILE_SUFFIX", &targetFileSuffixNode },
3285     { "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode },
3286     { "TARGET_FILE_NAME", &targetNodeGroup.FileName },
3287     { "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.FileName },
3288     { "TARGET_SONAME_FILE_NAME", &targetSoNameNodeGroup.FileName },
3289     { "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName },
3290     { "TARGET_FILE_DIR", &targetNodeGroup.FileDir },
3291     { "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.FileDir },
3292     { "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.FileDir },
3293     { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir },
3294     { "TARGET_BUNDLE_DIR", &targetBundleDirNode },
3295     { "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode },
3296     { "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode },
3297     { "STREQUAL", &strEqualNode },
3298     { "EQUAL", &equalNode },
3299     { "IN_LIST", &inListNode },
3300     { "FILTER", &filterNode },
3301     { "REMOVE_DUPLICATES", &removeDuplicatesNode },
3302     { "LOWER_CASE", &lowerCaseNode },
3303     { "UPPER_CASE", &upperCaseNode },
3304     { "PATH", &pathNode },
3305     { "PATH_EQUAL", &pathEqualNode },
3306     { "MAKE_C_IDENTIFIER", &makeCIdentifierNode },
3307     { "BOOL", &boolNode },
3308     { "IF", &ifNode },
3309     { "ANGLE-R", &angle_rNode },
3310     { "COMMA", &commaNode },
3311     { "SEMICOLON", &semicolonNode },
3312     { "TARGET_PROPERTY", &targetPropertyNode },
3313     { "TARGET_NAME", &targetNameNode },
3314     { "TARGET_OBJECTS", &targetObjectsNode },
3315     { "TARGET_POLICY", &targetPolicyNode },
3316     { "TARGET_EXISTS", &targetExistsNode },
3317     { "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode },
3318     { "TARGET_GENEX_EVAL", &targetGenexEvalNode },
3319     { "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode },
3320     { "GENEX_EVAL", &genexEvalNode },
3321     { "BUILD_INTERFACE", &buildInterfaceNode },
3322     { "INSTALL_INTERFACE", &installInterfaceNode },
3323     { "INSTALL_PREFIX", &installPrefixNode },
3324     { "JOIN", &joinNode },
3325     { "LINK_ONLY", &linkOnlyNode },
3326     { "COMPILE_LANG_AND_ID", &languageAndIdNode },
3327     { "COMPILE_LANGUAGE", &languageNode },
3328     { "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
3329     { "LINK_LANGUAGE", &linkLanguageNode },
3330     { "LINK_LIBRARY", &linkLibraryNode },
3331     { "LINK_GROUP", &linkGroupNode },
3332     { "HOST_LINK", &hostLinkNode },
3333     { "DEVICE_LINK", &deviceLinkNode },
3334     { "SHELL_PATH", &shellPathNode }
3335   };
3336
3337   {
3338     auto itr = nodeMap.find(identifier);
3339     if (itr != nodeMap.end()) {
3340       return itr->second;
3341     }
3342   }
3343   return nullptr;
3344 }
3345
3346 void reportError(cmGeneratorExpressionContext* context,
3347                  const std::string& expr, const std::string& result)
3348 {
3349   context->HadError = true;
3350   if (context->Quiet) {
3351     return;
3352   }
3353
3354   std::ostringstream e;
3355   /* clang-format off */
3356   e << "Error evaluating generator expression:\n"
3357     << "  " << expr << "\n"
3358     << result;
3359   /* clang-format on */
3360   context->LG->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
3361                                                 e.str(), context->Backtrace);
3362 }