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 <map>
11 #include <memory>
12 #include <set>
13 #include <sstream>
14 #include <utility>
15 
16 #include <cm/iterator>
17 #include <cm/optional>
18 #include <cm/string_view>
19 #include <cm/vector>
20 #include <cmext/algorithm>
21 #include <cmext/string_view>
22 
23 #include "cmsys/RegularExpression.hxx"
24 #include "cmsys/String.h"
25 
26 #include "cmAlgorithms.h"
27 #include "cmComputeLinkInformation.h"
28 #include "cmGeneratorExpression.h"
29 #include "cmGeneratorExpressionContext.h"
30 #include "cmGeneratorExpressionDAGChecker.h"
31 #include "cmGeneratorExpressionEvaluator.h"
32 #include "cmGeneratorTarget.h"
33 #include "cmGlobalGenerator.h"
34 #include "cmLinkItem.h"
35 #include "cmLocalGenerator.h"
36 #include "cmMakefile.h"
37 #include "cmMessageType.h"
38 #include "cmOutputConverter.h"
39 #include "cmPolicies.h"
40 #include "cmRange.h"
41 #include "cmStandardLevelResolver.h"
42 #include "cmState.h"
43 #include "cmStateSnapshot.h"
44 #include "cmStateTypes.h"
45 #include "cmStringAlgorithms.h"
46 #include "cmSystemTools.h"
47 #include "cmTarget.h"
48 #include "cmValue.h"
49 #include "cmake.h"
50 
EvaluateDependentExpression(std::string const & prop,cmLocalGenerator * lg,cmGeneratorExpressionContext * context,cmGeneratorTarget const * headTarget,cmGeneratorExpressionDAGChecker * dagChecker,cmGeneratorTarget const * currentTarget)51 std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
52   std::string const& prop, cmLocalGenerator* lg,
53   cmGeneratorExpressionContext* context, cmGeneratorTarget const* headTarget,
54   cmGeneratorExpressionDAGChecker* dagChecker,
55   cmGeneratorTarget const* currentTarget)
56 {
57   cmGeneratorExpression ge(context->Backtrace);
58   std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop);
59   cge->SetEvaluateForBuildsystem(context->EvaluateForBuildsystem);
60   cge->SetQuiet(context->Quiet);
61   std::string result =
62     cge->Evaluate(lg, context->Config, headTarget, dagChecker, currentTarget,
63                   context->Language);
64   if (cge->GetHadContextSensitiveCondition()) {
65     context->HadContextSensitiveCondition = true;
66   }
67   if (cge->GetHadHeadSensitiveCondition()) {
68     context->HadHeadSensitiveCondition = true;
69   }
70   if (cge->GetHadLinkLanguageSensitiveCondition()) {
71     context->HadLinkLanguageSensitiveCondition = true;
72   }
73   return result;
74 }
75 
76 static const struct ZeroNode : public cmGeneratorExpressionNode
77 {
ZeroNodeZeroNode78   ZeroNode() {} // NOLINT(modernize-use-equals-default)
79 
GeneratesContentZeroNode80   bool GeneratesContent() const override { return false; }
81 
AcceptsArbitraryContentParameterZeroNode82   bool AcceptsArbitraryContentParameter() const override { return true; }
83 
EvaluateZeroNode84   std::string Evaluate(
85     const std::vector<std::string>& /*parameters*/,
86     cmGeneratorExpressionContext* /*context*/,
87     const GeneratorExpressionContent* /*content*/,
88     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
89   {
90     return std::string();
91   }
92 } zeroNode;
93 
94 static const struct OneNode : public cmGeneratorExpressionNode
95 {
OneNodeOneNode96   OneNode() {} // NOLINT(modernize-use-equals-default)
97 
AcceptsArbitraryContentParameterOneNode98   bool AcceptsArbitraryContentParameter() const override { return true; }
99 
EvaluateOneNode100   std::string Evaluate(
101     const std::vector<std::string>& parameters,
102     cmGeneratorExpressionContext* /*context*/,
103     const GeneratorExpressionContent* /*content*/,
104     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
105   {
106     return parameters.front();
107   }
108 } oneNode;
109 
110 static const struct OneNode buildInterfaceNode;
111 
112 static const struct ZeroNode installInterfaceNode;
113 
114 struct BooleanOpNode : public cmGeneratorExpressionNode
115 {
BooleanOpNodeBooleanOpNode116   BooleanOpNode(const char* op_, const char* successVal_,
117                 const char* failureVal_)
118     : op(op_)
119     , successVal(successVal_)
120     , failureVal(failureVal_)
121   {
122   }
123 
NumExpectedParametersBooleanOpNode124   int NumExpectedParameters() const override { return OneOrMoreParameters; }
125 
EvaluateBooleanOpNode126   std::string Evaluate(const std::vector<std::string>& parameters,
127                        cmGeneratorExpressionContext* context,
128                        const GeneratorExpressionContent* content,
129                        cmGeneratorExpressionDAGChecker*) const override
130   {
131     for (std::string const& param : parameters) {
132       if (param == this->failureVal) {
133         return this->failureVal;
134       }
135       if (param != this->successVal) {
136         std::ostringstream e;
137         e << "Parameters to $<" << this->op;
138         e << "> must resolve to either '0' or '1'.";
139         reportError(context, content->GetOriginalExpression(), e.str());
140         return std::string();
141       }
142     }
143     return this->successVal;
144   }
145 
146   const char *const op, *const successVal, *const failureVal;
147 };
148 
149 static const BooleanOpNode andNode("AND", "1", "0"), orNode("OR", "0", "1");
150 
151 static const struct NotNode : public cmGeneratorExpressionNode
152 {
NotNodeNotNode153   NotNode() {} // NOLINT(modernize-use-equals-default)
154 
EvaluateNotNode155   std::string Evaluate(
156     const std::vector<std::string>& parameters,
157     cmGeneratorExpressionContext* context,
158     const GeneratorExpressionContent* content,
159     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
160   {
161     if (parameters.front() != "0" && parameters.front() != "1") {
162       reportError(
163         context, content->GetOriginalExpression(),
164         "$<NOT> parameter must resolve to exactly one '0' or '1' value.");
165       return std::string();
166     }
167     return parameters.front() == "0" ? "1" : "0";
168   }
169 } notNode;
170 
171 static const struct BoolNode : public cmGeneratorExpressionNode
172 {
BoolNodeBoolNode173   BoolNode() {} // NOLINT(modernize-use-equals-default)
174 
NumExpectedParametersBoolNode175   int NumExpectedParameters() const override { return 1; }
176 
EvaluateBoolNode177   std::string Evaluate(
178     const std::vector<std::string>& parameters,
179     cmGeneratorExpressionContext* /*context*/,
180     const GeneratorExpressionContent* /*content*/,
181     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
182   {
183     return !cmIsOff(parameters.front()) ? "1" : "0";
184   }
185 } boolNode;
186 
187 static const struct IfNode : public cmGeneratorExpressionNode
188 {
IfNodeIfNode189   IfNode() {} // NOLINT(modernize-use-equals-default)
190 
NumExpectedParametersIfNode191   int NumExpectedParameters() const override { return 3; }
192 
EvaluateIfNode193   std::string Evaluate(const std::vector<std::string>& parameters,
194                        cmGeneratorExpressionContext* context,
195                        const GeneratorExpressionContent* content,
196                        cmGeneratorExpressionDAGChecker*) const override
197   {
198     if (parameters[0] != "1" && parameters[0] != "0") {
199       reportError(context, content->GetOriginalExpression(),
200                   "First parameter to $<IF> must resolve to exactly one '0' "
201                   "or '1' value.");
202       return std::string();
203     }
204     return parameters[0] == "1" ? parameters[1] : parameters[2];
205   }
206 } ifNode;
207 
208 static const struct StrEqualNode : public cmGeneratorExpressionNode
209 {
StrEqualNodeStrEqualNode210   StrEqualNode() {} // NOLINT(modernize-use-equals-default)
211 
NumExpectedParametersStrEqualNode212   int NumExpectedParameters() const override { return 2; }
213 
EvaluateStrEqualNode214   std::string Evaluate(
215     const std::vector<std::string>& parameters,
216     cmGeneratorExpressionContext* /*context*/,
217     const GeneratorExpressionContent* /*content*/,
218     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
219   {
220     return parameters.front() == parameters[1] ? "1" : "0";
221   }
222 } strEqualNode;
223 
224 static const struct EqualNode : public cmGeneratorExpressionNode
225 {
EqualNodeEqualNode226   EqualNode() {} // NOLINT(modernize-use-equals-default)
227 
NumExpectedParametersEqualNode228   int NumExpectedParameters() const override { return 2; }
229 
EvaluateEqualNode230   std::string Evaluate(
231     const std::vector<std::string>& parameters,
232     cmGeneratorExpressionContext* context,
233     const GeneratorExpressionContent* content,
234     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
235   {
236     long numbers[2];
237     for (int i = 0; i < 2; ++i) {
238       if (!ParameterToLong(parameters[i].c_str(), &numbers[i])) {
239         reportError(context, content->GetOriginalExpression(),
240                     "$<EQUAL> parameter " + parameters[i] +
241                       " is not a valid integer.");
242         return {};
243       }
244     }
245     return numbers[0] == numbers[1] ? "1" : "0";
246   }
247 
ParameterToLongEqualNode248   static bool ParameterToLong(const char* param, long* outResult)
249   {
250     const char isNegative = param[0] == '-';
251 
252     int base = 0;
253     if (cmHasLiteralPrefix(param, "0b") || cmHasLiteralPrefix(param, "0B")) {
254       base = 2;
255       param += 2;
256     } else if (cmHasLiteralPrefix(param, "-0b") ||
257                cmHasLiteralPrefix(param, "-0B") ||
258                cmHasLiteralPrefix(param, "+0b") ||
259                cmHasLiteralPrefix(param, "+0B")) {
260       base = 2;
261       param += 3;
262     }
263 
264     char* pEnd;
265     long result = strtol(param, &pEnd, base);
266     if (pEnd == param || *pEnd != '\0' || errno == ERANGE) {
267       return false;
268     }
269     if (isNegative && result > 0) {
270       result *= -1;
271     }
272     *outResult = result;
273     return true;
274   }
275 } equalNode;
276 
277 static const struct InListNode : public cmGeneratorExpressionNode
278 {
InListNodeInListNode279   InListNode() {} // NOLINT(modernize-use-equals-default)
280 
NumExpectedParametersInListNode281   int NumExpectedParameters() const override { return 2; }
282 
EvaluateInListNode283   std::string Evaluate(
284     const std::vector<std::string>& parameters,
285     cmGeneratorExpressionContext* context,
286     const GeneratorExpressionContent* /*content*/,
287     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
288   {
289     std::vector<std::string> values;
290     std::vector<std::string> checkValues;
291     bool check = false;
292     switch (context->LG->GetPolicyStatus(cmPolicies::CMP0085)) {
293       case cmPolicies::WARN:
294         if (parameters.front().empty()) {
295           check = true;
296           cmExpandList(parameters[1], checkValues, true);
297         }
298         CM_FALLTHROUGH;
299       case cmPolicies::OLD:
300         cmExpandList(parameters[1], values);
301         if (check && values != checkValues) {
302           std::ostringstream e;
303           e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085)
304             << "\nSearch Item:\n  \"" << parameters.front()
305             << "\"\nList:\n  \"" << parameters[1] << "\"\n";
306           context->LG->GetCMakeInstance()->IssueMessage(
307             MessageType ::AUTHOR_WARNING, e.str(), context->Backtrace);
308           return "0";
309         }
310         if (values.empty()) {
311           return "0";
312         }
313         break;
314       case cmPolicies::REQUIRED_IF_USED:
315       case cmPolicies::REQUIRED_ALWAYS:
316       case cmPolicies::NEW:
317         cmExpandList(parameters[1], values, true);
318         break;
319     }
320 
321     return cm::contains(values, parameters.front()) ? "1" : "0";
322   }
323 } inListNode;
324 
325 static const struct FilterNode : public cmGeneratorExpressionNode
326 {
FilterNodeFilterNode327   FilterNode() {} // NOLINT(modernize-use-equals-default)
328 
NumExpectedParametersFilterNode329   int NumExpectedParameters() const override { return 3; }
330 
EvaluateFilterNode331   std::string Evaluate(
332     const std::vector<std::string>& parameters,
333     cmGeneratorExpressionContext* context,
334     const GeneratorExpressionContent* content,
335     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
336   {
337     if (parameters.size() != 3) {
338       reportError(context, content->GetOriginalExpression(),
339                   "$<FILTER:...> expression requires three parameters");
340       return {};
341     }
342 
343     if (parameters[1] != "INCLUDE" && parameters[1] != "EXCLUDE") {
344       reportError(
345         context, content->GetOriginalExpression(),
346         "$<FILTER:...> second parameter must be either INCLUDE or EXCLUDE");
347       return {};
348     }
349 
350     const bool exclude = parameters[1] == "EXCLUDE";
351 
352     cmsys::RegularExpression re;
353     if (!re.compile(parameters[2])) {
354       reportError(context, content->GetOriginalExpression(),
355                   "$<FILTER:...> failed to compile regex");
356       return {};
357     }
358 
359     std::vector<std::string> values;
360     std::vector<std::string> result;
361     cmExpandList(parameters.front(), values, true);
362 
363     std::copy_if(values.cbegin(), values.cend(), std::back_inserter(result),
364                  [&re, exclude](std::string const& input) {
365                    return exclude ^ re.find(input);
366                  });
367     return cmJoin(cmMakeRange(result.cbegin(), result.cend()), ";");
368   }
369 } filterNode;
370 
371 static const struct RemoveDuplicatesNode : public cmGeneratorExpressionNode
372 {
RemoveDuplicatesNodeRemoveDuplicatesNode373   RemoveDuplicatesNode() {} // NOLINT(modernize-use-equals-default)
374 
NumExpectedParametersRemoveDuplicatesNode375   int NumExpectedParameters() const override { return 1; }
376 
EvaluateRemoveDuplicatesNode377   std::string Evaluate(
378     const std::vector<std::string>& parameters,
379     cmGeneratorExpressionContext* context,
380     const GeneratorExpressionContent* content,
381     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
382   {
383     if (parameters.size() != 1) {
384       reportError(
385         context, content->GetOriginalExpression(),
386         "$<REMOVE_DUPLICATES:...> expression requires one parameter");
387     }
388 
389     std::vector<std::string> values = cmExpandedList(parameters.front(), true);
390 
391     auto valuesEnd = cmRemoveDuplicates(values);
392     auto valuesBegin = values.cbegin();
393     return cmJoin(cmMakeRange(valuesBegin, valuesEnd), ";");
394   }
395 
396 } removeDuplicatesNode;
397 
398 static const struct TargetExistsNode : public cmGeneratorExpressionNode
399 {
TargetExistsNodeTargetExistsNode400   TargetExistsNode() {} // NOLINT(modernize-use-equals-default)
401 
NumExpectedParametersTargetExistsNode402   int NumExpectedParameters() const override { return 1; }
403 
EvaluateTargetExistsNode404   std::string Evaluate(
405     const std::vector<std::string>& parameters,
406     cmGeneratorExpressionContext* context,
407     const GeneratorExpressionContent* content,
408     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
409   {
410     if (parameters.size() != 1) {
411       reportError(context, content->GetOriginalExpression(),
412                   "$<TARGET_EXISTS:...> expression requires one parameter");
413       return std::string();
414     }
415 
416     std::string targetName = parameters.front();
417     if (targetName.empty() ||
418         !cmGeneratorExpression::IsValidTargetName(targetName)) {
419       reportError(context, content->GetOriginalExpression(),
420                   "$<TARGET_EXISTS:tgt> expression requires a non-empty "
421                   "valid target name.");
422       return std::string();
423     }
424 
425     return context->LG->GetMakefile()->FindTargetToUse(targetName) ? "1" : "0";
426   }
427 } targetExistsNode;
428 
429 static const struct TargetNameIfExistsNode : public cmGeneratorExpressionNode
430 {
TargetNameIfExistsNodeTargetNameIfExistsNode431   TargetNameIfExistsNode() {} // NOLINT(modernize-use-equals-default)
432 
NumExpectedParametersTargetNameIfExistsNode433   int NumExpectedParameters() const override { return 1; }
434 
EvaluateTargetNameIfExistsNode435   std::string Evaluate(
436     const std::vector<std::string>& parameters,
437     cmGeneratorExpressionContext* context,
438     const GeneratorExpressionContent* content,
439     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
440   {
441     if (parameters.size() != 1) {
442       reportError(context, content->GetOriginalExpression(),
443                   "$<TARGET_NAME_IF_EXISTS:...> expression requires one "
444                   "parameter");
445       return std::string();
446     }
447 
448     std::string targetName = parameters.front();
449     if (targetName.empty() ||
450         !cmGeneratorExpression::IsValidTargetName(targetName)) {
451       reportError(context, content->GetOriginalExpression(),
452                   "$<TARGET_NAME_IF_EXISTS:tgt> expression requires a "
453                   "non-empty valid target name.");
454       return std::string();
455     }
456 
457     return context->LG->GetMakefile()->FindTargetToUse(targetName)
458       ? targetName
459       : std::string();
460   }
461 } targetNameIfExistsNode;
462 
463 struct GenexEvaluator : public cmGeneratorExpressionNode
464 {
GenexEvaluatorGenexEvaluator465   GenexEvaluator() {} // NOLINT(modernize-use-equals-default)
466 
467 protected:
EvaluateExpressionGenexEvaluator468   std::string EvaluateExpression(
469     const std::string& genexOperator, const std::string& expression,
470     cmGeneratorExpressionContext* context,
471     const GeneratorExpressionContent* content,
472     cmGeneratorExpressionDAGChecker* dagCheckerParent) const
473   {
474     if (context->HeadTarget) {
475       cmGeneratorExpressionDAGChecker dagChecker(
476         context->Backtrace, context->HeadTarget,
477         genexOperator + ":" + expression, content, dagCheckerParent);
478       switch (dagChecker.Check()) {
479         case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
480         case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: {
481           dagChecker.ReportError(context, content->GetOriginalExpression());
482           return std::string();
483         }
484         case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
485         case cmGeneratorExpressionDAGChecker::DAG:
486           break;
487       }
488 
489       return this->EvaluateDependentExpression(
490         expression, context->LG, context, context->HeadTarget, &dagChecker,
491         context->CurrentTarget);
492     }
493 
494     return this->EvaluateDependentExpression(
495       expression, context->LG, context, context->HeadTarget, dagCheckerParent,
496       context->CurrentTarget);
497   }
498 };
499 
500 static const struct TargetGenexEvalNode : public GenexEvaluator
501 {
TargetGenexEvalNodeTargetGenexEvalNode502   TargetGenexEvalNode() {} // NOLINT(modernize-use-equals-default)
503 
NumExpectedParametersTargetGenexEvalNode504   int NumExpectedParameters() const override { return 2; }
505 
AcceptsArbitraryContentParameterTargetGenexEvalNode506   bool AcceptsArbitraryContentParameter() const override { return true; }
507 
EvaluateTargetGenexEvalNode508   std::string Evaluate(
509     const std::vector<std::string>& parameters,
510     cmGeneratorExpressionContext* context,
511     const GeneratorExpressionContent* content,
512     cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
513   {
514     const std::string& targetName = parameters.front();
515     if (targetName.empty() ||
516         !cmGeneratorExpression::IsValidTargetName(targetName)) {
517       reportError(context, content->GetOriginalExpression(),
518                   "$<TARGET_GENEX_EVAL:tgt, ...> expression requires a "
519                   "non-empty valid target name.");
520       return std::string();
521     }
522 
523     const auto* target = context->LG->FindGeneratorTargetToUse(targetName);
524     if (!target) {
525       std::ostringstream e;
526       e << "$<TARGET_GENEX_EVAL:tgt, ...> target \"" << targetName
527         << "\" not found.";
528       reportError(context, content->GetOriginalExpression(), e.str());
529       return std::string();
530     }
531 
532     const std::string& expression = parameters[1];
533     if (expression.empty()) {
534       return expression;
535     }
536 
537     cmGeneratorExpressionContext targetContext(
538       context->LG, context->Config, context->Quiet, target, target,
539       context->EvaluateForBuildsystem, context->Backtrace, context->Language);
540 
541     return this->EvaluateExpression("TARGET_GENEX_EVAL", expression,
542                                     &targetContext, content, dagCheckerParent);
543   }
544 } targetGenexEvalNode;
545 
546 static const struct GenexEvalNode : public GenexEvaluator
547 {
GenexEvalNodeGenexEvalNode548   GenexEvalNode() {} // NOLINT(modernize-use-equals-default)
549 
NumExpectedParametersGenexEvalNode550   int NumExpectedParameters() const override { return 1; }
551 
AcceptsArbitraryContentParameterGenexEvalNode552   bool AcceptsArbitraryContentParameter() const override { return true; }
553 
EvaluateGenexEvalNode554   std::string Evaluate(
555     const std::vector<std::string>& parameters,
556     cmGeneratorExpressionContext* context,
557     const GeneratorExpressionContent* content,
558     cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
559   {
560     const std::string& expression = parameters[0];
561     if (expression.empty()) {
562       return expression;
563     }
564 
565     return this->EvaluateExpression("GENEX_EVAL", expression, context, content,
566                                     dagCheckerParent);
567   }
568 } genexEvalNode;
569 
570 static const struct LowerCaseNode : public cmGeneratorExpressionNode
571 {
LowerCaseNodeLowerCaseNode572   LowerCaseNode() {} // NOLINT(modernize-use-equals-default)
573 
AcceptsArbitraryContentParameterLowerCaseNode574   bool AcceptsArbitraryContentParameter() const override { return true; }
575 
EvaluateLowerCaseNode576   std::string Evaluate(
577     const std::vector<std::string>& parameters,
578     cmGeneratorExpressionContext* /*context*/,
579     const GeneratorExpressionContent* /*content*/,
580     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
581   {
582     return cmSystemTools::LowerCase(parameters.front());
583   }
584 } lowerCaseNode;
585 
586 static const struct UpperCaseNode : public cmGeneratorExpressionNode
587 {
UpperCaseNodeUpperCaseNode588   UpperCaseNode() {} // NOLINT(modernize-use-equals-default)
589 
AcceptsArbitraryContentParameterUpperCaseNode590   bool AcceptsArbitraryContentParameter() const override { return true; }
591 
EvaluateUpperCaseNode592   std::string Evaluate(
593     const std::vector<std::string>& parameters,
594     cmGeneratorExpressionContext* /*context*/,
595     const GeneratorExpressionContent* /*content*/,
596     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
597   {
598     return cmSystemTools::UpperCase(parameters.front());
599   }
600 } upperCaseNode;
601 
602 static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode
603 {
MakeCIdentifierNodeMakeCIdentifierNode604   MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default)
605 
AcceptsArbitraryContentParameterMakeCIdentifierNode606   bool AcceptsArbitraryContentParameter() const override { return true; }
607 
EvaluateMakeCIdentifierNode608   std::string Evaluate(
609     const std::vector<std::string>& parameters,
610     cmGeneratorExpressionContext* /*context*/,
611     const GeneratorExpressionContent* /*content*/,
612     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
613   {
614     return cmSystemTools::MakeCidentifier(parameters.front());
615   }
616 } makeCIdentifierNode;
617 
618 template <char C>
619 struct CharacterNode : public cmGeneratorExpressionNode
620 {
CharacterNodeCharacterNode621   CharacterNode() {} // NOLINT(modernize-use-equals-default)
622 
NumExpectedParametersCharacterNode623   int NumExpectedParameters() const override { return 0; }
624 
EvaluateCharacterNode625   std::string Evaluate(
626     const std::vector<std::string>& /*parameters*/,
627     cmGeneratorExpressionContext* /*context*/,
628     const GeneratorExpressionContent* /*content*/,
629     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
630   {
631     return { C };
632   }
633 };
634 static const CharacterNode<'>'> angle_rNode;
635 static const CharacterNode<','> commaNode;
636 static const CharacterNode<';'> semicolonNode;
637 
638 struct CompilerIdNode : public cmGeneratorExpressionNode
639 {
CompilerIdNodeCompilerIdNode640   CompilerIdNode(const char* compilerLang)
641     : CompilerLanguage(compilerLang)
642   {
643   }
644 
NumExpectedParametersCompilerIdNode645   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
646 
EvaluateCompilerIdNode647   std::string Evaluate(
648     const std::vector<std::string>& parameters,
649     cmGeneratorExpressionContext* context,
650     const GeneratorExpressionContent* content,
651     cmGeneratorExpressionDAGChecker* dagChecker) const override
652   {
653     if (!context->HeadTarget) {
654       std::ostringstream e;
655       e << "$<" << this->CompilerLanguage
656         << "_COMPILER_ID> may only be used with binary targets.  It may "
657            "not be used with add_custom_command or add_custom_target.";
658       reportError(context, content->GetOriginalExpression(), e.str());
659       return {};
660     }
661     return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
662                                       this->CompilerLanguage);
663   }
664 
EvaluateWithLanguageCompilerIdNode665   std::string EvaluateWithLanguage(const std::vector<std::string>& parameters,
666                                    cmGeneratorExpressionContext* context,
667                                    const GeneratorExpressionContent* content,
668                                    cmGeneratorExpressionDAGChecker* /*unused*/,
669                                    const std::string& lang) const
670   {
671     std::string const& compilerId =
672       context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
673                                                     "_COMPILER_ID");
674     if (parameters.empty()) {
675       return compilerId;
676     }
677     if (compilerId.empty()) {
678       return parameters.front().empty() ? "1" : "0";
679     }
680     static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$");
681 
682     for (auto const& param : parameters) {
683 
684       if (!compilerIdValidator.find(param)) {
685         reportError(context, content->GetOriginalExpression(),
686                     "Expression syntax not recognized.");
687         return std::string();
688       }
689 
690       if (strcmp(param.c_str(), compilerId.c_str()) == 0) {
691         return "1";
692       }
693 
694       if (cmsysString_strcasecmp(param.c_str(), compilerId.c_str()) == 0) {
695         switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) {
696           case cmPolicies::WARN: {
697             context->LG->GetCMakeInstance()->IssueMessage(
698               MessageType::AUTHOR_WARNING,
699               cmPolicies::GetPolicyWarning(cmPolicies::CMP0044),
700               context->Backtrace);
701             CM_FALLTHROUGH;
702           }
703           case cmPolicies::OLD:
704             return "1";
705           case cmPolicies::NEW:
706           case cmPolicies::REQUIRED_ALWAYS:
707           case cmPolicies::REQUIRED_IF_USED:
708             break;
709         }
710       }
711     }
712     return "0";
713   }
714 
715   const char* const CompilerLanguage;
716 };
717 
718 static const CompilerIdNode cCompilerIdNode("C"), cxxCompilerIdNode("CXX"),
719   cudaCompilerIdNode("CUDA"), objcCompilerIdNode("OBJC"),
720   objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran"),
721   hipCompilerIdNode("HIP"), ispcCompilerIdNode("ISPC");
722 
723 struct CompilerVersionNode : public cmGeneratorExpressionNode
724 {
CompilerVersionNodeCompilerVersionNode725   CompilerVersionNode(const char* compilerLang)
726     : CompilerLanguage(compilerLang)
727   {
728   }
729 
NumExpectedParametersCompilerVersionNode730   int NumExpectedParameters() const override { return OneOrZeroParameters; }
731 
EvaluateCompilerVersionNode732   std::string Evaluate(
733     const std::vector<std::string>& parameters,
734     cmGeneratorExpressionContext* context,
735     const GeneratorExpressionContent* content,
736     cmGeneratorExpressionDAGChecker* dagChecker) const override
737   {
738     if (!context->HeadTarget) {
739       std::ostringstream e;
740       e << "$<" << this->CompilerLanguage
741         << "_COMPILER_VERSION> may only be used with binary targets.  It "
742            "may not be used with add_custom_command or add_custom_target.";
743       reportError(context, content->GetOriginalExpression(), e.str());
744       return {};
745     }
746     return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
747                                       this->CompilerLanguage);
748   }
749 
EvaluateWithLanguageCompilerVersionNode750   std::string EvaluateWithLanguage(const std::vector<std::string>& parameters,
751                                    cmGeneratorExpressionContext* context,
752                                    const GeneratorExpressionContent* content,
753                                    cmGeneratorExpressionDAGChecker* /*unused*/,
754                                    const std::string& lang) const
755   {
756     std::string const& compilerVersion =
757       context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
758                                                     "_COMPILER_VERSION");
759     if (parameters.empty()) {
760       return compilerVersion;
761     }
762 
763     static cmsys::RegularExpression compilerIdValidator("^[0-9\\.]*$");
764     if (!compilerIdValidator.find(parameters.front())) {
765       reportError(context, content->GetOriginalExpression(),
766                   "Expression syntax not recognized.");
767       return {};
768     }
769     if (compilerVersion.empty()) {
770       return parameters.front().empty() ? "1" : "0";
771     }
772 
773     return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL,
774                                          parameters.front(), compilerVersion)
775       ? "1"
776       : "0";
777   }
778 
779   const char* const CompilerLanguage;
780 };
781 
782 static const CompilerVersionNode cCompilerVersionNode("C"),
783   cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"),
784   objcCompilerVersionNode("OBJC"), objcxxCompilerVersionNode("OBJCXX"),
785   fortranCompilerVersionNode("Fortran"), ispcCompilerVersionNode("ISPC"),
786   hipCompilerVersionNode("HIP");
787 
788 struct PlatformIdNode : public cmGeneratorExpressionNode
789 {
PlatformIdNodePlatformIdNode790   PlatformIdNode() {} // NOLINT(modernize-use-equals-default)
791 
NumExpectedParametersPlatformIdNode792   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
793 
EvaluatePlatformIdNode794   std::string Evaluate(
795     const std::vector<std::string>& parameters,
796     cmGeneratorExpressionContext* context,
797     const GeneratorExpressionContent* /*content*/,
798     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
799   {
800     std::string const& platformId =
801       context->LG->GetMakefile()->GetSafeDefinition("CMAKE_SYSTEM_NAME");
802     if (parameters.empty()) {
803       return platformId;
804     }
805 
806     if (platformId.empty()) {
807       return parameters.front().empty() ? "1" : "0";
808     }
809 
810     for (auto const& param : parameters) {
811       if (param == platformId) {
812         return "1";
813       }
814     }
815     return "0";
816   }
817 } platformIdNode;
818 
819 template <cmSystemTools::CompareOp Op>
820 struct VersionNode : public cmGeneratorExpressionNode
821 {
VersionNodeVersionNode822   VersionNode() {} // NOLINT(modernize-use-equals-default)
823 
NumExpectedParametersVersionNode824   int NumExpectedParameters() const override { return 2; }
825 
EvaluateVersionNode826   std::string Evaluate(
827     const std::vector<std::string>& parameters,
828     cmGeneratorExpressionContext* /*context*/,
829     const GeneratorExpressionContent* /*content*/,
830     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
831   {
832     return cmSystemTools::VersionCompare(Op, parameters.front(), parameters[1])
833       ? "1"
834       : "0";
835   }
836 };
837 
838 static const VersionNode<cmSystemTools::OP_GREATER> versionGreaterNode;
839 static const VersionNode<cmSystemTools::OP_GREATER_EQUAL> versionGreaterEqNode;
840 static const VersionNode<cmSystemTools::OP_LESS> versionLessNode;
841 static const VersionNode<cmSystemTools::OP_LESS_EQUAL> versionLessEqNode;
842 static const VersionNode<cmSystemTools::OP_EQUAL> versionEqualNode;
843 
844 static const struct LinkOnlyNode : public cmGeneratorExpressionNode
845 {
LinkOnlyNodeLinkOnlyNode846   LinkOnlyNode() {} // NOLINT(modernize-use-equals-default)
847 
EvaluateLinkOnlyNode848   std::string Evaluate(
849     const std::vector<std::string>& parameters,
850     cmGeneratorExpressionContext* context,
851     const GeneratorExpressionContent* content,
852     cmGeneratorExpressionDAGChecker* dagChecker) const override
853   {
854     if (!dagChecker) {
855       reportError(context, content->GetOriginalExpression(),
856                   "$<LINK_ONLY:...> may only be used for linking");
857       return std::string();
858     }
859     if (!dagChecker->GetTransitivePropertiesOnly()) {
860       return parameters.front();
861     }
862     return std::string();
863   }
864 } linkOnlyNode;
865 
866 static const struct ConfigurationNode : public cmGeneratorExpressionNode
867 {
ConfigurationNodeConfigurationNode868   ConfigurationNode() {} // NOLINT(modernize-use-equals-default)
869 
NumExpectedParametersConfigurationNode870   int NumExpectedParameters() const override { return 0; }
871 
EvaluateConfigurationNode872   std::string Evaluate(
873     const std::vector<std::string>& /*parameters*/,
874     cmGeneratorExpressionContext* context,
875     const GeneratorExpressionContent* /*content*/,
876     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
877   {
878     context->HadContextSensitiveCondition = true;
879     return context->Config;
880   }
881 } configurationNode;
882 
883 static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
884 {
ConfigurationTestNodeConfigurationTestNode885   ConfigurationTestNode() {} // NOLINT(modernize-use-equals-default)
886 
NumExpectedParametersConfigurationTestNode887   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
888 
EvaluateConfigurationTestNode889   std::string Evaluate(
890     const std::vector<std::string>& parameters,
891     cmGeneratorExpressionContext* context,
892     const GeneratorExpressionContent* content,
893     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
894   {
895     if (parameters.empty()) {
896       return configurationNode.Evaluate(parameters, context, content, nullptr);
897     }
898     static cmsys::RegularExpression configValidator("^[A-Za-z0-9_]*$");
899     if (!configValidator.find(parameters.front())) {
900       reportError(context, content->GetOriginalExpression(),
901                   "Expression syntax not recognized.");
902       return std::string();
903     }
904     context->HadContextSensitiveCondition = true;
905     for (auto const& param : parameters) {
906       if (context->Config.empty()) {
907         if (param.empty()) {
908           return "1";
909         }
910       } else if (cmsysString_strcasecmp(param.c_str(),
911                                         context->Config.c_str()) == 0) {
912         return "1";
913       }
914     }
915 
916     if (context->CurrentTarget && context->CurrentTarget->IsImported()) {
917       cmValue loc = nullptr;
918       cmValue imp = nullptr;
919       std::string suffix;
920       if (context->CurrentTarget->Target->GetMappedConfig(context->Config, loc,
921                                                           imp, suffix)) {
922         // This imported target has an appropriate location
923         // for this (possibly mapped) config.
924         // Check if there is a proper config mapping for the tested config.
925         std::vector<std::string> mappedConfigs;
926         std::string mapProp = cmStrCat(
927           "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(context->Config));
928         if (cmValue mapValue = context->CurrentTarget->GetProperty(mapProp)) {
929           cmExpandList(cmSystemTools::UpperCase(*mapValue), mappedConfigs);
930 
931           for (auto const& param : parameters) {
932             if (cm::contains(mappedConfigs, cmSystemTools::UpperCase(param))) {
933               return "1";
934             }
935           }
936         }
937       }
938     }
939     return "0";
940   }
941 } configurationTestNode;
942 
943 static const struct JoinNode : public cmGeneratorExpressionNode
944 {
JoinNodeJoinNode945   JoinNode() {} // NOLINT(modernize-use-equals-default)
946 
NumExpectedParametersJoinNode947   int NumExpectedParameters() const override { return 2; }
948 
AcceptsArbitraryContentParameterJoinNode949   bool AcceptsArbitraryContentParameter() const override { return true; }
950 
EvaluateJoinNode951   std::string Evaluate(
952     const std::vector<std::string>& parameters,
953     cmGeneratorExpressionContext* /*context*/,
954     const GeneratorExpressionContent* /*content*/,
955     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
956   {
957     std::vector<std::string> list = cmExpandedList(parameters.front());
958     return cmJoin(list, parameters[1]);
959   }
960 } joinNode;
961 
962 static const struct CompileLanguageNode : public cmGeneratorExpressionNode
963 {
CompileLanguageNodeCompileLanguageNode964   CompileLanguageNode() {} // NOLINT(modernize-use-equals-default)
965 
NumExpectedParametersCompileLanguageNode966   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
967 
EvaluateCompileLanguageNode968   std::string Evaluate(
969     const std::vector<std::string>& parameters,
970     cmGeneratorExpressionContext* context,
971     const GeneratorExpressionContent* content,
972     cmGeneratorExpressionDAGChecker* dagChecker) const override
973   {
974     if (context->Language.empty() &&
975         (!dagChecker || !dagChecker->EvaluatingCompileExpression())) {
976       reportError(
977         context, content->GetOriginalExpression(),
978         "$<COMPILE_LANGUAGE:...> may only be used to specify include "
979         "directories, compile definitions, compile options, and to evaluate "
980         "components of the file(GENERATE) command.");
981       return std::string();
982     }
983 
984     cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
985     std::string genName = gg->GetName();
986     if (genName.find("Makefiles") == std::string::npos &&
987         genName.find("Ninja") == std::string::npos &&
988         genName.find("Visual Studio") == std::string::npos &&
989         genName.find("Xcode") == std::string::npos &&
990         genName.find("Watcom WMake") == std::string::npos) {
991       reportError(context, content->GetOriginalExpression(),
992                   "$<COMPILE_LANGUAGE:...> not supported for this generator.");
993       return std::string();
994     }
995     if (parameters.empty()) {
996       return context->Language;
997     }
998 
999     for (auto const& param : parameters) {
1000       if (context->Language == param) {
1001         return "1";
1002       }
1003     }
1004     return "0";
1005   }
1006 } languageNode;
1007 
1008 static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode
1009 {
CompileLanguageAndIdNodeCompileLanguageAndIdNode1010   CompileLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default)
1011 
NumExpectedParametersCompileLanguageAndIdNode1012   int NumExpectedParameters() const override { return TwoOrMoreParameters; }
1013 
EvaluateCompileLanguageAndIdNode1014   std::string Evaluate(
1015     const std::vector<std::string>& parameters,
1016     cmGeneratorExpressionContext* context,
1017     const GeneratorExpressionContent* content,
1018     cmGeneratorExpressionDAGChecker* dagChecker) const override
1019   {
1020     if (!context->HeadTarget ||
1021         (context->Language.empty() &&
1022          (!dagChecker || !dagChecker->EvaluatingCompileExpression()))) {
1023       // reportError(context, content->GetOriginalExpression(), "");
1024       reportError(
1025         context, content->GetOriginalExpression(),
1026         "$<COMPILE_LANG_AND_ID:lang,id> may only be used with binary targets "
1027         "to specify include directories, compile definitions, and compile "
1028         "options.  It may not be used with the add_custom_command, "
1029         "add_custom_target, or file(GENERATE) commands.");
1030       return std::string();
1031     }
1032     cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1033     std::string genName = gg->GetName();
1034     if (genName.find("Makefiles") == std::string::npos &&
1035         genName.find("Ninja") == std::string::npos &&
1036         genName.find("Visual Studio") == std::string::npos &&
1037         genName.find("Xcode") == std::string::npos &&
1038         genName.find("Watcom WMake") == std::string::npos) {
1039       reportError(
1040         context, content->GetOriginalExpression(),
1041         "$<COMPILE_LANG_AND_ID:lang,id> not supported for this generator.");
1042       return std::string();
1043     }
1044 
1045     const std::string& lang = context->Language;
1046     if (lang == parameters.front()) {
1047       std::vector<std::string> idParameter((parameters.cbegin() + 1),
1048                                            parameters.cend());
1049       return CompilerIdNode{ lang.c_str() }.EvaluateWithLanguage(
1050         idParameter, context, content, dagChecker, lang);
1051     }
1052     return "0";
1053   }
1054 } languageAndIdNode;
1055 
1056 static const struct LinkLanguageNode : public cmGeneratorExpressionNode
1057 {
LinkLanguageNodeLinkLanguageNode1058   LinkLanguageNode() {} // NOLINT(modernize-use-equals-default)
1059 
NumExpectedParametersLinkLanguageNode1060   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1061 
EvaluateLinkLanguageNode1062   std::string Evaluate(
1063     const std::vector<std::string>& parameters,
1064     cmGeneratorExpressionContext* context,
1065     const GeneratorExpressionContent* content,
1066     cmGeneratorExpressionDAGChecker* dagChecker) const override
1067   {
1068     if (!context->HeadTarget || !dagChecker ||
1069         !(dagChecker->EvaluatingLinkExpression() ||
1070           dagChecker->EvaluatingLinkLibraries())) {
1071       reportError(context, content->GetOriginalExpression(),
1072                   "$<LINK_LANGUAGE:...> may only be used with binary targets "
1073                   "to specify link libraries, link directories, link options "
1074                   "and link depends.");
1075       return std::string();
1076     }
1077     if (dagChecker->EvaluatingLinkLibraries() && parameters.empty()) {
1078       reportError(
1079         context, content->GetOriginalExpression(),
1080         "$<LINK_LANGUAGE> is not supported in link libraries expression.");
1081       return std::string();
1082     }
1083 
1084     cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1085     std::string genName = gg->GetName();
1086     if (genName.find("Makefiles") == std::string::npos &&
1087         genName.find("Ninja") == std::string::npos &&
1088         genName.find("Visual Studio") == std::string::npos &&
1089         genName.find("Xcode") == std::string::npos &&
1090         genName.find("Watcom WMake") == std::string::npos) {
1091       reportError(context, content->GetOriginalExpression(),
1092                   "$<LINK_LANGUAGE:...> not supported for this generator.");
1093       return std::string();
1094     }
1095 
1096     if (dagChecker->EvaluatingLinkLibraries()) {
1097       context->HadHeadSensitiveCondition = true;
1098       context->HadLinkLanguageSensitiveCondition = true;
1099     }
1100 
1101     if (parameters.empty()) {
1102       return context->Language;
1103     }
1104 
1105     for (auto const& param : parameters) {
1106       if (context->Language == param) {
1107         return "1";
1108       }
1109     }
1110     return "0";
1111   }
1112 } linkLanguageNode;
1113 
1114 namespace {
1115 struct LinkerId
1116 {
Evaluate__anona0236acb0211::LinkerId1117   static std::string Evaluate(const std::vector<std::string>& parameters,
1118                               cmGeneratorExpressionContext* context,
1119                               const GeneratorExpressionContent* content,
1120                               const std::string& lang)
1121   {
1122     std::string const& linkerId =
1123       context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
1124                                                     "_COMPILER_ID");
1125     if (parameters.empty()) {
1126       return linkerId;
1127     }
1128     if (linkerId.empty()) {
1129       return parameters.front().empty() ? "1" : "0";
1130     }
1131     static cmsys::RegularExpression linkerIdValidator("^[A-Za-z0-9_]*$");
1132 
1133     for (auto const& param : parameters) {
1134       if (!linkerIdValidator.find(param)) {
1135         reportError(context, content->GetOriginalExpression(),
1136                     "Expression syntax not recognized.");
1137         return std::string();
1138       }
1139 
1140       if (param == linkerId) {
1141         return "1";
1142       }
1143     }
1144     return "0";
1145   }
1146 };
1147 }
1148 
1149 static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
1150 {
LinkLanguageAndIdNodeLinkLanguageAndIdNode1151   LinkLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default)
1152 
NumExpectedParametersLinkLanguageAndIdNode1153   int NumExpectedParameters() const override { return TwoOrMoreParameters; }
1154 
EvaluateLinkLanguageAndIdNode1155   std::string Evaluate(
1156     const std::vector<std::string>& parameters,
1157     cmGeneratorExpressionContext* context,
1158     const GeneratorExpressionContent* content,
1159     cmGeneratorExpressionDAGChecker* dagChecker) const override
1160   {
1161     if (!context->HeadTarget || !dagChecker ||
1162         !(dagChecker->EvaluatingLinkExpression() ||
1163           dagChecker->EvaluatingLinkLibraries())) {
1164       reportError(
1165         context, content->GetOriginalExpression(),
1166         "$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets "
1167         "to specify link libraries, link directories, link options, and link "
1168         "depends.");
1169       return std::string();
1170     }
1171 
1172     cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1173     std::string genName = gg->GetName();
1174     if (genName.find("Makefiles") == std::string::npos &&
1175         genName.find("Ninja") == std::string::npos &&
1176         genName.find("Visual Studio") == std::string::npos &&
1177         genName.find("Xcode") == std::string::npos &&
1178         genName.find("Watcom WMake") == std::string::npos) {
1179       reportError(
1180         context, content->GetOriginalExpression(),
1181         "$<LINK_LANG_AND_ID:lang,id> not supported for this generator.");
1182       return std::string();
1183     }
1184 
1185     if (dagChecker->EvaluatingLinkLibraries()) {
1186       context->HadHeadSensitiveCondition = true;
1187       context->HadLinkLanguageSensitiveCondition = true;
1188     }
1189 
1190     const std::string& lang = context->Language;
1191     if (lang == parameters.front()) {
1192       std::vector<std::string> idParameter((parameters.cbegin() + 1),
1193                                            parameters.cend());
1194       return LinkerId::Evaluate(idParameter, context, content, lang);
1195     }
1196     return "0";
1197   }
1198 } linkLanguageAndIdNode;
1199 
1200 static const struct HostLinkNode : public cmGeneratorExpressionNode
1201 {
HostLinkNodeHostLinkNode1202   HostLinkNode() {} // NOLINT(modernize-use-equals-default)
1203 
NumExpectedParametersHostLinkNode1204   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1205 
EvaluateHostLinkNode1206   std::string Evaluate(
1207     const std::vector<std::string>& parameters,
1208     cmGeneratorExpressionContext* context,
1209     const GeneratorExpressionContent* content,
1210     cmGeneratorExpressionDAGChecker* dagChecker) const override
1211   {
1212     if (!context->HeadTarget || !dagChecker ||
1213         !dagChecker->EvaluatingLinkOptionsExpression()) {
1214       reportError(context, content->GetOriginalExpression(),
1215                   "$<HOST_LINK:...> may only be used with binary targets "
1216                   "to specify link options.");
1217       return std::string();
1218     }
1219 
1220     return context->HeadTarget->IsDeviceLink() ? std::string()
1221                                                : cmJoin(parameters, ";");
1222   }
1223 } hostLinkNode;
1224 
1225 static const struct DeviceLinkNode : public cmGeneratorExpressionNode
1226 {
DeviceLinkNodeDeviceLinkNode1227   DeviceLinkNode() {} // NOLINT(modernize-use-equals-default)
1228 
NumExpectedParametersDeviceLinkNode1229   int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1230 
EvaluateDeviceLinkNode1231   std::string Evaluate(
1232     const std::vector<std::string>& parameters,
1233     cmGeneratorExpressionContext* context,
1234     const GeneratorExpressionContent* content,
1235     cmGeneratorExpressionDAGChecker* dagChecker) const override
1236   {
1237     if (!context->HeadTarget || !dagChecker ||
1238         !dagChecker->EvaluatingLinkOptionsExpression()) {
1239       reportError(context, content->GetOriginalExpression(),
1240                   "$<DEVICE_LINK:...> may only be used with binary targets "
1241                   "to specify link options.");
1242       return std::string();
1243     }
1244 
1245     if (context->HeadTarget->IsDeviceLink()) {
1246       std::vector<std::string> list;
1247       cmExpandLists(parameters.begin(), parameters.end(), list);
1248       const auto DL_BEGIN = "<DEVICE_LINK>"_s;
1249       const auto DL_END = "</DEVICE_LINK>"_s;
1250       cm::erase_if(list, [&](const std::string& item) {
1251         return item == DL_BEGIN || item == DL_END;
1252       });
1253 
1254       list.insert(list.begin(), static_cast<std::string>(DL_BEGIN));
1255       list.push_back(static_cast<std::string>(DL_END));
1256 
1257       return cmJoin(list, ";");
1258     }
1259 
1260     return std::string();
1261   }
1262 } deviceLinkNode;
1263 
getLinkedTargetsContent(cmGeneratorTarget const * target,std::string const & prop,cmGeneratorExpressionContext * context,cmGeneratorExpressionDAGChecker * dagChecker)1264 std::string getLinkedTargetsContent(
1265   cmGeneratorTarget const* target, std::string const& prop,
1266   cmGeneratorExpressionContext* context,
1267   cmGeneratorExpressionDAGChecker* dagChecker)
1268 {
1269   std::string result;
1270   if (cmLinkImplementationLibraries const* impl =
1271         target->GetLinkImplementationLibraries(context->Config)) {
1272     for (cmLinkImplItem const& lib : impl->Libraries) {
1273       if (lib.Target) {
1274         // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
1275         // caller's property and hand-evaluate it as if it were compiled.
1276         // Create a context as cmCompiledGeneratorExpression::Evaluate does.
1277         cmGeneratorExpressionContext libContext(
1278           target->GetLocalGenerator(), context->Config, context->Quiet, target,
1279           target, context->EvaluateForBuildsystem, lib.Backtrace,
1280           context->Language);
1281         std::string libResult =
1282           lib.Target->EvaluateInterfaceProperty(prop, &libContext, dagChecker);
1283         if (!libResult.empty()) {
1284           if (result.empty()) {
1285             result = std::move(libResult);
1286           } else {
1287             result.reserve(result.size() + 1 + libResult.size());
1288             result += ";";
1289             result += libResult;
1290           }
1291         }
1292       }
1293     }
1294   }
1295   return result;
1296 }
1297 
1298 static const struct TargetPropertyNode : public cmGeneratorExpressionNode
1299 {
TargetPropertyNodeTargetPropertyNode1300   TargetPropertyNode() {} // NOLINT(modernize-use-equals-default)
1301 
1302   // This node handles errors on parameter count itself.
NumExpectedParametersTargetPropertyNode1303   int NumExpectedParameters() const override { return OneOrMoreParameters; }
1304 
GetErrorTextTargetPropertyNode1305   static const char* GetErrorText(std::string const& targetName,
1306                                   std::string const& propertyName)
1307   {
1308     static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
1309     if (targetName.empty() && propertyName.empty()) {
1310       return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty "
1311              "target name and property name.";
1312     }
1313     if (targetName.empty()) {
1314       return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty "
1315              "target name.";
1316     }
1317     if (!cmGeneratorExpression::IsValidTargetName(targetName)) {
1318       if (!propertyNameValidator.find(propertyName)) {
1319         return "Target name and property name not supported.";
1320       }
1321       return "Target name not supported.";
1322     }
1323     return nullptr;
1324   }
1325 
EvaluateTargetPropertyNode1326   std::string Evaluate(
1327     const std::vector<std::string>& parameters,
1328     cmGeneratorExpressionContext* context,
1329     const GeneratorExpressionContent* content,
1330     cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
1331   {
1332     static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
1333 
1334     cmGeneratorTarget const* target = nullptr;
1335     std::string targetName;
1336     std::string propertyName;
1337 
1338     if (parameters.size() == 2) {
1339       targetName = parameters[0];
1340       propertyName = parameters[1];
1341 
1342       if (const char* e = GetErrorText(targetName, propertyName)) {
1343         reportError(context, content->GetOriginalExpression(), e);
1344         return std::string();
1345       }
1346       if (propertyName == "ALIASED_TARGET"_s) {
1347         if (context->LG->GetMakefile()->IsAlias(targetName)) {
1348           if (cmGeneratorTarget* tgt =
1349                 context->LG->FindGeneratorTargetToUse(targetName)) {
1350             return tgt->GetName();
1351           }
1352         }
1353         return std::string();
1354       }
1355       if (propertyName == "ALIAS_GLOBAL"_s) {
1356         if (context->LG->GetMakefile()->IsAlias(targetName)) {
1357           return context->LG->GetGlobalGenerator()->IsAlias(targetName)
1358             ? "TRUE"
1359             : "FALSE";
1360         }
1361         return std::string();
1362       }
1363       target = context->LG->FindGeneratorTargetToUse(targetName);
1364 
1365       if (!target) {
1366         std::ostringstream e;
1367         e << "Target \"" << targetName << "\" not found.";
1368         reportError(context, content->GetOriginalExpression(), e.str());
1369         return std::string();
1370       }
1371       context->AllTargets.insert(target);
1372 
1373     } else if (parameters.size() == 1) {
1374       target = context->HeadTarget;
1375       propertyName = parameters[0];
1376 
1377       // Keep track of the properties seen while processing.
1378       // The evaluation of the LINK_LIBRARIES generator expressions
1379       // will check this to ensure that properties have one consistent
1380       // value for all evaluations.
1381       context->SeenTargetProperties.insert(propertyName);
1382 
1383       context->HadHeadSensitiveCondition = true;
1384       if (!target) {
1385         reportError(
1386           context, content->GetOriginalExpression(),
1387           "$<TARGET_PROPERTY:prop>  may only be used with binary targets.  "
1388           "It may not be used with add_custom_command or add_custom_target.  "
1389           " "
1390           "Specify the target to read a property from using the "
1391           "$<TARGET_PROPERTY:tgt,prop> signature instead.");
1392         return std::string();
1393       }
1394     } else {
1395       reportError(
1396         context, content->GetOriginalExpression(),
1397         "$<TARGET_PROPERTY:...> expression requires one or two parameters");
1398       return std::string();
1399     }
1400 
1401     if (propertyName == "SOURCES") {
1402       context->SourceSensitiveTargets.insert(target);
1403     }
1404 
1405     if (propertyName.empty()) {
1406       reportError(
1407         context, content->GetOriginalExpression(),
1408         "$<TARGET_PROPERTY:...> expression requires a non-empty property "
1409         "name.");
1410       return std::string();
1411     }
1412 
1413     if (!propertyNameValidator.find(propertyName)) {
1414       ::reportError(context, content->GetOriginalExpression(),
1415                     "Property name not supported.");
1416       return std::string();
1417     }
1418 
1419     assert(target);
1420 
1421     if (propertyName == "LINKER_LANGUAGE") {
1422       if (target->LinkLanguagePropagatesToDependents() && dagCheckerParent &&
1423           (dagCheckerParent->EvaluatingLinkLibraries() ||
1424            dagCheckerParent->EvaluatingSources())) {
1425         reportError(
1426           context, content->GetOriginalExpression(),
1427           "LINKER_LANGUAGE target property can not be used while evaluating "
1428           "link libraries for a static library");
1429         return std::string();
1430       }
1431       return target->GetLinkerLanguage(context->Config);
1432     }
1433 
1434     std::string interfacePropertyName;
1435     bool isInterfaceProperty = false;
1436 
1437 #define POPULATE_INTERFACE_PROPERTY_NAME(prop)                                \
1438   if (propertyName == #prop) {                                                \
1439     interfacePropertyName = "INTERFACE_" #prop;                               \
1440   } else if (propertyName == "INTERFACE_" #prop) {                            \
1441     interfacePropertyName = "INTERFACE_" #prop;                               \
1442     isInterfaceProperty = true;                                               \
1443   } else
1444 
1445     CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(POPULATE_INTERFACE_PROPERTY_NAME)
1446     // Note that the above macro terminates with an else
1447     /* else */ if (cmHasLiteralPrefix(propertyName, "COMPILE_DEFINITIONS_")) {
1448       cmPolicies::PolicyStatus polSt =
1449         context->LG->GetPolicyStatus(cmPolicies::CMP0043);
1450       if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
1451         interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS";
1452       }
1453     }
1454 #undef POPULATE_INTERFACE_PROPERTY_NAME
1455 
1456     bool evaluatingLinkLibraries = false;
1457 
1458     if (dagCheckerParent) {
1459       if (dagCheckerParent->EvaluatingGenexExpression() ||
1460           dagCheckerParent->EvaluatingPICExpression()) {
1461         // No check required.
1462       } else if (dagCheckerParent->EvaluatingLinkLibraries()) {
1463         evaluatingLinkLibraries = true;
1464         if (!interfacePropertyName.empty()) {
1465           reportError(
1466             context, content->GetOriginalExpression(),
1467             "$<TARGET_PROPERTY:...> expression in link libraries "
1468             "evaluation depends on target property which is transitive "
1469             "over the link libraries, creating a recursion.");
1470           return std::string();
1471         }
1472       } else {
1473 #define ASSERT_TRANSITIVE_PROPERTY_METHOD(METHOD) dagCheckerParent->METHOD() ||
1474         assert(CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(
1475           ASSERT_TRANSITIVE_PROPERTY_METHOD) false); // NOLINT(clang-tidy)
1476 #undef ASSERT_TRANSITIVE_PROPERTY_METHOD
1477       }
1478     }
1479 
1480     if (isInterfaceProperty) {
1481       return cmGeneratorExpression::StripEmptyListElements(
1482         target->EvaluateInterfaceProperty(propertyName, context,
1483                                           dagCheckerParent));
1484     }
1485 
1486     cmGeneratorExpressionDAGChecker dagChecker(
1487       context->Backtrace, target, propertyName, content, dagCheckerParent);
1488 
1489     switch (dagChecker.Check()) {
1490       case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
1491         dagChecker.ReportError(context, content->GetOriginalExpression());
1492         return std::string();
1493       case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
1494         // No error. We just skip cyclic references.
1495         return std::string();
1496       case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
1497         // We handle transitive properties above.  For non-transitive
1498         // properties we accept repeats anyway.
1499       case cmGeneratorExpressionDAGChecker::DAG:
1500         break;
1501     }
1502 
1503     std::string result;
1504     bool haveProp = false;
1505     if (cmValue p = target->GetProperty(propertyName)) {
1506       result = *p;
1507       haveProp = true;
1508     } else if (evaluatingLinkLibraries) {
1509       return std::string();
1510     }
1511 
1512     if (!haveProp && !target->IsImported() &&
1513         target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
1514       if (target->IsLinkInterfaceDependentBoolProperty(propertyName,
1515                                                        context->Config)) {
1516         context->HadContextSensitiveCondition = true;
1517         return target->GetLinkInterfaceDependentBoolProperty(propertyName,
1518                                                              context->Config)
1519           ? "1"
1520           : "0";
1521       }
1522       if (target->IsLinkInterfaceDependentStringProperty(propertyName,
1523                                                          context->Config)) {
1524         context->HadContextSensitiveCondition = true;
1525         const char* propContent =
1526           target->GetLinkInterfaceDependentStringProperty(propertyName,
1527                                                           context->Config);
1528         return propContent ? propContent : "";
1529       }
1530       if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName,
1531                                                             context->Config)) {
1532         context->HadContextSensitiveCondition = true;
1533         const char* propContent =
1534           target->GetLinkInterfaceDependentNumberMinProperty(propertyName,
1535                                                              context->Config);
1536         return propContent ? propContent : "";
1537       }
1538       if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName,
1539                                                             context->Config)) {
1540         context->HadContextSensitiveCondition = true;
1541         const char* propContent =
1542           target->GetLinkInterfaceDependentNumberMaxProperty(propertyName,
1543                                                              context->Config);
1544         return propContent ? propContent : "";
1545       }
1546     }
1547 
1548     if (!target->IsImported() && dagCheckerParent &&
1549         !dagCheckerParent->EvaluatingLinkLibraries()) {
1550       if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName,
1551                                                             context->Config)) {
1552         context->HadContextSensitiveCondition = true;
1553         const char* propContent =
1554           target->GetLinkInterfaceDependentNumberMinProperty(propertyName,
1555                                                              context->Config);
1556         return propContent ? propContent : "";
1557       }
1558       if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName,
1559                                                             context->Config)) {
1560         context->HadContextSensitiveCondition = true;
1561         const char* propContent =
1562           target->GetLinkInterfaceDependentNumberMaxProperty(propertyName,
1563                                                              context->Config);
1564         return propContent ? propContent : "";
1565       }
1566     }
1567 
1568     if (!interfacePropertyName.empty()) {
1569       result = cmGeneratorExpression::StripEmptyListElements(
1570         this->EvaluateDependentExpression(result, context->LG, context, target,
1571                                           &dagChecker, target));
1572       std::string linkedTargetsContent = getLinkedTargetsContent(
1573         target, interfacePropertyName, context, &dagChecker);
1574       if (!linkedTargetsContent.empty()) {
1575         result += (result.empty() ? "" : ";") + linkedTargetsContent;
1576       }
1577     }
1578     return result;
1579   }
1580 } targetPropertyNode;
1581 
1582 static const struct TargetNameNode : public cmGeneratorExpressionNode
1583 {
TargetNameNodeTargetNameNode1584   TargetNameNode() {} // NOLINT(modernize-use-equals-default)
1585 
GeneratesContentTargetNameNode1586   bool GeneratesContent() const override { return true; }
1587 
AcceptsArbitraryContentParameterTargetNameNode1588   bool AcceptsArbitraryContentParameter() const override { return true; }
RequiresLiteralInputTargetNameNode1589   bool RequiresLiteralInput() const override { return true; }
1590 
EvaluateTargetNameNode1591   std::string Evaluate(
1592     const std::vector<std::string>& parameters,
1593     cmGeneratorExpressionContext* /*context*/,
1594     const GeneratorExpressionContent* /*content*/,
1595     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1596   {
1597     return parameters.front();
1598   }
1599 
NumExpectedParametersTargetNameNode1600   int NumExpectedParameters() const override { return 1; }
1601 
1602 } targetNameNode;
1603 
1604 static const struct TargetObjectsNode : public cmGeneratorExpressionNode
1605 {
TargetObjectsNodeTargetObjectsNode1606   TargetObjectsNode() {} // NOLINT(modernize-use-equals-default)
1607 
EvaluateTargetObjectsNode1608   std::string Evaluate(
1609     const std::vector<std::string>& parameters,
1610     cmGeneratorExpressionContext* context,
1611     const GeneratorExpressionContent* content,
1612     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1613   {
1614     std::string tgtName = parameters.front();
1615     cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
1616     if (!gt) {
1617       std::ostringstream e;
1618       e << "Objects of target \"" << tgtName
1619         << "\" referenced but no such target exists.";
1620       reportError(context, content->GetOriginalExpression(), e.str());
1621       return std::string();
1622     }
1623     cmStateEnums::TargetType type = gt->GetType();
1624     if (type != cmStateEnums::EXECUTABLE &&
1625         type != cmStateEnums::STATIC_LIBRARY &&
1626         type != cmStateEnums::SHARED_LIBRARY &&
1627         type != cmStateEnums::MODULE_LIBRARY &&
1628         type != cmStateEnums::OBJECT_LIBRARY) {
1629       std::ostringstream e;
1630       e << "Objects of target \"" << tgtName
1631         << "\" referenced but is not one of the allowed target types "
1632         << "(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT).";
1633       reportError(context, content->GetOriginalExpression(), e.str());
1634       return std::string();
1635     }
1636     cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1637     {
1638       std::string reason;
1639       if (!context->EvaluateForBuildsystem &&
1640           !gg->HasKnownObjectFileLocation(&reason)) {
1641         std::ostringstream e;
1642         e << "The evaluation of the TARGET_OBJECTS generator expression "
1643              "is only suitable for consumption by CMake (limited"
1644           << reason
1645           << ").  "
1646              "It is not suitable for writing out elsewhere.";
1647         reportError(context, content->GetOriginalExpression(), e.str());
1648         return std::string();
1649       }
1650     }
1651 
1652     std::vector<std::string> objects;
1653 
1654     if (gt->IsImported()) {
1655       cmValue loc = nullptr;
1656       cmValue imp = nullptr;
1657       std::string suffix;
1658       if (gt->Target->GetMappedConfig(context->Config, loc, imp, suffix)) {
1659         cmExpandList(*loc, objects);
1660       }
1661       context->HadContextSensitiveCondition = true;
1662     } else {
1663       gt->GetTargetObjectNames(context->Config, objects);
1664 
1665       std::string obj_dir;
1666       if (context->EvaluateForBuildsystem && !gg->SupportsCrossConfigs()) {
1667         // Use object file directory with buildsystem placeholder.
1668         obj_dir = gt->ObjectDirectory;
1669         context->HadContextSensitiveCondition =
1670           gt->HasContextDependentSources();
1671       } else {
1672         // Use object file directory with per-config location.
1673         obj_dir = gt->GetObjectDirectory(context->Config);
1674         context->HadContextSensitiveCondition = true;
1675       }
1676 
1677       for (std::string& o : objects) {
1678         o = cmStrCat(obj_dir, o);
1679       }
1680     }
1681 
1682     // Create the cmSourceFile instances in the referencing directory.
1683     cmMakefile* mf = context->LG->GetMakefile();
1684     for (std::string const& o : objects) {
1685       mf->AddTargetObject(tgtName, o);
1686     }
1687 
1688     return cmJoin(objects, ";");
1689   }
1690 } targetObjectsNode;
1691 
1692 static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
1693 {
TargetRuntimeDllsNodeTargetRuntimeDllsNode1694   TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default)
1695 
EvaluateTargetRuntimeDllsNode1696   std::string Evaluate(
1697     const std::vector<std::string>& parameters,
1698     cmGeneratorExpressionContext* context,
1699     const GeneratorExpressionContent* content,
1700     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1701   {
1702     std::string tgtName = parameters.front();
1703     cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
1704     if (!gt) {
1705       std::ostringstream e;
1706       e << "Objects of target \"" << tgtName
1707         << "\" referenced but no such target exists.";
1708       reportError(context, content->GetOriginalExpression(), e.str());
1709       return std::string();
1710     }
1711     cmStateEnums::TargetType type = gt->GetType();
1712     if (type != cmStateEnums::EXECUTABLE &&
1713         type != cmStateEnums::SHARED_LIBRARY &&
1714         type != cmStateEnums::MODULE_LIBRARY) {
1715       std::ostringstream e;
1716       e << "Objects of target \"" << tgtName
1717         << "\" referenced but is not one of the allowed target types "
1718         << "(EXECUTABLE, SHARED, MODULE).";
1719       reportError(context, content->GetOriginalExpression(), e.str());
1720       return std::string();
1721     }
1722 
1723     if (auto* cli = gt->GetLinkInformation(context->Config)) {
1724       std::vector<std::string> dllPaths;
1725       auto const& dlls = cli->GetRuntimeDLLs();
1726 
1727       for (auto const& dll : dlls) {
1728         if (auto loc = dll->MaybeGetLocation(context->Config)) {
1729           dllPaths.emplace_back(*loc);
1730         }
1731       }
1732 
1733       return cmJoin(dllPaths, ";");
1734     }
1735 
1736     return "";
1737   }
1738 } targetRuntimeDllsNode;
1739 
1740 static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
1741 {
CompileFeaturesNodeCompileFeaturesNode1742   CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default)
1743 
NumExpectedParametersCompileFeaturesNode1744   int NumExpectedParameters() const override { return OneOrMoreParameters; }
1745 
EvaluateCompileFeaturesNode1746   std::string Evaluate(
1747     const std::vector<std::string>& parameters,
1748     cmGeneratorExpressionContext* context,
1749     const GeneratorExpressionContent* content,
1750     cmGeneratorExpressionDAGChecker* dagChecker) const override
1751   {
1752     cmGeneratorTarget const* target = context->HeadTarget;
1753     if (!target) {
1754       reportError(
1755         context, content->GetOriginalExpression(),
1756         "$<COMPILE_FEATURE> may only be used with binary targets.  It may "
1757         "not be used with add_custom_command or add_custom_target.");
1758       return std::string();
1759     }
1760     context->HadHeadSensitiveCondition = true;
1761 
1762     using LangMap = std::map<std::string, std::vector<std::string>>;
1763     static LangMap availableFeatures;
1764 
1765     LangMap testedFeatures;
1766     cmStandardLevelResolver standardResolver(context->LG->GetMakefile());
1767     for (std::string const& p : parameters) {
1768       std::string error;
1769       std::string lang;
1770       if (!standardResolver.CompileFeatureKnown(
1771             context->HeadTarget->Target->GetName(), p, lang, &error)) {
1772         reportError(context, content->GetOriginalExpression(), error);
1773         return std::string();
1774       }
1775       testedFeatures[lang].push_back(p);
1776 
1777       if (availableFeatures.find(lang) == availableFeatures.end()) {
1778         cmValue featuresKnown =
1779           standardResolver.CompileFeaturesAvailable(lang, &error);
1780         if (!featuresKnown) {
1781           reportError(context, content->GetOriginalExpression(), error);
1782           return std::string();
1783         }
1784         cmExpandList(featuresKnown, availableFeatures[lang]);
1785       }
1786     }
1787 
1788     bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries();
1789 
1790     for (auto const& lit : testedFeatures) {
1791       std::vector<std::string> const& langAvailable =
1792         availableFeatures[lit.first];
1793       cmValue standardDefault = context->LG->GetMakefile()->GetDefinition(
1794         "CMAKE_" + lit.first + "_STANDARD_DEFAULT");
1795       for (std::string const& it : lit.second) {
1796         if (!cm::contains(langAvailable, it)) {
1797           return "0";
1798         }
1799         if (standardDefault && standardDefault->empty()) {
1800           // This compiler has no notion of language standard levels.
1801           // All features known for the language are always available.
1802           continue;
1803         }
1804         if (!standardResolver.HaveStandardAvailable(target, lit.first,
1805                                                     context->Config, it)) {
1806           if (evalLL) {
1807             cmValue l =
1808               target->GetLanguageStandard(lit.first, context->Config);
1809             if (!l) {
1810               l = standardDefault;
1811             }
1812             assert(l);
1813             context->MaxLanguageStandard[target][lit.first] = *l;
1814           } else {
1815             return "0";
1816           }
1817         }
1818       }
1819     }
1820     return "1";
1821   }
1822 } compileFeaturesNode;
1823 
1824 static const char* targetPolicyWhitelist[] = {
1825   nullptr
1826 #define TARGET_POLICY_STRING(POLICY) , #POLICY
1827 
1828   CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_STRING)
1829 
1830 #undef TARGET_POLICY_STRING
1831 };
1832 
statusForTarget(cmGeneratorTarget const * tgt,const char * policy)1833 cmPolicies::PolicyStatus statusForTarget(cmGeneratorTarget const* tgt,
1834                                          const char* policy)
1835 {
1836 #define RETURN_POLICY(POLICY)                                                 \
1837   if (strcmp(policy, #POLICY) == 0) {                                         \
1838     return tgt->GetPolicyStatus##POLICY();                                    \
1839   }
1840 
1841   CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY)
1842 
1843 #undef RETURN_POLICY
1844 
1845   assert(false && "Unreachable code. Not a valid policy");
1846   return cmPolicies::WARN;
1847 }
1848 
policyForString(const char * policy_id)1849 cmPolicies::PolicyID policyForString(const char* policy_id)
1850 {
1851 #define RETURN_POLICY_ID(POLICY_ID)                                           \
1852   if (strcmp(policy_id, #POLICY_ID) == 0) {                                   \
1853     return cmPolicies::POLICY_ID;                                             \
1854   }
1855 
1856   CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY_ID)
1857 
1858 #undef RETURN_POLICY_ID
1859 
1860   assert(false && "Unreachable code. Not a valid policy");
1861   return cmPolicies::CMP0002;
1862 }
1863 
1864 static const struct TargetPolicyNode : public cmGeneratorExpressionNode
1865 {
TargetPolicyNodeTargetPolicyNode1866   TargetPolicyNode() {} // NOLINT(modernize-use-equals-default)
1867 
NumExpectedParametersTargetPolicyNode1868   int NumExpectedParameters() const override { return 1; }
1869 
EvaluateTargetPolicyNode1870   std::string Evaluate(
1871     const std::vector<std::string>& parameters,
1872     cmGeneratorExpressionContext* context,
1873     const GeneratorExpressionContent* content,
1874     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1875   {
1876     if (!context->HeadTarget) {
1877       reportError(
1878         context, content->GetOriginalExpression(),
1879         "$<TARGET_POLICY:prop> may only be used with binary targets.  It "
1880         "may not be used with add_custom_command or add_custom_target.");
1881       return std::string();
1882     }
1883 
1884     context->HadContextSensitiveCondition = true;
1885     context->HadHeadSensitiveCondition = true;
1886 
1887     for (size_t i = 1; i < cm::size(targetPolicyWhitelist); ++i) {
1888       const char* policy = targetPolicyWhitelist[i];
1889       if (parameters.front() == policy) {
1890         cmLocalGenerator* lg = context->HeadTarget->GetLocalGenerator();
1891         switch (statusForTarget(context->HeadTarget, policy)) {
1892           case cmPolicies::WARN:
1893             lg->IssueMessage(
1894               MessageType::AUTHOR_WARNING,
1895               cmPolicies::GetPolicyWarning(policyForString(policy)));
1896             CM_FALLTHROUGH;
1897           case cmPolicies::REQUIRED_IF_USED:
1898           case cmPolicies::REQUIRED_ALWAYS:
1899           case cmPolicies::OLD:
1900             return "0";
1901           case cmPolicies::NEW:
1902             return "1";
1903         }
1904       }
1905     }
1906     reportError(
1907       context, content->GetOriginalExpression(),
1908       "$<TARGET_POLICY:prop> may only be used with a limited number of "
1909       "policies.  Currently it may be used with the following policies:\n"
1910 
1911 #define STRINGIFY_HELPER(X) #X
1912 #define STRINGIFY(X) STRINGIFY_HELPER(X)
1913 
1914 #define TARGET_POLICY_LIST_ITEM(POLICY) " * " STRINGIFY(POLICY) "\n"
1915 
1916       CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_LIST_ITEM)
1917 
1918 #undef TARGET_POLICY_LIST_ITEM
1919     );
1920     return std::string();
1921   }
1922 
1923 } targetPolicyNode;
1924 
1925 static const struct InstallPrefixNode : public cmGeneratorExpressionNode
1926 {
InstallPrefixNodeInstallPrefixNode1927   InstallPrefixNode() {} // NOLINT(modernize-use-equals-default)
1928 
GeneratesContentInstallPrefixNode1929   bool GeneratesContent() const override { return true; }
NumExpectedParametersInstallPrefixNode1930   int NumExpectedParameters() const override { return 0; }
1931 
EvaluateInstallPrefixNode1932   std::string Evaluate(
1933     const std::vector<std::string>& /*parameters*/,
1934     cmGeneratorExpressionContext* context,
1935     const GeneratorExpressionContent* content,
1936     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1937   {
1938     reportError(context, content->GetOriginalExpression(),
1939                 "INSTALL_PREFIX is a marker for install(EXPORT) only.  It "
1940                 "should never be evaluated.");
1941     return std::string();
1942   }
1943 
1944 } installPrefixNode;
1945 
1946 class ArtifactDirTag;
1947 class ArtifactLinkerTag;
1948 class ArtifactNameTag;
1949 class ArtifactPathTag;
1950 class ArtifactPdbTag;
1951 class ArtifactSonameTag;
1952 class ArtifactBundleDirTag;
1953 class ArtifactBundleContentDirTag;
1954 
1955 template <typename ArtifactT, typename ComponentT>
1956 struct TargetFilesystemArtifactDependency
1957 {
AddDependencyTargetFilesystemArtifactDependency1958   static void AddDependency(cmGeneratorTarget* target,
1959                             cmGeneratorExpressionContext* context)
1960   {
1961     context->DependTargets.insert(target);
1962     context->AllTargets.insert(target);
1963   }
1964 };
1965 
1966 struct TargetFilesystemArtifactDependencyCMP0112
1967 {
AddDependencyTargetFilesystemArtifactDependencyCMP01121968   static void AddDependency(cmGeneratorTarget* target,
1969                             cmGeneratorExpressionContext* context)
1970   {
1971     context->AllTargets.insert(target);
1972     cmLocalGenerator* lg = context->LG;
1973     switch (target->GetPolicyStatusCMP0112()) {
1974       case cmPolicies::WARN:
1975         if (lg->GetMakefile()->PolicyOptionalWarningEnabled(
1976               "CMAKE_POLICY_WARNING_CMP0112")) {
1977           std::string err =
1978             cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0112),
1979                      "\nDependency being added to target:\n  \"",
1980                      target->GetName(), "\"\n");
1981           lg->GetCMakeInstance()->IssueMessage(MessageType ::AUTHOR_WARNING,
1982                                                err, context->Backtrace);
1983         }
1984         CM_FALLTHROUGH;
1985       case cmPolicies::OLD:
1986         context->DependTargets.insert(target);
1987         break;
1988       case cmPolicies::REQUIRED_IF_USED:
1989       case cmPolicies::REQUIRED_ALWAYS:
1990       case cmPolicies::NEW:
1991         break;
1992     }
1993   }
1994 };
1995 
1996 template <typename ArtifactT>
1997 struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactNameTag>
1998   : TargetFilesystemArtifactDependencyCMP0112
1999 {
2000 };
2001 template <typename ArtifactT>
2002 struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactDirTag>
2003   : TargetFilesystemArtifactDependencyCMP0112
2004 {
2005 };
2006 template <>
2007 struct TargetFilesystemArtifactDependency<ArtifactBundleDirTag,
2008                                           ArtifactPathTag>
2009   : TargetFilesystemArtifactDependencyCMP0112
2010 {
2011 };
2012 template <>
2013 struct TargetFilesystemArtifactDependency<ArtifactBundleContentDirTag,
2014                                           ArtifactPathTag>
2015   : TargetFilesystemArtifactDependencyCMP0112
2016 {
2017 };
2018 
2019 template <typename ArtifactT>
2020 struct TargetFilesystemArtifactResultCreator
2021 {
2022   static std::string Create(cmGeneratorTarget* target,
2023                             cmGeneratorExpressionContext* context,
2024                             const GeneratorExpressionContent* content);
2025 };
2026 
2027 template <>
2028 struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag>
2029 {
CreateTargetFilesystemArtifactResultCreator2030   static std::string Create(cmGeneratorTarget* target,
2031                             cmGeneratorExpressionContext* context,
2032                             const GeneratorExpressionContent* content)
2033   {
2034     // The target soname file (.so.1).
2035     if (target->IsDLLPlatform()) {
2036       ::reportError(context, content->GetOriginalExpression(),
2037                     "TARGET_SONAME_FILE is not allowed "
2038                     "for DLL target platforms.");
2039       return std::string();
2040     }
2041     if (target->GetType() != cmStateEnums::SHARED_LIBRARY) {
2042       ::reportError(context, content->GetOriginalExpression(),
2043                     "TARGET_SONAME_FILE is allowed only for "
2044                     "SHARED libraries.");
2045       return std::string();
2046     }
2047     std::string result = cmStrCat(target->GetDirectory(context->Config), '/',
2048                                   target->GetSOName(context->Config));
2049     return result;
2050   }
2051 };
2052 
2053 template <>
2054 struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag>
2055 {
CreateTargetFilesystemArtifactResultCreator2056   static std::string Create(cmGeneratorTarget* target,
2057                             cmGeneratorExpressionContext* context,
2058                             const GeneratorExpressionContent* content)
2059   {
2060     if (target->IsImported()) {
2061       ::reportError(context, content->GetOriginalExpression(),
2062                     "TARGET_PDB_FILE not allowed for IMPORTED targets.");
2063       return std::string();
2064     }
2065 
2066     std::string language = target->GetLinkerLanguage(context->Config);
2067 
2068     std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB";
2069 
2070     if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) {
2071       ::reportError(context, content->GetOriginalExpression(),
2072                     "TARGET_PDB_FILE is not supported by the target linker.");
2073       return std::string();
2074     }
2075 
2076     cmStateEnums::TargetType targetType = target->GetType();
2077 
2078     if (targetType != cmStateEnums::SHARED_LIBRARY &&
2079         targetType != cmStateEnums::MODULE_LIBRARY &&
2080         targetType != cmStateEnums::EXECUTABLE) {
2081       ::reportError(context, content->GetOriginalExpression(),
2082                     "TARGET_PDB_FILE is allowed only for "
2083                     "targets with linker created artifacts.");
2084       return std::string();
2085     }
2086 
2087     std::string result = cmStrCat(target->GetPDBDirectory(context->Config),
2088                                   '/', target->GetPDBName(context->Config));
2089     return result;
2090   }
2091 };
2092 
2093 template <>
2094 struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag>
2095 {
CreateTargetFilesystemArtifactResultCreator2096   static std::string Create(cmGeneratorTarget* target,
2097                             cmGeneratorExpressionContext* context,
2098                             const GeneratorExpressionContent* content)
2099   {
2100     // The file used to link to the target (.so, .lib, .a).
2101     if (!target->IsLinkable()) {
2102       ::reportError(context, content->GetOriginalExpression(),
2103                     "TARGET_LINKER_FILE is allowed only for libraries and "
2104                     "executables with ENABLE_EXPORTS.");
2105       return std::string();
2106     }
2107     cmStateEnums::ArtifactType artifact =
2108       target->HasImportLibrary(context->Config)
2109       ? cmStateEnums::ImportLibraryArtifact
2110       : cmStateEnums::RuntimeBinaryArtifact;
2111     return target->GetFullPath(context->Config, artifact);
2112   }
2113 };
2114 
2115 template <>
2116 struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag>
2117 {
CreateTargetFilesystemArtifactResultCreator2118   static std::string Create(cmGeneratorTarget* target,
2119                             cmGeneratorExpressionContext* context,
2120                             const GeneratorExpressionContent* content)
2121   {
2122     if (target->IsImported()) {
2123       ::reportError(context, content->GetOriginalExpression(),
2124                     "TARGET_BUNDLE_DIR not allowed for IMPORTED targets.");
2125       return std::string();
2126     }
2127     if (!target->IsBundleOnApple()) {
2128       ::reportError(context, content->GetOriginalExpression(),
2129                     "TARGET_BUNDLE_DIR is allowed only for Bundle targets.");
2130       return std::string();
2131     }
2132 
2133     std::string outpath = target->GetDirectory(context->Config) + '/';
2134     return target->BuildBundleDirectory(outpath, context->Config,
2135                                         cmGeneratorTarget::BundleDirLevel);
2136   }
2137 };
2138 
2139 template <>
2140 struct TargetFilesystemArtifactResultCreator<ArtifactBundleContentDirTag>
2141 {
CreateTargetFilesystemArtifactResultCreator2142   static std::string Create(cmGeneratorTarget* target,
2143                             cmGeneratorExpressionContext* context,
2144                             const GeneratorExpressionContent* content)
2145   {
2146     if (target->IsImported()) {
2147       ::reportError(
2148         context, content->GetOriginalExpression(),
2149         "TARGET_BUNDLE_CONTENT_DIR not allowed for IMPORTED targets.");
2150       return std::string();
2151     }
2152     if (!target->IsBundleOnApple()) {
2153       ::reportError(
2154         context, content->GetOriginalExpression(),
2155         "TARGET_BUNDLE_CONTENT_DIR is allowed only for Bundle targets.");
2156       return std::string();
2157     }
2158 
2159     std::string outpath = target->GetDirectory(context->Config) + '/';
2160     return target->BuildBundleDirectory(outpath, context->Config,
2161                                         cmGeneratorTarget::ContentLevel);
2162   }
2163 };
2164 
2165 template <>
2166 struct TargetFilesystemArtifactResultCreator<ArtifactNameTag>
2167 {
CreateTargetFilesystemArtifactResultCreator2168   static std::string Create(cmGeneratorTarget* target,
2169                             cmGeneratorExpressionContext* context,
2170                             const GeneratorExpressionContent* /*unused*/)
2171   {
2172     return target->GetFullPath(context->Config,
2173                                cmStateEnums::RuntimeBinaryArtifact, true);
2174   }
2175 };
2176 
2177 template <typename ArtifactT>
2178 struct TargetFilesystemArtifactResultGetter
2179 {
2180   static std::string Get(const std::string& result);
2181 };
2182 
2183 template <>
2184 struct TargetFilesystemArtifactResultGetter<ArtifactNameTag>
2185 {
GetTargetFilesystemArtifactResultGetter2186   static std::string Get(const std::string& result)
2187   {
2188     return cmSystemTools::GetFilenameName(result);
2189   }
2190 };
2191 
2192 template <>
2193 struct TargetFilesystemArtifactResultGetter<ArtifactDirTag>
2194 {
GetTargetFilesystemArtifactResultGetter2195   static std::string Get(const std::string& result)
2196   {
2197     return cmSystemTools::GetFilenamePath(result);
2198   }
2199 };
2200 
2201 template <>
2202 struct TargetFilesystemArtifactResultGetter<ArtifactPathTag>
2203 {
GetTargetFilesystemArtifactResultGetter2204   static std::string Get(const std::string& result) { return result; }
2205 };
2206 
2207 struct TargetArtifactBase : public cmGeneratorExpressionNode
2208 {
TargetArtifactBaseTargetArtifactBase2209   TargetArtifactBase() {} // NOLINT(modernize-use-equals-default)
2210 
2211 protected:
GetTargetTargetArtifactBase2212   cmGeneratorTarget* GetTarget(
2213     const std::vector<std::string>& parameters,
2214     cmGeneratorExpressionContext* context,
2215     const GeneratorExpressionContent* content,
2216     cmGeneratorExpressionDAGChecker* dagChecker) const
2217   {
2218     // Lookup the referenced target.
2219     std::string name = parameters.front();
2220 
2221     if (!cmGeneratorExpression::IsValidTargetName(name)) {
2222       ::reportError(context, content->GetOriginalExpression(),
2223                     "Expression syntax not recognized.");
2224       return nullptr;
2225     }
2226     cmGeneratorTarget* target = context->LG->FindGeneratorTargetToUse(name);
2227     if (!target) {
2228       ::reportError(context, content->GetOriginalExpression(),
2229                     "No target \"" + name + "\"");
2230       return nullptr;
2231     }
2232     if (target->GetType() >= cmStateEnums::OBJECT_LIBRARY &&
2233         target->GetType() != cmStateEnums::UNKNOWN_LIBRARY) {
2234       ::reportError(context, content->GetOriginalExpression(),
2235                     "Target \"" + name +
2236                       "\" is not an executable or library.");
2237       return nullptr;
2238     }
2239     if (dagChecker &&
2240         (dagChecker->EvaluatingLinkLibraries(target) ||
2241          (dagChecker->EvaluatingSources() &&
2242           target == dagChecker->TopTarget()))) {
2243       ::reportError(context, content->GetOriginalExpression(),
2244                     "Expressions which require the linker language may not "
2245                     "be used while evaluating link libraries");
2246       return nullptr;
2247     }
2248 
2249     return target;
2250   }
2251 };
2252 
2253 template <typename ArtifactT, typename ComponentT>
2254 struct TargetFilesystemArtifact : public TargetArtifactBase
2255 {
TargetFilesystemArtifactTargetFilesystemArtifact2256   TargetFilesystemArtifact() {} // NOLINT(modernize-use-equals-default)
2257 
NumExpectedParametersTargetFilesystemArtifact2258   int NumExpectedParameters() const override { return 1; }
2259 
EvaluateTargetFilesystemArtifact2260   std::string Evaluate(
2261     const std::vector<std::string>& parameters,
2262     cmGeneratorExpressionContext* context,
2263     const GeneratorExpressionContent* content,
2264     cmGeneratorExpressionDAGChecker* dagChecker) const override
2265   {
2266     cmGeneratorTarget* target =
2267       this->GetTarget(parameters, context, content, dagChecker);
2268     if (!target) {
2269       return std::string();
2270     }
2271     // Not a dependent target if we are querying for ArtifactDirTag,
2272     // ArtifactNameTag, ArtifactBundleDirTag, and ArtifactBundleContentDirTag
2273     TargetFilesystemArtifactDependency<ArtifactT, ComponentT>::AddDependency(
2274       target, context);
2275 
2276     std::string result =
2277       TargetFilesystemArtifactResultCreator<ArtifactT>::Create(target, context,
2278                                                                content);
2279     if (context->HadError) {
2280       return std::string();
2281     }
2282     return TargetFilesystemArtifactResultGetter<ComponentT>::Get(result);
2283   }
2284 };
2285 
2286 template <typename ArtifactT>
2287 struct TargetFilesystemArtifactNodeGroup
2288 {
TargetFilesystemArtifactNodeGroupTargetFilesystemArtifactNodeGroup2289   TargetFilesystemArtifactNodeGroup() // NOLINT(modernize-use-equals-default)
2290   {
2291   }
2292 
2293   TargetFilesystemArtifact<ArtifactT, ArtifactPathTag> File;
2294   TargetFilesystemArtifact<ArtifactT, ArtifactNameTag> FileName;
2295   TargetFilesystemArtifact<ArtifactT, ArtifactDirTag> FileDir;
2296 };
2297 
2298 static const TargetFilesystemArtifactNodeGroup<ArtifactNameTag>
2299   targetNodeGroup;
2300 
2301 static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag>
2302   targetLinkerNodeGroup;
2303 
2304 static const TargetFilesystemArtifactNodeGroup<ArtifactSonameTag>
2305   targetSoNameNodeGroup;
2306 
2307 static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag>
2308   targetPdbNodeGroup;
2309 
2310 static const TargetFilesystemArtifact<ArtifactBundleDirTag, ArtifactPathTag>
2311   targetBundleDirNode;
2312 
2313 static const TargetFilesystemArtifact<ArtifactBundleContentDirTag,
2314                                       ArtifactPathTag>
2315   targetBundleContentDirNode;
2316 
2317 //
2318 // To retrieve base name for various artifacts
2319 //
2320 template <typename ArtifactT>
2321 struct TargetOutputNameArtifactResultGetter
2322 {
2323   static std::string Get(cmGeneratorTarget* target,
2324                          cmGeneratorExpressionContext* context,
2325                          const GeneratorExpressionContent* content);
2326 };
2327 
2328 template <>
2329 struct TargetOutputNameArtifactResultGetter<ArtifactNameTag>
2330 {
GetTargetOutputNameArtifactResultGetter2331   static std::string Get(cmGeneratorTarget* target,
2332                          cmGeneratorExpressionContext* context,
2333                          const GeneratorExpressionContent* /*unused*/)
2334   {
2335     return target->GetOutputName(context->Config,
2336                                  cmStateEnums::RuntimeBinaryArtifact) +
2337       target->GetFilePostfix(context->Config);
2338   }
2339 };
2340 
2341 template <>
2342 struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
2343 {
GetTargetOutputNameArtifactResultGetter2344   static std::string Get(cmGeneratorTarget* target,
2345                          cmGeneratorExpressionContext* context,
2346                          const GeneratorExpressionContent* content)
2347   {
2348     // The file used to link to the target (.so, .lib, .a).
2349     if (!target->IsLinkable()) {
2350       ::reportError(context, content->GetOriginalExpression(),
2351                     "TARGET_LINKER_FILE_BASE_NAME is allowed only for "
2352                     "libraries and executables with ENABLE_EXPORTS.");
2353       return std::string();
2354     }
2355     cmStateEnums::ArtifactType artifact =
2356       target->HasImportLibrary(context->Config)
2357       ? cmStateEnums::ImportLibraryArtifact
2358       : cmStateEnums::RuntimeBinaryArtifact;
2359     return target->GetOutputName(context->Config, artifact) +
2360       target->GetFilePostfix(context->Config);
2361   }
2362 };
2363 
2364 template <>
2365 struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag>
2366 {
GetTargetOutputNameArtifactResultGetter2367   static std::string Get(cmGeneratorTarget* target,
2368                          cmGeneratorExpressionContext* context,
2369                          const GeneratorExpressionContent* content)
2370   {
2371     if (target->IsImported()) {
2372       ::reportError(
2373         context, content->GetOriginalExpression(),
2374         "TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets.");
2375       return std::string();
2376     }
2377 
2378     std::string language = target->GetLinkerLanguage(context->Config);
2379 
2380     std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB";
2381 
2382     if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) {
2383       ::reportError(
2384         context, content->GetOriginalExpression(),
2385         "TARGET_PDB_FILE_BASE_NAME is not supported by the target linker.");
2386       return std::string();
2387     }
2388 
2389     cmStateEnums::TargetType targetType = target->GetType();
2390 
2391     if (targetType != cmStateEnums::SHARED_LIBRARY &&
2392         targetType != cmStateEnums::MODULE_LIBRARY &&
2393         targetType != cmStateEnums::EXECUTABLE) {
2394       ::reportError(context, content->GetOriginalExpression(),
2395                     "TARGET_PDB_FILE_BASE_NAME is allowed only for "
2396                     "targets with linker created artifacts.");
2397       return std::string();
2398     }
2399 
2400     return target->GetPDBOutputName(context->Config) +
2401       target->GetFilePostfix(context->Config);
2402   }
2403 };
2404 
2405 template <typename ArtifactT>
2406 struct TargetFileBaseNameArtifact : public TargetArtifactBase
2407 {
TargetFileBaseNameArtifactTargetFileBaseNameArtifact2408   TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default)
2409 
NumExpectedParametersTargetFileBaseNameArtifact2410   int NumExpectedParameters() const override { return 1; }
2411 
EvaluateTargetFileBaseNameArtifact2412   std::string Evaluate(
2413     const std::vector<std::string>& parameters,
2414     cmGeneratorExpressionContext* context,
2415     const GeneratorExpressionContent* content,
2416     cmGeneratorExpressionDAGChecker* dagChecker) const override
2417   {
2418     cmGeneratorTarget* target =
2419       this->GetTarget(parameters, context, content, dagChecker);
2420     if (!target) {
2421       return std::string();
2422     }
2423 
2424     std::string result = TargetOutputNameArtifactResultGetter<ArtifactT>::Get(
2425       target, context, content);
2426     if (context->HadError) {
2427       return std::string();
2428     }
2429     return result;
2430   }
2431 };
2432 
2433 static const TargetFileBaseNameArtifact<ArtifactNameTag>
2434   targetFileBaseNameNode;
2435 static const TargetFileBaseNameArtifact<ArtifactLinkerTag>
2436   targetLinkerFileBaseNameNode;
2437 static const TargetFileBaseNameArtifact<ArtifactPdbTag>
2438   targetPdbFileBaseNameNode;
2439 
2440 class ArtifactFilePrefixTag;
2441 class ArtifactLinkerFilePrefixTag;
2442 class ArtifactFileSuffixTag;
2443 class ArtifactLinkerFileSuffixTag;
2444 
2445 template <typename ArtifactT>
2446 struct TargetFileArtifactResultGetter
2447 {
2448   static std::string Get(cmGeneratorTarget* target,
2449                          cmGeneratorExpressionContext* context,
2450                          const GeneratorExpressionContent* content);
2451 };
2452 
2453 template <>
2454 struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag>
2455 {
GetTargetFileArtifactResultGetter2456   static std::string Get(cmGeneratorTarget* target,
2457                          cmGeneratorExpressionContext* context,
2458                          const GeneratorExpressionContent*)
2459   {
2460     return target->GetFilePrefix(context->Config);
2461   }
2462 };
2463 template <>
2464 struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
2465 {
GetTargetFileArtifactResultGetter2466   static std::string Get(cmGeneratorTarget* target,
2467                          cmGeneratorExpressionContext* context,
2468                          const GeneratorExpressionContent* content)
2469   {
2470     if (!target->IsLinkable()) {
2471       ::reportError(context, content->GetOriginalExpression(),
2472                     "TARGET_LINKER_PREFIX is allowed only for libraries and "
2473                     "executables with ENABLE_EXPORTS.");
2474       return std::string();
2475     }
2476 
2477     cmStateEnums::ArtifactType artifact =
2478       target->HasImportLibrary(context->Config)
2479       ? cmStateEnums::ImportLibraryArtifact
2480       : cmStateEnums::RuntimeBinaryArtifact;
2481 
2482     return target->GetFilePrefix(context->Config, artifact);
2483   }
2484 };
2485 template <>
2486 struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag>
2487 {
GetTargetFileArtifactResultGetter2488   static std::string Get(cmGeneratorTarget* target,
2489                          cmGeneratorExpressionContext* context,
2490                          const GeneratorExpressionContent*)
2491   {
2492     return target->GetFileSuffix(context->Config);
2493   }
2494 };
2495 template <>
2496 struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
2497 {
GetTargetFileArtifactResultGetter2498   static std::string Get(cmGeneratorTarget* target,
2499                          cmGeneratorExpressionContext* context,
2500                          const GeneratorExpressionContent* content)
2501   {
2502     if (!target->IsLinkable()) {
2503       ::reportError(context, content->GetOriginalExpression(),
2504                     "TARGET_LINKER_SUFFIX is allowed only for libraries and "
2505                     "executables with ENABLE_EXPORTS.");
2506       return std::string();
2507     }
2508 
2509     cmStateEnums::ArtifactType artifact =
2510       target->HasImportLibrary(context->Config)
2511       ? cmStateEnums::ImportLibraryArtifact
2512       : cmStateEnums::RuntimeBinaryArtifact;
2513 
2514     return target->GetFileSuffix(context->Config, artifact);
2515   }
2516 };
2517 
2518 template <typename ArtifactT>
2519 struct TargetFileArtifact : public TargetArtifactBase
2520 {
TargetFileArtifactTargetFileArtifact2521   TargetFileArtifact() {} // NOLINT(modernize-use-equals-default)
2522 
NumExpectedParametersTargetFileArtifact2523   int NumExpectedParameters() const override { return 1; }
2524 
EvaluateTargetFileArtifact2525   std::string Evaluate(
2526     const std::vector<std::string>& parameters,
2527     cmGeneratorExpressionContext* context,
2528     const GeneratorExpressionContent* content,
2529     cmGeneratorExpressionDAGChecker* dagChecker) const override
2530   {
2531     cmGeneratorTarget* target =
2532       this->GetTarget(parameters, context, content, dagChecker);
2533     if (!target) {
2534       return std::string();
2535     }
2536 
2537     std::string result =
2538       TargetFileArtifactResultGetter<ArtifactT>::Get(target, context, content);
2539     if (context->HadError) {
2540       return std::string();
2541     }
2542     return result;
2543   }
2544 };
2545 
2546 static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode;
2547 static const TargetFileArtifact<ArtifactLinkerFilePrefixTag>
2548   targetLinkerFilePrefixNode;
2549 static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode;
2550 static const TargetFileArtifact<ArtifactLinkerFileSuffixTag>
2551   targetLinkerFileSuffixNode;
2552 
2553 static const struct ShellPathNode : public cmGeneratorExpressionNode
2554 {
ShellPathNodeShellPathNode2555   ShellPathNode() {} // NOLINT(modernize-use-equals-default)
2556 
EvaluateShellPathNode2557   std::string Evaluate(
2558     const std::vector<std::string>& parameters,
2559     cmGeneratorExpressionContext* context,
2560     const GeneratorExpressionContent* content,
2561     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2562   {
2563     std::vector<std::string> listIn = cmExpandedList(parameters.front());
2564     if (listIn.empty()) {
2565       reportError(context, content->GetOriginalExpression(),
2566                   "\"\" is not an absolute path.");
2567       return std::string();
2568     }
2569     cmStateSnapshot snapshot = context->LG->GetStateSnapshot();
2570     cmOutputConverter converter(snapshot);
2571     const char* separator = snapshot.GetState()->UseWindowsShell() ? ";" : ":";
2572     std::vector<std::string> listOut;
2573     listOut.reserve(listIn.size());
2574     for (auto const& in : listIn) {
2575       if (!cmSystemTools::FileIsFullPath(in)) {
2576         reportError(context, content->GetOriginalExpression(),
2577                     "\"" + in + "\" is not an absolute path.");
2578         return std::string();
2579       }
2580       listOut.emplace_back(converter.ConvertDirectorySeparatorsForShell(in));
2581     }
2582     return cmJoin(listOut, separator);
2583   }
2584 } shellPathNode;
2585 
GetNode(const std::string & identifier)2586 const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
2587   const std::string& identifier)
2588 {
2589   static std::map<std::string, cmGeneratorExpressionNode const*> const nodeMap{
2590     { "0", &zeroNode },
2591     { "1", &oneNode },
2592     { "AND", &andNode },
2593     { "OR", &orNode },
2594     { "NOT", &notNode },
2595     { "C_COMPILER_ID", &cCompilerIdNode },
2596     { "CXX_COMPILER_ID", &cxxCompilerIdNode },
2597     { "OBJC_COMPILER_ID", &objcCompilerIdNode },
2598     { "OBJCXX_COMPILER_ID", &objcxxCompilerIdNode },
2599     { "CUDA_COMPILER_ID", &cudaCompilerIdNode },
2600     { "Fortran_COMPILER_ID", &fortranCompilerIdNode },
2601     { "HIP_COMPILER_ID", &hipCompilerIdNode },
2602     { "VERSION_GREATER", &versionGreaterNode },
2603     { "VERSION_GREATER_EQUAL", &versionGreaterEqNode },
2604     { "VERSION_LESS", &versionLessNode },
2605     { "VERSION_LESS_EQUAL", &versionLessEqNode },
2606     { "VERSION_EQUAL", &versionEqualNode },
2607     { "C_COMPILER_VERSION", &cCompilerVersionNode },
2608     { "CXX_COMPILER_VERSION", &cxxCompilerVersionNode },
2609     { "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode },
2610     { "OBJC_COMPILER_VERSION", &objcCompilerVersionNode },
2611     { "OBJCXX_COMPILER_VERSION", &objcxxCompilerVersionNode },
2612     { "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode },
2613     { "HIP_COMPILER_VERSION", &hipCompilerVersionNode },
2614     { "PLATFORM_ID", &platformIdNode },
2615     { "COMPILE_FEATURES", &compileFeaturesNode },
2616     { "CONFIGURATION", &configurationNode },
2617     { "CONFIG", &configurationTestNode },
2618     { "TARGET_FILE", &targetNodeGroup.File },
2619     { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File },
2620     { "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File },
2621     { "TARGET_PDB_FILE", &targetPdbNodeGroup.File },
2622     { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode },
2623     { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode },
2624     { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode },
2625     { "TARGET_FILE_PREFIX", &targetFilePrefixNode },
2626     { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode },
2627     { "TARGET_FILE_SUFFIX", &targetFileSuffixNode },
2628     { "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode },
2629     { "TARGET_FILE_NAME", &targetNodeGroup.FileName },
2630     { "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.FileName },
2631     { "TARGET_SONAME_FILE_NAME", &targetSoNameNodeGroup.FileName },
2632     { "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName },
2633     { "TARGET_FILE_DIR", &targetNodeGroup.FileDir },
2634     { "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.FileDir },
2635     { "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.FileDir },
2636     { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir },
2637     { "TARGET_BUNDLE_DIR", &targetBundleDirNode },
2638     { "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode },
2639     { "STREQUAL", &strEqualNode },
2640     { "EQUAL", &equalNode },
2641     { "IN_LIST", &inListNode },
2642     { "FILTER", &filterNode },
2643     { "REMOVE_DUPLICATES", &removeDuplicatesNode },
2644     { "LOWER_CASE", &lowerCaseNode },
2645     { "UPPER_CASE", &upperCaseNode },
2646     { "MAKE_C_IDENTIFIER", &makeCIdentifierNode },
2647     { "BOOL", &boolNode },
2648     { "IF", &ifNode },
2649     { "ANGLE-R", &angle_rNode },
2650     { "COMMA", &commaNode },
2651     { "SEMICOLON", &semicolonNode },
2652     { "TARGET_PROPERTY", &targetPropertyNode },
2653     { "TARGET_NAME", &targetNameNode },
2654     { "TARGET_OBJECTS", &targetObjectsNode },
2655     { "TARGET_POLICY", &targetPolicyNode },
2656     { "TARGET_EXISTS", &targetExistsNode },
2657     { "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode },
2658     { "TARGET_GENEX_EVAL", &targetGenexEvalNode },
2659     { "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode },
2660     { "GENEX_EVAL", &genexEvalNode },
2661     { "BUILD_INTERFACE", &buildInterfaceNode },
2662     { "INSTALL_INTERFACE", &installInterfaceNode },
2663     { "INSTALL_PREFIX", &installPrefixNode },
2664     { "JOIN", &joinNode },
2665     { "LINK_ONLY", &linkOnlyNode },
2666     { "COMPILE_LANG_AND_ID", &languageAndIdNode },
2667     { "COMPILE_LANGUAGE", &languageNode },
2668     { "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
2669     { "LINK_LANGUAGE", &linkLanguageNode },
2670     { "HOST_LINK", &hostLinkNode },
2671     { "DEVICE_LINK", &deviceLinkNode },
2672     { "SHELL_PATH", &shellPathNode }
2673   };
2674 
2675   {
2676     auto itr = nodeMap.find(identifier);
2677     if (itr != nodeMap.end()) {
2678       return itr->second;
2679     }
2680   }
2681   return nullptr;
2682 }
2683 
reportError(cmGeneratorExpressionContext * context,const std::string & expr,const std::string & result)2684 void reportError(cmGeneratorExpressionContext* context,
2685                  const std::string& expr, const std::string& result)
2686 {
2687   context->HadError = true;
2688   if (context->Quiet) {
2689     return;
2690   }
2691 
2692   std::ostringstream e;
2693   /* clang-format off */
2694   e << "Error evaluating generator expression:\n"
2695     << "  " << expr << "\n"
2696     << result;
2697   /* clang-format on */
2698   context->LG->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
2699                                                 e.str(), context->Backtrace);
2700 }
2701