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", ¬Node },
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