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"
15 #include <unordered_map>
18 #include <cm/iterator>
19 #include <cm/optional>
20 #include <cm/string_view>
22 #include <cmext/algorithm>
23 #include <cmext/string_view>
25 #include "cmsys/RegularExpression.hxx"
26 #include "cmsys/String.h"
28 #include "cmAlgorithms.h"
29 #include "cmCMakePath.h"
30 #include "cmComputeLinkInformation.h"
31 #include "cmGeneratorExpression.h"
32 #include "cmGeneratorExpressionContext.h"
33 #include "cmGeneratorExpressionDAGChecker.h"
34 #include "cmGeneratorExpressionEvaluator.h"
35 #include "cmGeneratorTarget.h"
36 #include "cmGlobalGenerator.h"
37 #include "cmLinkItem.h"
38 #include "cmLocalGenerator.h"
39 #include "cmMakefile.h"
40 #include "cmMessageType.h"
41 #include "cmOutputConverter.h"
42 #include "cmPolicies.h"
44 #include "cmStandardLevelResolver.h"
46 #include "cmStateSnapshot.h"
47 #include "cmStateTypes.h"
48 #include "cmStringAlgorithms.h"
49 #include "cmSystemTools.h"
54 std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
55 std::string const& prop, cmLocalGenerator* lg,
56 cmGeneratorExpressionContext* context, cmGeneratorTarget const* headTarget,
57 cmGeneratorExpressionDAGChecker* dagChecker,
58 cmGeneratorTarget const* currentTarget)
60 cmGeneratorExpression ge(context->Backtrace);
61 std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop);
62 cge->SetEvaluateForBuildsystem(context->EvaluateForBuildsystem);
63 cge->SetQuiet(context->Quiet);
65 cge->Evaluate(lg, context->Config, headTarget, dagChecker, currentTarget,
67 if (cge->GetHadContextSensitiveCondition()) {
68 context->HadContextSensitiveCondition = true;
70 if (cge->GetHadHeadSensitiveCondition()) {
71 context->HadHeadSensitiveCondition = true;
73 if (cge->GetHadLinkLanguageSensitiveCondition()) {
74 context->HadLinkLanguageSensitiveCondition = true;
79 static const struct ZeroNode : public cmGeneratorExpressionNode
81 ZeroNode() {} // NOLINT(modernize-use-equals-default)
83 bool GeneratesContent() const override { return false; }
85 bool AcceptsArbitraryContentParameter() const override { return true; }
88 const std::vector<std::string>& /*parameters*/,
89 cmGeneratorExpressionContext* /*context*/,
90 const GeneratorExpressionContent* /*content*/,
91 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
97 static const struct OneNode : public cmGeneratorExpressionNode
99 OneNode() {} // NOLINT(modernize-use-equals-default)
101 bool AcceptsArbitraryContentParameter() const override { return true; }
103 std::string Evaluate(
104 const std::vector<std::string>& parameters,
105 cmGeneratorExpressionContext* /*context*/,
106 const GeneratorExpressionContent* /*content*/,
107 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
109 return parameters.front();
113 static const struct OneNode buildInterfaceNode;
115 static const struct ZeroNode installInterfaceNode;
117 struct BooleanOpNode : public cmGeneratorExpressionNode
119 BooleanOpNode(const char* op_, const char* successVal_,
120 const char* failureVal_)
122 , successVal(successVal_)
123 , failureVal(failureVal_)
127 int NumExpectedParameters() const override { return OneOrMoreParameters; }
129 std::string Evaluate(const std::vector<std::string>& parameters,
130 cmGeneratorExpressionContext* context,
131 const GeneratorExpressionContent* content,
132 cmGeneratorExpressionDAGChecker*) const override
134 for (std::string const& param : parameters) {
135 if (param == this->failureVal) {
136 return this->failureVal;
138 if (param != this->successVal) {
139 std::ostringstream e;
140 e << "Parameters to $<" << this->op;
141 e << "> must resolve to either '0' or '1'.";
142 reportError(context, content->GetOriginalExpression(), e.str());
143 return std::string();
146 return this->successVal;
149 const char *const op, *const successVal, *const failureVal;
152 static const BooleanOpNode andNode("AND", "1", "0"), orNode("OR", "0", "1");
154 static const struct NotNode : public cmGeneratorExpressionNode
156 NotNode() {} // NOLINT(modernize-use-equals-default)
158 std::string Evaluate(
159 const std::vector<std::string>& parameters,
160 cmGeneratorExpressionContext* context,
161 const GeneratorExpressionContent* content,
162 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
164 if (parameters.front() != "0" && parameters.front() != "1") {
166 context, content->GetOriginalExpression(),
167 "$<NOT> parameter must resolve to exactly one '0' or '1' value.");
168 return std::string();
170 return parameters.front() == "0" ? "1" : "0";
174 static const struct BoolNode : public cmGeneratorExpressionNode
176 BoolNode() {} // NOLINT(modernize-use-equals-default)
178 int NumExpectedParameters() const override { return 1; }
180 std::string Evaluate(
181 const std::vector<std::string>& parameters,
182 cmGeneratorExpressionContext* /*context*/,
183 const GeneratorExpressionContent* /*content*/,
184 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
186 return !cmIsOff(parameters.front()) ? "1" : "0";
190 static const struct IfNode : public cmGeneratorExpressionNode
192 IfNode() {} // NOLINT(modernize-use-equals-default)
194 int NumExpectedParameters() const override { return 3; }
196 std::string Evaluate(const std::vector<std::string>& parameters,
197 cmGeneratorExpressionContext* context,
198 const GeneratorExpressionContent* content,
199 cmGeneratorExpressionDAGChecker*) const override
201 if (parameters[0] != "1" && parameters[0] != "0") {
202 reportError(context, content->GetOriginalExpression(),
203 "First parameter to $<IF> must resolve to exactly one '0' "
205 return std::string();
207 return parameters[0] == "1" ? parameters[1] : parameters[2];
211 static const struct StrEqualNode : public cmGeneratorExpressionNode
213 StrEqualNode() {} // NOLINT(modernize-use-equals-default)
215 int NumExpectedParameters() const override { return 2; }
217 std::string Evaluate(
218 const std::vector<std::string>& parameters,
219 cmGeneratorExpressionContext* /*context*/,
220 const GeneratorExpressionContent* /*content*/,
221 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
223 return parameters.front() == parameters[1] ? "1" : "0";
227 static const struct EqualNode : public cmGeneratorExpressionNode
229 EqualNode() {} // NOLINT(modernize-use-equals-default)
231 int NumExpectedParameters() const override { return 2; }
233 std::string Evaluate(
234 const std::vector<std::string>& parameters,
235 cmGeneratorExpressionContext* context,
236 const GeneratorExpressionContent* content,
237 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
240 for (int i = 0; i < 2; ++i) {
241 if (!ParameterToLong(parameters[i].c_str(), &numbers[i])) {
242 reportError(context, content->GetOriginalExpression(),
243 "$<EQUAL> parameter " + parameters[i] +
244 " is not a valid integer.");
248 return numbers[0] == numbers[1] ? "1" : "0";
251 static bool ParameterToLong(const char* param, long* outResult)
253 const char isNegative = param[0] == '-';
256 if (cmHasLiteralPrefix(param, "0b") || cmHasLiteralPrefix(param, "0B")) {
259 } else if (cmHasLiteralPrefix(param, "-0b") ||
260 cmHasLiteralPrefix(param, "-0B") ||
261 cmHasLiteralPrefix(param, "+0b") ||
262 cmHasLiteralPrefix(param, "+0B")) {
268 long result = strtol(param, &pEnd, base);
269 if (pEnd == param || *pEnd != '\0' || errno == ERANGE) {
272 if (isNegative && result > 0) {
280 static const struct InListNode : public cmGeneratorExpressionNode
282 InListNode() {} // NOLINT(modernize-use-equals-default)
284 int NumExpectedParameters() const override { return 2; }
286 std::string Evaluate(
287 const std::vector<std::string>& parameters,
288 cmGeneratorExpressionContext* context,
289 const GeneratorExpressionContent* /*content*/,
290 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
292 std::vector<std::string> values;
293 std::vector<std::string> checkValues;
295 switch (context->LG->GetPolicyStatus(cmPolicies::CMP0085)) {
296 case cmPolicies::WARN:
297 if (parameters.front().empty()) {
299 cmExpandList(parameters[1], checkValues, true);
302 case cmPolicies::OLD:
303 cmExpandList(parameters[1], values);
304 if (check && values != checkValues) {
305 std::ostringstream e;
306 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085)
307 << "\nSearch Item:\n \"" << parameters.front()
308 << "\"\nList:\n \"" << parameters[1] << "\"\n";
309 context->LG->GetCMakeInstance()->IssueMessage(
310 MessageType ::AUTHOR_WARNING, e.str(), context->Backtrace);
313 if (values.empty()) {
317 case cmPolicies::REQUIRED_IF_USED:
318 case cmPolicies::REQUIRED_ALWAYS:
319 case cmPolicies::NEW:
320 cmExpandList(parameters[1], values, true);
324 return cm::contains(values, parameters.front()) ? "1" : "0";
328 static const struct FilterNode : public cmGeneratorExpressionNode
330 FilterNode() {} // NOLINT(modernize-use-equals-default)
332 int NumExpectedParameters() const override { return 3; }
334 std::string Evaluate(
335 const std::vector<std::string>& parameters,
336 cmGeneratorExpressionContext* context,
337 const GeneratorExpressionContent* content,
338 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
340 if (parameters.size() != 3) {
341 reportError(context, content->GetOriginalExpression(),
342 "$<FILTER:...> expression requires three parameters");
346 if (parameters[1] != "INCLUDE" && parameters[1] != "EXCLUDE") {
348 context, content->GetOriginalExpression(),
349 "$<FILTER:...> second parameter must be either INCLUDE or EXCLUDE");
353 const bool exclude = parameters[1] == "EXCLUDE";
355 cmsys::RegularExpression re;
356 if (!re.compile(parameters[2])) {
357 reportError(context, content->GetOriginalExpression(),
358 "$<FILTER:...> failed to compile regex");
362 std::vector<std::string> values;
363 std::vector<std::string> result;
364 cmExpandList(parameters.front(), values, true);
366 std::copy_if(values.cbegin(), values.cend(), std::back_inserter(result),
367 [&re, exclude](std::string const& input) {
368 return exclude ^ re.find(input);
370 return cmJoin(cmMakeRange(result.cbegin(), result.cend()), ";");
374 static const struct RemoveDuplicatesNode : public cmGeneratorExpressionNode
376 RemoveDuplicatesNode() {} // NOLINT(modernize-use-equals-default)
378 int NumExpectedParameters() const override { return 1; }
380 std::string Evaluate(
381 const std::vector<std::string>& parameters,
382 cmGeneratorExpressionContext* context,
383 const GeneratorExpressionContent* content,
384 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
386 if (parameters.size() != 1) {
388 context, content->GetOriginalExpression(),
389 "$<REMOVE_DUPLICATES:...> expression requires one parameter");
392 std::vector<std::string> values = cmExpandedList(parameters.front(), true);
394 auto valuesEnd = cmRemoveDuplicates(values);
395 auto valuesBegin = values.cbegin();
396 return cmJoin(cmMakeRange(valuesBegin, valuesEnd), ";");
399 } removeDuplicatesNode;
401 static const struct TargetExistsNode : public cmGeneratorExpressionNode
403 TargetExistsNode() {} // NOLINT(modernize-use-equals-default)
405 int NumExpectedParameters() const override { return 1; }
407 std::string Evaluate(
408 const std::vector<std::string>& parameters,
409 cmGeneratorExpressionContext* context,
410 const GeneratorExpressionContent* content,
411 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
413 if (parameters.size() != 1) {
414 reportError(context, content->GetOriginalExpression(),
415 "$<TARGET_EXISTS:...> expression requires one parameter");
416 return std::string();
419 std::string const& targetName = parameters.front();
420 if (targetName.empty() ||
421 !cmGeneratorExpression::IsValidTargetName(targetName)) {
422 reportError(context, content->GetOriginalExpression(),
423 "$<TARGET_EXISTS:tgt> expression requires a non-empty "
424 "valid target name.");
425 return std::string();
428 return context->LG->GetMakefile()->FindTargetToUse(targetName) ? "1" : "0";
432 static const struct TargetNameIfExistsNode : public cmGeneratorExpressionNode
434 TargetNameIfExistsNode() {} // NOLINT(modernize-use-equals-default)
436 int NumExpectedParameters() const override { return 1; }
438 std::string Evaluate(
439 const std::vector<std::string>& parameters,
440 cmGeneratorExpressionContext* context,
441 const GeneratorExpressionContent* content,
442 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
444 if (parameters.size() != 1) {
445 reportError(context, content->GetOriginalExpression(),
446 "$<TARGET_NAME_IF_EXISTS:...> expression requires one "
448 return std::string();
451 std::string const& targetName = parameters.front();
452 if (targetName.empty() ||
453 !cmGeneratorExpression::IsValidTargetName(targetName)) {
454 reportError(context, content->GetOriginalExpression(),
455 "$<TARGET_NAME_IF_EXISTS:tgt> expression requires a "
456 "non-empty valid target name.");
457 return std::string();
460 return context->LG->GetMakefile()->FindTargetToUse(targetName)
464 } targetNameIfExistsNode;
466 struct GenexEvaluator : public cmGeneratorExpressionNode
468 GenexEvaluator() {} // NOLINT(modernize-use-equals-default)
471 std::string EvaluateExpression(
472 const std::string& genexOperator, const std::string& expression,
473 cmGeneratorExpressionContext* context,
474 const GeneratorExpressionContent* content,
475 cmGeneratorExpressionDAGChecker* dagCheckerParent) const
477 if (context->HeadTarget) {
478 cmGeneratorExpressionDAGChecker dagChecker(
479 context->Backtrace, context->HeadTarget,
480 genexOperator + ":" + expression, content, dagCheckerParent);
481 switch (dagChecker.Check()) {
482 case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
483 case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: {
484 dagChecker.ReportError(context, content->GetOriginalExpression());
485 return std::string();
487 case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
488 case cmGeneratorExpressionDAGChecker::DAG:
492 return this->EvaluateDependentExpression(
493 expression, context->LG, context, context->HeadTarget, &dagChecker,
494 context->CurrentTarget);
497 return this->EvaluateDependentExpression(
498 expression, context->LG, context, context->HeadTarget, dagCheckerParent,
499 context->CurrentTarget);
503 static const struct TargetGenexEvalNode : public GenexEvaluator
505 TargetGenexEvalNode() {} // NOLINT(modernize-use-equals-default)
507 int NumExpectedParameters() const override { return 2; }
509 bool AcceptsArbitraryContentParameter() const override { return true; }
511 std::string Evaluate(
512 const std::vector<std::string>& parameters,
513 cmGeneratorExpressionContext* context,
514 const GeneratorExpressionContent* content,
515 cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
517 const std::string& targetName = parameters.front();
518 if (targetName.empty() ||
519 !cmGeneratorExpression::IsValidTargetName(targetName)) {
520 reportError(context, content->GetOriginalExpression(),
521 "$<TARGET_GENEX_EVAL:tgt, ...> expression requires a "
522 "non-empty valid target name.");
523 return std::string();
526 const auto* target = context->LG->FindGeneratorTargetToUse(targetName);
528 std::ostringstream e;
529 e << "$<TARGET_GENEX_EVAL:tgt, ...> target \"" << targetName
531 reportError(context, content->GetOriginalExpression(), e.str());
532 return std::string();
535 const std::string& expression = parameters[1];
536 if (expression.empty()) {
540 cmGeneratorExpressionContext targetContext(
541 context->LG, context->Config, context->Quiet, target, target,
542 context->EvaluateForBuildsystem, context->Backtrace, context->Language);
544 return this->EvaluateExpression("TARGET_GENEX_EVAL", expression,
545 &targetContext, content, dagCheckerParent);
547 } targetGenexEvalNode;
549 static const struct GenexEvalNode : public GenexEvaluator
551 GenexEvalNode() {} // NOLINT(modernize-use-equals-default)
553 int NumExpectedParameters() const override { return 1; }
555 bool AcceptsArbitraryContentParameter() const override { return true; }
557 std::string Evaluate(
558 const std::vector<std::string>& parameters,
559 cmGeneratorExpressionContext* context,
560 const GeneratorExpressionContent* content,
561 cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
563 const std::string& expression = parameters[0];
564 if (expression.empty()) {
568 return this->EvaluateExpression("GENEX_EVAL", expression, context, content,
573 static const struct LowerCaseNode : public cmGeneratorExpressionNode
575 LowerCaseNode() {} // NOLINT(modernize-use-equals-default)
577 bool AcceptsArbitraryContentParameter() const override { return true; }
579 std::string Evaluate(
580 const std::vector<std::string>& parameters,
581 cmGeneratorExpressionContext* /*context*/,
582 const GeneratorExpressionContent* /*content*/,
583 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
585 return cmSystemTools::LowerCase(parameters.front());
589 static const struct UpperCaseNode : public cmGeneratorExpressionNode
591 UpperCaseNode() {} // NOLINT(modernize-use-equals-default)
593 bool AcceptsArbitraryContentParameter() const override { return true; }
595 std::string Evaluate(
596 const std::vector<std::string>& parameters,
597 cmGeneratorExpressionContext* /*context*/,
598 const GeneratorExpressionContent* /*content*/,
599 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
601 return cmSystemTools::UpperCase(parameters.front());
606 template <typename Container>
607 class Range : public cmRange<typename Container::const_iterator>
610 using Base = cmRange<typename Container::const_iterator>;
613 using const_iterator = typename Container::const_iterator;
614 using value_type = typename Container::value_type;
615 using size_type = typename Container::size_type;
616 using difference_type = typename Container::difference_type;
617 using const_reference = typename Container::const_reference;
619 Range(const Container& container)
620 : Base(container.begin(), container.end())
624 const_reference operator[](size_type pos) const
626 return *(this->begin() + pos);
629 const_reference front() const { return *this->begin(); }
630 const_reference back() const { return *std::prev(this->end()); }
632 Range& advance(difference_type amount) &
634 Base::advance(amount);
637 Range advance(difference_type amount) &&
639 Base::advance(amount);
640 return std::move(*this);
644 using Arguments = Range<std::vector<std::string>>;
646 bool CheckPathParametersEx(cmGeneratorExpressionContext* ctx,
647 const GeneratorExpressionContent* cnt,
648 cm::string_view option, std::size_t count,
649 int required = 1, bool exactly = true)
651 if (static_cast<int>(count) < required ||
652 (exactly && static_cast<int>(count) > required)) {
653 reportError(ctx, cnt->GetOriginalExpression(),
654 cmStrCat("$<PATH:", option, "> expression requires ",
655 (exactly ? "exactly" : "at least"), ' ',
656 (required == 1 ? "one parameter" : "two parameters"),
662 bool CheckPathParameters(cmGeneratorExpressionContext* ctx,
663 const GeneratorExpressionContent* cnt,
664 cm::string_view option, const Arguments& args,
667 return CheckPathParametersEx(ctx, cnt, option, args.size(), required);
669 std::string ToString(bool isTrue)
671 return isTrue ? "1" : "0";
675 static const struct PathNode : public cmGeneratorExpressionNode
677 PathNode() {} // NOLINT(modernize-use-equals-default)
679 int NumExpectedParameters() const override { return TwoOrMoreParameters; }
681 bool AcceptsArbitraryContentParameter() const override { return true; }
683 std::string Evaluate(
684 const std::vector<std::string>& parameters,
685 cmGeneratorExpressionContext* context,
686 const GeneratorExpressionContent* content,
687 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
689 static std::unordered_map<
691 std::function<std::string(cmGeneratorExpressionContext*,
692 const GeneratorExpressionContent*,
696 [](cmGeneratorExpressionContext* ctx,
697 const GeneratorExpressionContent* cnt,
698 Arguments& args) -> std::string {
699 return CheckPathParameters(ctx, cnt, "GET_ROOT_NAME"_s, args) &&
700 !args.front().empty()
701 ? cmCMakePath{ args.front() }.GetRootName().String()
704 { "GET_ROOT_DIRECTORY"_s,
705 [](cmGeneratorExpressionContext* ctx,
706 const GeneratorExpressionContent* cnt,
707 Arguments& args) -> std::string {
708 return CheckPathParameters(ctx, cnt, "GET_ROOT_DIRECTORY"_s,
710 !args.front().empty()
711 ? cmCMakePath{ args.front() }.GetRootDirectory().String()
715 [](cmGeneratorExpressionContext* ctx,
716 const GeneratorExpressionContent* cnt,
717 Arguments& args) -> std::string {
718 return CheckPathParameters(ctx, cnt, "GET_ROOT_PATH"_s, args) &&
719 !args.front().empty()
720 ? cmCMakePath{ args.front() }.GetRootPath().String()
724 [](cmGeneratorExpressionContext* ctx,
725 const GeneratorExpressionContent* cnt,
726 Arguments& args) -> std::string {
727 return CheckPathParameters(ctx, cnt, "GET_FILENAME"_s, args) &&
728 !args.front().empty()
729 ? cmCMakePath{ args.front() }.GetFileName().String()
733 [](cmGeneratorExpressionContext* ctx,
734 const GeneratorExpressionContent* cnt,
735 Arguments& args) -> std::string {
736 bool lastOnly = args.front() == "LAST_ONLY"_s;
740 if (CheckPathParametersEx(ctx, cnt,
741 lastOnly ? "GET_EXTENSION,LAST_ONLY"_s
744 if (args.front().empty()) {
745 return std::string{};
748 ? cmCMakePath{ args.front() }.GetExtension().String()
749 : cmCMakePath{ args.front() }.GetWideExtension().String();
751 return std::string{};
754 [](cmGeneratorExpressionContext* ctx,
755 const GeneratorExpressionContent* cnt,
756 Arguments& args) -> std::string {
757 bool lastOnly = args.front() == "LAST_ONLY"_s;
761 if (CheckPathParametersEx(
762 ctx, cnt, lastOnly ? "GET_STEM,LAST_ONLY"_s : "GET_STEM"_s,
764 if (args.front().empty()) {
765 return std::string{};
768 ? cmCMakePath{ args.front() }.GetStem().String()
769 : cmCMakePath{ args.front() }.GetNarrowStem().String();
771 return std::string{};
773 { "GET_RELATIVE_PART"_s,
774 [](cmGeneratorExpressionContext* ctx,
775 const GeneratorExpressionContent* cnt,
776 Arguments& args) -> std::string {
777 return CheckPathParameters(ctx, cnt, "GET_RELATIVE_PART"_s,
779 !args.front().empty()
780 ? cmCMakePath{ args.front() }.GetRelativePath().String()
783 { "GET_PARENT_PATH"_s,
784 [](cmGeneratorExpressionContext* ctx,
785 const GeneratorExpressionContent* cnt,
786 Arguments& args) -> std::string {
787 return CheckPathParameters(ctx, cnt, "GET_PARENT_PATH"_s, args)
788 ? cmCMakePath{ args.front() }.GetParentPath().String()
792 [](cmGeneratorExpressionContext* ctx,
793 const GeneratorExpressionContent* cnt,
794 Arguments& args) -> std::string {
795 return CheckPathParameters(ctx, cnt, "HAS_ROOT_NAME"_s, args)
796 ? ToString(cmCMakePath{ args.front() }.HasRootName())
797 : std::string{ "0" };
799 { "HAS_ROOT_DIRECTORY"_s,
800 [](cmGeneratorExpressionContext* ctx,
801 const GeneratorExpressionContent* cnt,
802 Arguments& args) -> std::string {
803 return CheckPathParameters(ctx, cnt, "HAS_ROOT_DIRECTORY"_s, args)
804 ? ToString(cmCMakePath{ args.front() }.HasRootDirectory())
805 : std::string{ "0" };
808 [](cmGeneratorExpressionContext* ctx,
809 const GeneratorExpressionContent* cnt,
810 Arguments& args) -> std::string {
811 return CheckPathParameters(ctx, cnt, "HAS_ROOT_PATH"_s, args)
812 ? ToString(cmCMakePath{ args.front() }.HasRootPath())
813 : std::string{ "0" };
816 [](cmGeneratorExpressionContext* ctx,
817 const GeneratorExpressionContent* cnt,
818 Arguments& args) -> std::string {
819 return CheckPathParameters(ctx, cnt, "HAS_FILENAME"_s, args)
820 ? ToString(cmCMakePath{ args.front() }.HasFileName())
821 : std::string{ "0" };
824 [](cmGeneratorExpressionContext* ctx,
825 const GeneratorExpressionContent* cnt,
826 Arguments& args) -> std::string {
827 return CheckPathParameters(ctx, cnt, "HAS_EXTENSION"_s, args) &&
828 !args.front().empty()
829 ? ToString(cmCMakePath{ args.front() }.HasExtension())
830 : std::string{ "0" };
833 [](cmGeneratorExpressionContext* ctx,
834 const GeneratorExpressionContent* cnt,
835 Arguments& args) -> std::string {
836 return CheckPathParameters(ctx, cnt, "HAS_STEM"_s, args)
837 ? ToString(cmCMakePath{ args.front() }.HasStem())
838 : std::string{ "0" };
840 { "HAS_RELATIVE_PART"_s,
841 [](cmGeneratorExpressionContext* ctx,
842 const GeneratorExpressionContent* cnt,
843 Arguments& args) -> std::string {
844 return CheckPathParameters(ctx, cnt, "HAS_RELATIVE_PART"_s, args)
845 ? ToString(cmCMakePath{ args.front() }.HasRelativePath())
846 : std::string{ "0" };
848 { "HAS_PARENT_PATH"_s,
849 [](cmGeneratorExpressionContext* ctx,
850 const GeneratorExpressionContent* cnt,
851 Arguments& args) -> std::string {
852 return CheckPathParameters(ctx, cnt, "HAS_PARENT_PATH"_s, args)
853 ? ToString(cmCMakePath{ args.front() }.HasParentPath())
854 : std::string{ "0" };
857 [](cmGeneratorExpressionContext* ctx,
858 const GeneratorExpressionContent* cnt,
859 Arguments& args) -> std::string {
860 return CheckPathParameters(ctx, cnt, "IS_ABSOLUTE"_s, args)
861 ? ToString(cmCMakePath{ args.front() }.IsAbsolute())
862 : std::string{ "0" };
865 [](cmGeneratorExpressionContext* ctx,
866 const GeneratorExpressionContent* cnt,
867 Arguments& args) -> std::string {
868 return CheckPathParameters(ctx, cnt, "IS_RELATIVE"_s, args)
869 ? ToString(cmCMakePath{ args.front() }.IsRelative())
870 : std::string{ "0" };
873 [](cmGeneratorExpressionContext* ctx,
874 const GeneratorExpressionContent* cnt,
875 Arguments& args) -> std::string {
876 bool normalize = args.front() == "NORMALIZE"_s;
880 if (CheckPathParametersEx(ctx, cnt,
881 normalize ? "IS_PREFIX,NORMALIZE"_s
885 return ToString(cmCMakePath{ args[0] }.Normal().IsPrefix(
886 cmCMakePath{ args[1] }.Normal()));
889 cmCMakePath{ args[0] }.IsPrefix(cmCMakePath{ args[1] }));
891 return std::string{};
894 [](cmGeneratorExpressionContext* ctx,
895 const GeneratorExpressionContent* cnt,
896 Arguments& args) -> std::string {
897 bool normalize = args.front() == "NORMALIZE"_s;
901 if (CheckPathParametersEx(ctx, cnt,
902 normalize ? "CMAKE_PATH,NORMALIZE"_s
906 cmCMakePath{ args.front(), cmCMakePath::auto_format };
907 return normalize ? path.Normal().GenericString()
908 : path.GenericString();
910 return std::string{};
913 [](cmGeneratorExpressionContext* ctx,
914 const GeneratorExpressionContent* cnt,
915 Arguments& args) -> std::string {
916 if (CheckPathParametersEx(ctx, cnt, "APPEND"_s, args.size(), 1,
919 for (const auto& p : args) {
922 return path.String();
924 return std::string{};
926 { "REMOVE_FILENAME"_s,
927 [](cmGeneratorExpressionContext* ctx,
928 const GeneratorExpressionContent* cnt,
929 Arguments& args) -> std::string {
930 return CheckPathParameters(ctx, cnt, "REMOVE_FILENAME"_s, args) &&
931 !args.front().empty()
932 ? cmCMakePath{ args.front() }.RemoveFileName().String()
935 { "REPLACE_FILENAME"_s,
936 [](cmGeneratorExpressionContext* ctx,
937 const GeneratorExpressionContent* cnt,
938 Arguments& args) -> std::string {
939 return CheckPathParameters(ctx, cnt, "REPLACE_FILENAME"_s, args, 2)
940 ? cmCMakePath{ args[0] }
941 .ReplaceFileName(cmCMakePath{ args[1] })
945 { "REMOVE_EXTENSION"_s,
946 [](cmGeneratorExpressionContext* ctx,
947 const GeneratorExpressionContent* cnt,
948 Arguments& args) -> std::string {
949 bool lastOnly = args.front() == "LAST_ONLY"_s;
953 if (CheckPathParametersEx(ctx, cnt,
954 lastOnly ? "REMOVE_EXTENSION,LAST_ONLY"_s
955 : "REMOVE_EXTENSION"_s,
957 if (args.front().empty()) {
958 return std::string{};
961 ? cmCMakePath{ args.front() }.RemoveExtension().String()
962 : cmCMakePath{ args.front() }.RemoveWideExtension().String();
964 return std::string{};
966 { "REPLACE_EXTENSION"_s,
967 [](cmGeneratorExpressionContext* ctx,
968 const GeneratorExpressionContent* cnt,
969 Arguments& args) -> std::string {
970 bool lastOnly = args.front() == "LAST_ONLY"_s;
974 if (CheckPathParametersEx(ctx, cnt,
976 ? "REPLACE_EXTENSION,LAST_ONLY"_s
977 : "REPLACE_EXTENSION"_s,
980 return cmCMakePath{ args[0] }
981 .ReplaceExtension(cmCMakePath{ args[1] })
984 return cmCMakePath{ args[0] }
985 .ReplaceWideExtension(cmCMakePath{ args[1] })
988 return std::string{};
991 [](cmGeneratorExpressionContext* ctx,
992 const GeneratorExpressionContent* cnt,
993 Arguments& args) -> std::string {
994 return CheckPathParameters(ctx, cnt, "NORMAL_PATH"_s, args) &&
995 !args.front().empty()
996 ? cmCMakePath{ args.front() }.Normal().String()
1000 [](cmGeneratorExpressionContext* ctx,
1001 const GeneratorExpressionContent* cnt,
1002 Arguments& args) -> std::string {
1003 return CheckPathParameters(ctx, cnt, "RELATIVE_PATH"_s, args, 2)
1004 ? cmCMakePath{ args[0] }.Relative(args[1]).String()
1007 { "ABSOLUTE_PATH"_s,
1008 [](cmGeneratorExpressionContext* ctx,
1009 const GeneratorExpressionContent* cnt,
1010 Arguments& args) -> std::string {
1011 bool normalize = args.front() == "NORMALIZE"_s;
1015 if (CheckPathParametersEx(ctx, cnt,
1016 normalize ? "ABSOLUTE_PATH,NORMALIZE"_s
1017 : "ABSOLUTE_PATH"_s,
1019 auto path = cmCMakePath{ args[0] }.Absolute(args[1]);
1020 return normalize ? path.Normal().String() : path.String();
1022 return std::string{};
1026 if (cm::contains(pathCommands, parameters.front())) {
1027 auto args = Arguments{ parameters }.advance(1);
1028 return pathCommands[parameters.front()](context, content, args);
1031 reportError(context, content->GetOriginalExpression(),
1032 cmStrCat(parameters.front(), ": invalid option."));
1033 return std::string{};
1037 static const struct PathEqualNode : public cmGeneratorExpressionNode
1039 PathEqualNode() {} // NOLINT(modernize-use-equals-default)
1041 int NumExpectedParameters() const override { return 2; }
1043 std::string Evaluate(
1044 const std::vector<std::string>& parameters,
1045 cmGeneratorExpressionContext* /*context*/,
1046 const GeneratorExpressionContent* /*content*/,
1047 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1049 return cmCMakePath{ parameters[0] } == cmCMakePath{ parameters[1] } ? "1"
1054 static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode
1056 MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default)
1058 bool AcceptsArbitraryContentParameter() const override { return true; }
1060 std::string Evaluate(
1061 const std::vector<std::string>& parameters,
1062 cmGeneratorExpressionContext* /*context*/,
1063 const GeneratorExpressionContent* /*content*/,
1064 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1066 return cmSystemTools::MakeCidentifier(parameters.front());
1068 } makeCIdentifierNode;
1071 struct CharacterNode : public cmGeneratorExpressionNode
1073 CharacterNode() {} // NOLINT(modernize-use-equals-default)
1075 int NumExpectedParameters() const override { return 0; }
1077 std::string Evaluate(
1078 const std::vector<std::string>& /*parameters*/,
1079 cmGeneratorExpressionContext* /*context*/,
1080 const GeneratorExpressionContent* /*content*/,
1081 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1086 static const CharacterNode<'>'> angle_rNode;
1087 static const CharacterNode<','> commaNode;
1088 static const CharacterNode<';'> semicolonNode;
1090 struct CompilerIdNode : public cmGeneratorExpressionNode
1092 CompilerIdNode(const char* compilerLang)
1093 : CompilerLanguage(compilerLang)
1097 int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1099 std::string Evaluate(
1100 const std::vector<std::string>& parameters,
1101 cmGeneratorExpressionContext* context,
1102 const GeneratorExpressionContent* content,
1103 cmGeneratorExpressionDAGChecker* dagChecker) const override
1105 if (!context->HeadTarget) {
1106 std::ostringstream e;
1107 e << "$<" << this->CompilerLanguage
1108 << "_COMPILER_ID> may only be used with binary targets. It may "
1109 "not be used with add_custom_command or add_custom_target.";
1110 reportError(context, content->GetOriginalExpression(), e.str());
1113 return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
1114 this->CompilerLanguage);
1117 std::string EvaluateWithLanguage(const std::vector<std::string>& parameters,
1118 cmGeneratorExpressionContext* context,
1119 const GeneratorExpressionContent* content,
1120 cmGeneratorExpressionDAGChecker* /*unused*/,
1121 const std::string& lang) const
1123 std::string const& compilerId =
1124 context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
1126 if (parameters.empty()) {
1129 if (compilerId.empty()) {
1130 return parameters.front().empty() ? "1" : "0";
1132 static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$");
1134 for (auto const& param : parameters) {
1136 if (!compilerIdValidator.find(param)) {
1137 reportError(context, content->GetOriginalExpression(),
1138 "Expression syntax not recognized.");
1139 return std::string();
1142 if (strcmp(param.c_str(), compilerId.c_str()) == 0) {
1146 if (cmsysString_strcasecmp(param.c_str(), compilerId.c_str()) == 0) {
1147 switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) {
1148 case cmPolicies::WARN: {
1149 context->LG->GetCMakeInstance()->IssueMessage(
1150 MessageType::AUTHOR_WARNING,
1151 cmPolicies::GetPolicyWarning(cmPolicies::CMP0044),
1152 context->Backtrace);
1155 case cmPolicies::OLD:
1157 case cmPolicies::NEW:
1158 case cmPolicies::REQUIRED_ALWAYS:
1159 case cmPolicies::REQUIRED_IF_USED:
1167 const char* const CompilerLanguage;
1170 static const CompilerIdNode cCompilerIdNode("C"), cxxCompilerIdNode("CXX"),
1171 cudaCompilerIdNode("CUDA"), objcCompilerIdNode("OBJC"),
1172 objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran"),
1173 hipCompilerIdNode("HIP"), ispcCompilerIdNode("ISPC");
1175 struct CompilerVersionNode : public cmGeneratorExpressionNode
1177 CompilerVersionNode(const char* compilerLang)
1178 : CompilerLanguage(compilerLang)
1182 int NumExpectedParameters() const override { return OneOrZeroParameters; }
1184 std::string Evaluate(
1185 const std::vector<std::string>& parameters,
1186 cmGeneratorExpressionContext* context,
1187 const GeneratorExpressionContent* content,
1188 cmGeneratorExpressionDAGChecker* dagChecker) const override
1190 if (!context->HeadTarget) {
1191 std::ostringstream e;
1192 e << "$<" << this->CompilerLanguage
1193 << "_COMPILER_VERSION> may only be used with binary targets. It "
1194 "may not be used with add_custom_command or add_custom_target.";
1195 reportError(context, content->GetOriginalExpression(), e.str());
1198 return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
1199 this->CompilerLanguage);
1202 std::string EvaluateWithLanguage(const std::vector<std::string>& parameters,
1203 cmGeneratorExpressionContext* context,
1204 const GeneratorExpressionContent* content,
1205 cmGeneratorExpressionDAGChecker* /*unused*/,
1206 const std::string& lang) const
1208 std::string const& compilerVersion =
1209 context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
1210 "_COMPILER_VERSION");
1211 if (parameters.empty()) {
1212 return compilerVersion;
1215 static cmsys::RegularExpression compilerIdValidator("^[0-9\\.]*$");
1216 if (!compilerIdValidator.find(parameters.front())) {
1217 reportError(context, content->GetOriginalExpression(),
1218 "Expression syntax not recognized.");
1221 if (compilerVersion.empty()) {
1222 return parameters.front().empty() ? "1" : "0";
1225 return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL,
1226 parameters.front(), compilerVersion)
1231 const char* const CompilerLanguage;
1234 static const CompilerVersionNode cCompilerVersionNode("C"),
1235 cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"),
1236 objcCompilerVersionNode("OBJC"), objcxxCompilerVersionNode("OBJCXX"),
1237 fortranCompilerVersionNode("Fortran"), ispcCompilerVersionNode("ISPC"),
1238 hipCompilerVersionNode("HIP");
1240 struct PlatformIdNode : public cmGeneratorExpressionNode
1242 PlatformIdNode() {} // NOLINT(modernize-use-equals-default)
1244 int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1246 std::string Evaluate(
1247 const std::vector<std::string>& parameters,
1248 cmGeneratorExpressionContext* context,
1249 const GeneratorExpressionContent* /*content*/,
1250 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1252 std::string const& platformId =
1253 context->LG->GetMakefile()->GetSafeDefinition("CMAKE_SYSTEM_NAME");
1254 if (parameters.empty()) {
1258 if (platformId.empty()) {
1259 return parameters.front().empty() ? "1" : "0";
1262 for (auto const& param : parameters) {
1263 if (param == platformId) {
1270 static struct PlatformIdNode platformIdNode;
1272 template <cmSystemTools::CompareOp Op>
1273 struct VersionNode : public cmGeneratorExpressionNode
1275 VersionNode() {} // NOLINT(modernize-use-equals-default)
1277 int NumExpectedParameters() const override { return 2; }
1279 std::string Evaluate(
1280 const std::vector<std::string>& parameters,
1281 cmGeneratorExpressionContext* /*context*/,
1282 const GeneratorExpressionContent* /*content*/,
1283 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1285 return cmSystemTools::VersionCompare(Op, parameters.front(), parameters[1])
1291 static const VersionNode<cmSystemTools::OP_GREATER> versionGreaterNode;
1292 static const VersionNode<cmSystemTools::OP_GREATER_EQUAL> versionGreaterEqNode;
1293 static const VersionNode<cmSystemTools::OP_LESS> versionLessNode;
1294 static const VersionNode<cmSystemTools::OP_LESS_EQUAL> versionLessEqNode;
1295 static const VersionNode<cmSystemTools::OP_EQUAL> versionEqualNode;
1297 static const struct LinkOnlyNode : public cmGeneratorExpressionNode
1299 LinkOnlyNode() {} // NOLINT(modernize-use-equals-default)
1301 std::string Evaluate(
1302 const std::vector<std::string>& parameters,
1303 cmGeneratorExpressionContext* context,
1304 const GeneratorExpressionContent* content,
1305 cmGeneratorExpressionDAGChecker* dagChecker) const override
1308 reportError(context, content->GetOriginalExpression(),
1309 "$<LINK_ONLY:...> may only be used for linking");
1310 return std::string();
1312 if (!dagChecker->GetTransitivePropertiesOnly()) {
1313 return parameters.front();
1315 return std::string();
1319 static const struct ConfigurationNode : public cmGeneratorExpressionNode
1321 ConfigurationNode() {} // NOLINT(modernize-use-equals-default)
1323 int NumExpectedParameters() const override { return 0; }
1325 std::string Evaluate(
1326 const std::vector<std::string>& /*parameters*/,
1327 cmGeneratorExpressionContext* context,
1328 const GeneratorExpressionContent* /*content*/,
1329 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1331 context->HadContextSensitiveCondition = true;
1332 return context->Config;
1334 } configurationNode;
1336 static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
1338 ConfigurationTestNode() {} // NOLINT(modernize-use-equals-default)
1340 int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1342 std::string Evaluate(
1343 const std::vector<std::string>& parameters,
1344 cmGeneratorExpressionContext* context,
1345 const GeneratorExpressionContent* content,
1346 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1348 if (parameters.empty()) {
1349 return configurationNode.Evaluate(parameters, context, content, nullptr);
1351 static cmsys::RegularExpression configValidator("^[A-Za-z0-9_]*$");
1352 if (!configValidator.find(parameters.front())) {
1353 reportError(context, content->GetOriginalExpression(),
1354 "Expression syntax not recognized.");
1355 return std::string();
1357 context->HadContextSensitiveCondition = true;
1358 for (auto const& param : parameters) {
1359 if (context->Config.empty()) {
1360 if (param.empty()) {
1363 } else if (cmsysString_strcasecmp(param.c_str(),
1364 context->Config.c_str()) == 0) {
1369 if (context->CurrentTarget && context->CurrentTarget->IsImported()) {
1370 cmValue loc = nullptr;
1371 cmValue imp = nullptr;
1373 if (context->CurrentTarget->Target->GetMappedConfig(context->Config, loc,
1375 // This imported target has an appropriate location
1376 // for this (possibly mapped) config.
1377 // Check if there is a proper config mapping for the tested config.
1378 std::vector<std::string> mappedConfigs;
1379 std::string mapProp = cmStrCat(
1380 "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(context->Config));
1381 if (cmValue mapValue = context->CurrentTarget->GetProperty(mapProp)) {
1382 cmExpandList(cmSystemTools::UpperCase(*mapValue), mappedConfigs);
1384 for (auto const& param : parameters) {
1385 if (cm::contains(mappedConfigs, cmSystemTools::UpperCase(param))) {
1394 } configurationTestNode;
1396 static const struct JoinNode : public cmGeneratorExpressionNode
1398 JoinNode() {} // NOLINT(modernize-use-equals-default)
1400 int NumExpectedParameters() const override { return 2; }
1402 bool AcceptsArbitraryContentParameter() const override { return true; }
1404 std::string Evaluate(
1405 const std::vector<std::string>& parameters,
1406 cmGeneratorExpressionContext* /*context*/,
1407 const GeneratorExpressionContent* /*content*/,
1408 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
1410 std::vector<std::string> list = cmExpandedList(parameters.front());
1411 return cmJoin(list, parameters[1]);
1415 static const struct CompileLanguageNode : public cmGeneratorExpressionNode
1417 CompileLanguageNode() {} // NOLINT(modernize-use-equals-default)
1419 int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1421 std::string Evaluate(
1422 const std::vector<std::string>& parameters,
1423 cmGeneratorExpressionContext* context,
1424 const GeneratorExpressionContent* content,
1425 cmGeneratorExpressionDAGChecker* dagChecker) const override
1427 if (context->Language.empty() &&
1428 (!dagChecker || !dagChecker->EvaluatingCompileExpression())) {
1430 context, content->GetOriginalExpression(),
1431 "$<COMPILE_LANGUAGE:...> may only be used to specify include "
1432 "directories, compile definitions, compile options, and to evaluate "
1433 "components of the file(GENERATE) command.");
1434 return std::string();
1437 cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1438 std::string genName = gg->GetName();
1439 if (genName.find("Makefiles") == std::string::npos &&
1440 genName.find("Ninja") == std::string::npos &&
1441 genName.find("Visual Studio") == std::string::npos &&
1442 genName.find("Xcode") == std::string::npos &&
1443 genName.find("Watcom WMake") == std::string::npos) {
1444 reportError(context, content->GetOriginalExpression(),
1445 "$<COMPILE_LANGUAGE:...> not supported for this generator.");
1446 return std::string();
1448 if (parameters.empty()) {
1449 return context->Language;
1452 for (auto const& param : parameters) {
1453 if (context->Language == param) {
1461 static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode
1463 CompileLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default)
1465 int NumExpectedParameters() const override { return TwoOrMoreParameters; }
1467 std::string Evaluate(
1468 const std::vector<std::string>& parameters,
1469 cmGeneratorExpressionContext* context,
1470 const GeneratorExpressionContent* content,
1471 cmGeneratorExpressionDAGChecker* dagChecker) const override
1473 if (!context->HeadTarget ||
1474 (context->Language.empty() &&
1475 (!dagChecker || !dagChecker->EvaluatingCompileExpression()))) {
1476 // reportError(context, content->GetOriginalExpression(), "");
1478 context, content->GetOriginalExpression(),
1479 "$<COMPILE_LANG_AND_ID:lang,id> may only be used with binary targets "
1480 "to specify include directories, compile definitions, and compile "
1481 "options. It may not be used with the add_custom_command, "
1482 "add_custom_target, or file(GENERATE) commands.");
1483 return std::string();
1485 cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1486 std::string genName = gg->GetName();
1487 if (genName.find("Makefiles") == std::string::npos &&
1488 genName.find("Ninja") == std::string::npos &&
1489 genName.find("Visual Studio") == std::string::npos &&
1490 genName.find("Xcode") == std::string::npos &&
1491 genName.find("Watcom WMake") == std::string::npos) {
1493 context, content->GetOriginalExpression(),
1494 "$<COMPILE_LANG_AND_ID:lang,id> not supported for this generator.");
1495 return std::string();
1498 const std::string& lang = context->Language;
1499 if (lang == parameters.front()) {
1500 std::vector<std::string> idParameter((parameters.cbegin() + 1),
1502 return CompilerIdNode{ lang.c_str() }.EvaluateWithLanguage(
1503 idParameter, context, content, dagChecker, lang);
1507 } languageAndIdNode;
1509 static const struct LinkLanguageNode : public cmGeneratorExpressionNode
1511 LinkLanguageNode() {} // NOLINT(modernize-use-equals-default)
1513 int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1515 std::string Evaluate(
1516 const std::vector<std::string>& parameters,
1517 cmGeneratorExpressionContext* context,
1518 const GeneratorExpressionContent* content,
1519 cmGeneratorExpressionDAGChecker* dagChecker) const override
1521 if (!context->HeadTarget || !dagChecker ||
1522 !(dagChecker->EvaluatingLinkExpression() ||
1523 dagChecker->EvaluatingLinkLibraries())) {
1524 reportError(context, content->GetOriginalExpression(),
1525 "$<LINK_LANGUAGE:...> may only be used with binary targets "
1526 "to specify link libraries, link directories, link options "
1527 "and link depends.");
1528 return std::string();
1530 if (dagChecker->EvaluatingLinkLibraries() && parameters.empty()) {
1532 context, content->GetOriginalExpression(),
1533 "$<LINK_LANGUAGE> is not supported in link libraries expression.");
1534 return std::string();
1537 cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1538 std::string genName = gg->GetName();
1539 if (genName.find("Makefiles") == std::string::npos &&
1540 genName.find("Ninja") == std::string::npos &&
1541 genName.find("Visual Studio") == std::string::npos &&
1542 genName.find("Xcode") == std::string::npos &&
1543 genName.find("Watcom WMake") == std::string::npos) {
1544 reportError(context, content->GetOriginalExpression(),
1545 "$<LINK_LANGUAGE:...> not supported for this generator.");
1546 return std::string();
1549 if (dagChecker->EvaluatingLinkLibraries()) {
1550 context->HadHeadSensitiveCondition = true;
1551 context->HadLinkLanguageSensitiveCondition = true;
1554 if (parameters.empty()) {
1555 return context->Language;
1558 for (auto const& param : parameters) {
1559 if (context->Language == param) {
1570 static std::string Evaluate(const std::vector<std::string>& parameters,
1571 cmGeneratorExpressionContext* context,
1572 const GeneratorExpressionContent* content,
1573 const std::string& lang)
1575 std::string const& linkerId =
1576 context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
1578 if (parameters.empty()) {
1581 if (linkerId.empty()) {
1582 return parameters.front().empty() ? "1" : "0";
1584 static cmsys::RegularExpression linkerIdValidator("^[A-Za-z0-9_]*$");
1586 for (auto const& param : parameters) {
1587 if (!linkerIdValidator.find(param)) {
1588 reportError(context, content->GetOriginalExpression(),
1589 "Expression syntax not recognized.");
1590 return std::string();
1593 if (param == linkerId) {
1602 static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
1604 LinkLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default)
1606 int NumExpectedParameters() const override { return TwoOrMoreParameters; }
1608 std::string Evaluate(
1609 const std::vector<std::string>& parameters,
1610 cmGeneratorExpressionContext* context,
1611 const GeneratorExpressionContent* content,
1612 cmGeneratorExpressionDAGChecker* dagChecker) const override
1614 if (!context->HeadTarget || !dagChecker ||
1615 !(dagChecker->EvaluatingLinkExpression() ||
1616 dagChecker->EvaluatingLinkLibraries())) {
1618 context, content->GetOriginalExpression(),
1619 "$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets "
1620 "to specify link libraries, link directories, link options, and link "
1622 return std::string();
1625 cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
1626 std::string genName = gg->GetName();
1627 if (genName.find("Makefiles") == std::string::npos &&
1628 genName.find("Ninja") == std::string::npos &&
1629 genName.find("Visual Studio") == std::string::npos &&
1630 genName.find("Xcode") == std::string::npos &&
1631 genName.find("Watcom WMake") == std::string::npos) {
1633 context, content->GetOriginalExpression(),
1634 "$<LINK_LANG_AND_ID:lang,id> not supported for this generator.");
1635 return std::string();
1638 if (dagChecker->EvaluatingLinkLibraries()) {
1639 context->HadHeadSensitiveCondition = true;
1640 context->HadLinkLanguageSensitiveCondition = true;
1643 const std::string& lang = context->Language;
1644 if (lang == parameters.front()) {
1645 std::vector<std::string> idParameter((parameters.cbegin() + 1),
1647 return LinkerId::Evaluate(idParameter, context, content, lang);
1651 } linkLanguageAndIdNode;
1653 static const struct LinkLibraryNode : public cmGeneratorExpressionNode
1655 LinkLibraryNode() {} // NOLINT(modernize-use-equals-default)
1657 int NumExpectedParameters() const override { return OneOrMoreParameters; }
1659 std::string Evaluate(
1660 const std::vector<std::string>& parameters,
1661 cmGeneratorExpressionContext* context,
1662 const GeneratorExpressionContent* content,
1663 cmGeneratorExpressionDAGChecker* dagChecker) const override
1665 using ForGenex = cmGeneratorExpressionDAGChecker::ForGenex;
1667 if (!context->HeadTarget || !dagChecker ||
1668 !dagChecker->EvaluatingLinkLibraries(nullptr,
1669 ForGenex::LINK_LIBRARY)) {
1670 reportError(context, content->GetOriginalExpression(),
1671 "$<LINK_LIBRARY:...> may only be used with binary targets "
1672 "to specify link libraries through 'LINK_LIBRARIES', "
1673 "'INTERFACE_LINK_LIBRARIES', and "
1674 "'INTERFACE_LINK_LIBRARIES_DIRECT' properties.");
1675 return std::string();
1678 std::vector<std::string> list;
1679 cmExpandLists(parameters.begin(), parameters.end(), list);
1682 context, content->GetOriginalExpression(),
1683 "$<LINK_LIBRARY:...> expects a feature name as first argument.");
1684 return std::string();
1686 if (list.size() == 1) {
1687 // no libraries specified, ignore this genex
1688 return std::string();
1691 static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$");
1692 auto const& feature = list.front();
1693 if (!featureNameValidator.find(feature)) {
1694 reportError(context, content->GetOriginalExpression(),
1695 cmStrCat("The feature name '", feature,
1696 "' contains invalid characters."));
1697 return std::string();
1700 const auto LL_BEGIN = cmStrCat("<LINK_LIBRARY:", feature, '>');
1701 const auto LL_END = cmStrCat("</LINK_LIBRARY:", feature, '>');
1703 // filter out $<LINK_LIBRARY:..> tags with same feature
1704 // and raise an error for any different feature
1705 cm::erase_if(list, [&](const std::string& item) -> bool {
1706 return item == LL_BEGIN || item == LL_END;
1709 std::find_if(list.cbegin() + 1, list.cend(),
1710 [&feature](const std::string& item) -> bool {
1711 return cmHasPrefix(item, "<LINK_LIBRARY:"_s) &&
1712 item.substr(14, item.find('>', 14) - 14) != feature;
1714 if (it != list.cend()) {
1716 context, content->GetOriginalExpression(),
1717 "$<LINK_LIBRARY:...> with different features cannot be nested.");
1718 return std::string();
1720 // $<LINK_GROUP:...> must not appear as part of $<LINK_LIBRARY:...>
1721 it = std::find_if(list.cbegin() + 1, list.cend(),
1722 [](const std::string& item) -> bool {
1723 return cmHasPrefix(item, "<LINK_GROUP:"_s);
1725 if (it != list.cend()) {
1726 reportError(context, content->GetOriginalExpression(),
1727 "$<LINK_GROUP:...> cannot be nested inside a "
1728 "$<LINK_LIBRARY:...> expression.");
1729 return std::string();
1732 list.front() = LL_BEGIN;
1733 list.push_back(LL_END);
1735 return cmJoin(list, ";"_s);
1739 static const struct LinkGroupNode : public cmGeneratorExpressionNode
1741 LinkGroupNode() {} // NOLINT(modernize-use-equals-default)
1743 int NumExpectedParameters() const override { return OneOrMoreParameters; }
1745 std::string Evaluate(
1746 const std::vector<std::string>& parameters,
1747 cmGeneratorExpressionContext* context,
1748 const GeneratorExpressionContent* content,
1749 cmGeneratorExpressionDAGChecker* dagChecker) const override
1751 using ForGenex = cmGeneratorExpressionDAGChecker::ForGenex;
1753 if (!context->HeadTarget || !dagChecker ||
1754 !dagChecker->EvaluatingLinkLibraries(nullptr, ForGenex::LINK_GROUP)) {
1756 context, content->GetOriginalExpression(),
1757 "$<LINK_GROUP:...> may only be used with binary targets "
1758 "to specify group of link libraries through 'LINK_LIBRARIES', "
1759 "'INTERFACE_LINK_LIBRARIES', and "
1760 "'INTERFACE_LINK_LIBRARIES_DIRECT' properties.");
1761 return std::string();
1764 std::vector<std::string> list;
1765 cmExpandLists(parameters.begin(), parameters.end(), list);
1768 context, content->GetOriginalExpression(),
1769 "$<LINK_GROUP:...> expects a feature name as first argument.");
1770 return std::string();
1772 // $<LINK_GROUP:..> cannot be nested
1773 if (std::find_if(list.cbegin(), list.cend(),
1774 [](const std::string& item) -> bool {
1775 return cmHasPrefix(item, "<LINK_GROUP"_s);
1776 }) != list.cend()) {
1777 reportError(context, content->GetOriginalExpression(),
1778 "$<LINK_GROUP:...> cannot be nested.");
1779 return std::string();
1781 if (list.size() == 1) {
1782 // no libraries specified, ignore this genex
1783 return std::string();
1786 static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$");
1787 auto const& feature = list.front();
1788 if (!featureNameValidator.find(feature)) {
1789 reportError(context, content->GetOriginalExpression(),
1790 cmStrCat("The feature name '", feature,
1791 "' contains invalid characters."));
1792 return std::string();
1795 const auto LG_BEGIN = cmStrCat(
1796 "<LINK_GROUP:", feature, ':',
1797 cmJoin(cmRange<decltype(list.cbegin())>(list.cbegin() + 1, list.cend()),
1800 const auto LG_END = cmStrCat("</LINK_GROUP:", feature, '>');
1802 list.front() = LG_BEGIN;
1803 list.push_back(LG_END);
1805 return cmJoin(list, ";"_s);
1809 static const struct HostLinkNode : public cmGeneratorExpressionNode
1811 HostLinkNode() {} // NOLINT(modernize-use-equals-default)
1813 int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1815 std::string Evaluate(
1816 const std::vector<std::string>& parameters,
1817 cmGeneratorExpressionContext* context,
1818 const GeneratorExpressionContent* content,
1819 cmGeneratorExpressionDAGChecker* dagChecker) const override
1821 if (!context->HeadTarget || !dagChecker ||
1822 !dagChecker->EvaluatingLinkOptionsExpression()) {
1823 reportError(context, content->GetOriginalExpression(),
1824 "$<HOST_LINK:...> may only be used with binary targets "
1825 "to specify link options.");
1826 return std::string();
1829 return context->HeadTarget->IsDeviceLink() ? std::string()
1830 : cmJoin(parameters, ";");
1834 static const struct DeviceLinkNode : public cmGeneratorExpressionNode
1836 DeviceLinkNode() {} // NOLINT(modernize-use-equals-default)
1838 int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
1840 std::string Evaluate(
1841 const std::vector<std::string>& parameters,
1842 cmGeneratorExpressionContext* context,
1843 const GeneratorExpressionContent* content,
1844 cmGeneratorExpressionDAGChecker* dagChecker) const override
1846 if (!context->HeadTarget || !dagChecker ||
1847 !dagChecker->EvaluatingLinkOptionsExpression()) {
1848 reportError(context, content->GetOriginalExpression(),
1849 "$<DEVICE_LINK:...> may only be used with binary targets "
1850 "to specify link options.");
1851 return std::string();
1854 if (context->HeadTarget->IsDeviceLink()) {
1855 std::vector<std::string> list;
1856 cmExpandLists(parameters.begin(), parameters.end(), list);
1857 const auto DL_BEGIN = "<DEVICE_LINK>"_s;
1858 const auto DL_END = "</DEVICE_LINK>"_s;
1859 cm::erase_if(list, [&](const std::string& item) {
1860 return item == DL_BEGIN || item == DL_END;
1863 list.insert(list.begin(), static_cast<std::string>(DL_BEGIN));
1864 list.push_back(static_cast<std::string>(DL_END));
1866 return cmJoin(list, ";");
1869 return std::string();
1873 static std::string getLinkedTargetsContent(
1874 cmGeneratorTarget const* target, std::string const& prop,
1875 cmGeneratorExpressionContext* context,
1876 cmGeneratorExpressionDAGChecker* dagChecker)
1879 if (cmLinkImplementationLibraries const* impl =
1880 target->GetLinkImplementationLibraries(
1881 context->Config, cmGeneratorTarget::LinkInterfaceFor::Usage)) {
1882 for (cmLinkImplItem const& lib : impl->Libraries) {
1884 // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
1885 // caller's property and hand-evaluate it as if it were compiled.
1886 // Create a context as cmCompiledGeneratorExpression::Evaluate does.
1887 cmGeneratorExpressionContext libContext(
1888 target->GetLocalGenerator(), context->Config, context->Quiet, target,
1889 target, context->EvaluateForBuildsystem, lib.Backtrace,
1891 std::string libResult =
1892 lib.Target->EvaluateInterfaceProperty(prop, &libContext, dagChecker);
1893 if (!libResult.empty()) {
1894 if (result.empty()) {
1895 result = std::move(libResult);
1897 result.reserve(result.size() + 1 + libResult.size());
1899 result += libResult;
1908 static const struct TargetPropertyNode : public cmGeneratorExpressionNode
1910 TargetPropertyNode() {} // NOLINT(modernize-use-equals-default)
1912 // This node handles errors on parameter count itself.
1913 int NumExpectedParameters() const override { return OneOrMoreParameters; }
1915 static const char* GetErrorText(std::string const& targetName,
1916 std::string const& propertyName)
1918 static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
1919 if (targetName.empty() && propertyName.empty()) {
1920 return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty "
1921 "target name and property name.";
1923 if (targetName.empty()) {
1924 return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty "
1927 if (!cmGeneratorExpression::IsValidTargetName(targetName)) {
1928 if (!propertyNameValidator.find(propertyName)) {
1929 return "Target name and property name not supported.";
1931 return "Target name not supported.";
1936 std::string Evaluate(
1937 const std::vector<std::string>& parameters,
1938 cmGeneratorExpressionContext* context,
1939 const GeneratorExpressionContent* content,
1940 cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
1942 static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
1944 cmGeneratorTarget const* target = nullptr;
1945 std::string targetName;
1946 std::string propertyName;
1948 if (parameters.size() == 2) {
1949 targetName = parameters[0];
1950 propertyName = parameters[1];
1952 if (const char* e = GetErrorText(targetName, propertyName)) {
1953 reportError(context, content->GetOriginalExpression(), e);
1954 return std::string();
1956 if (propertyName == "ALIASED_TARGET"_s) {
1957 if (context->LG->GetMakefile()->IsAlias(targetName)) {
1958 if (cmGeneratorTarget* tgt =
1959 context->LG->FindGeneratorTargetToUse(targetName)) {
1960 return tgt->GetName();
1963 return std::string();
1965 if (propertyName == "ALIAS_GLOBAL"_s) {
1966 if (context->LG->GetMakefile()->IsAlias(targetName)) {
1967 return context->LG->GetGlobalGenerator()->IsAlias(targetName)
1971 return std::string();
1973 target = context->LG->FindGeneratorTargetToUse(targetName);
1976 std::ostringstream e;
1977 e << "Target \"" << targetName << "\" not found.";
1978 reportError(context, content->GetOriginalExpression(), e.str());
1979 return std::string();
1981 context->AllTargets.insert(target);
1983 } else if (parameters.size() == 1) {
1984 target = context->HeadTarget;
1985 propertyName = parameters[0];
1987 // Keep track of the properties seen while processing.
1988 // The evaluation of the LINK_LIBRARIES generator expressions
1989 // will check this to ensure that properties have one consistent
1990 // value for all evaluations.
1991 context->SeenTargetProperties.insert(propertyName);
1993 context->HadHeadSensitiveCondition = true;
1996 context, content->GetOriginalExpression(),
1997 "$<TARGET_PROPERTY:prop> may only be used with binary targets. "
1998 "It may not be used with add_custom_command or add_custom_target. "
2000 "Specify the target to read a property from using the "
2001 "$<TARGET_PROPERTY:tgt,prop> signature instead.");
2002 return std::string();
2006 context, content->GetOriginalExpression(),
2007 "$<TARGET_PROPERTY:...> expression requires one or two parameters");
2008 return std::string();
2011 if (propertyName == "SOURCES") {
2012 context->SourceSensitiveTargets.insert(target);
2015 if (propertyName.empty()) {
2017 context, content->GetOriginalExpression(),
2018 "$<TARGET_PROPERTY:...> expression requires a non-empty property "
2020 return std::string();
2023 if (!propertyNameValidator.find(propertyName)) {
2024 ::reportError(context, content->GetOriginalExpression(),
2025 "Property name not supported.");
2026 return std::string();
2031 if (propertyName == "LINKER_LANGUAGE") {
2032 if (target->LinkLanguagePropagatesToDependents() && dagCheckerParent &&
2033 (dagCheckerParent->EvaluatingLinkLibraries() ||
2034 dagCheckerParent->EvaluatingSources())) {
2036 context, content->GetOriginalExpression(),
2037 "LINKER_LANGUAGE target property can not be used while evaluating "
2038 "link libraries for a static library");
2039 return std::string();
2041 return target->GetLinkerLanguage(context->Config);
2044 std::string interfacePropertyName;
2045 bool isInterfaceProperty = false;
2047 #define POPULATE_INTERFACE_PROPERTY_NAME(prop) \
2048 if (propertyName == #prop) { \
2049 interfacePropertyName = "INTERFACE_" #prop; \
2050 } else if (propertyName == "INTERFACE_" #prop) { \
2051 interfacePropertyName = "INTERFACE_" #prop; \
2052 isInterfaceProperty = true; \
2055 CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(POPULATE_INTERFACE_PROPERTY_NAME)
2056 // Note that the above macro terminates with an else
2057 /* else */ if (cmHasLiteralPrefix(propertyName, "COMPILE_DEFINITIONS_")) {
2058 cmPolicies::PolicyStatus polSt =
2059 context->LG->GetPolicyStatus(cmPolicies::CMP0043);
2060 if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
2061 interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS";
2064 #undef POPULATE_INTERFACE_PROPERTY_NAME
2066 bool evaluatingLinkLibraries = false;
2068 if (dagCheckerParent) {
2069 if (dagCheckerParent->EvaluatingGenexExpression() ||
2070 dagCheckerParent->EvaluatingPICExpression()) {
2071 // No check required.
2072 } else if (dagCheckerParent->EvaluatingLinkLibraries()) {
2073 evaluatingLinkLibraries = true;
2074 if (!interfacePropertyName.empty()) {
2076 context, content->GetOriginalExpression(),
2077 "$<TARGET_PROPERTY:...> expression in link libraries "
2078 "evaluation depends on target property which is transitive "
2079 "over the link libraries, creating a recursion.");
2080 return std::string();
2083 #define ASSERT_TRANSITIVE_PROPERTY_METHOD(METHOD) dagCheckerParent->METHOD() ||
2084 assert(CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(
2085 ASSERT_TRANSITIVE_PROPERTY_METHOD) false); // NOLINT(clang-tidy)
2086 #undef ASSERT_TRANSITIVE_PROPERTY_METHOD
2090 if (isInterfaceProperty) {
2091 return cmGeneratorExpression::StripEmptyListElements(
2092 target->EvaluateInterfaceProperty(propertyName, context,
2096 cmGeneratorExpressionDAGChecker dagChecker(
2097 context->Backtrace, target, propertyName, content, dagCheckerParent);
2099 switch (dagChecker.Check()) {
2100 case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
2101 dagChecker.ReportError(context, content->GetOriginalExpression());
2102 return std::string();
2103 case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
2104 // No error. We just skip cyclic references.
2105 return std::string();
2106 case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
2107 // We handle transitive properties above. For non-transitive
2108 // properties we accept repeats anyway.
2109 case cmGeneratorExpressionDAGChecker::DAG:
2114 bool haveProp = false;
2115 if (cmValue p = target->GetProperty(propertyName)) {
2118 } else if (evaluatingLinkLibraries) {
2119 return std::string();
2122 if (!haveProp && !target->IsImported() &&
2123 target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
2124 if (target->IsLinkInterfaceDependentBoolProperty(propertyName,
2126 context->HadContextSensitiveCondition = true;
2127 return target->GetLinkInterfaceDependentBoolProperty(propertyName,
2132 if (target->IsLinkInterfaceDependentStringProperty(propertyName,
2134 context->HadContextSensitiveCondition = true;
2135 const char* propContent =
2136 target->GetLinkInterfaceDependentStringProperty(propertyName,
2138 return propContent ? propContent : "";
2140 if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName,
2142 context->HadContextSensitiveCondition = true;
2143 const char* propContent =
2144 target->GetLinkInterfaceDependentNumberMinProperty(propertyName,
2146 return propContent ? propContent : "";
2148 if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName,
2150 context->HadContextSensitiveCondition = true;
2151 const char* propContent =
2152 target->GetLinkInterfaceDependentNumberMaxProperty(propertyName,
2154 return propContent ? propContent : "";
2158 if (!target->IsImported() && dagCheckerParent &&
2159 !dagCheckerParent->EvaluatingLinkLibraries()) {
2160 if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName,
2162 context->HadContextSensitiveCondition = true;
2163 const char* propContent =
2164 target->GetLinkInterfaceDependentNumberMinProperty(propertyName,
2166 return propContent ? propContent : "";
2168 if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName,
2170 context->HadContextSensitiveCondition = true;
2171 const char* propContent =
2172 target->GetLinkInterfaceDependentNumberMaxProperty(propertyName,
2174 return propContent ? propContent : "";
2178 if (!interfacePropertyName.empty()) {
2179 result = cmGeneratorExpression::StripEmptyListElements(
2180 this->EvaluateDependentExpression(result, context->LG, context, target,
2181 &dagChecker, target));
2182 std::string linkedTargetsContent = getLinkedTargetsContent(
2183 target, interfacePropertyName, context, &dagChecker);
2184 if (!linkedTargetsContent.empty()) {
2185 result += (result.empty() ? "" : ";") + linkedTargetsContent;
2190 } targetPropertyNode;
2192 static const struct TargetNameNode : public cmGeneratorExpressionNode
2194 TargetNameNode() {} // NOLINT(modernize-use-equals-default)
2196 bool GeneratesContent() const override { return true; }
2198 bool AcceptsArbitraryContentParameter() const override { return true; }
2199 bool RequiresLiteralInput() const override { return true; }
2201 std::string Evaluate(
2202 const std::vector<std::string>& parameters,
2203 cmGeneratorExpressionContext* /*context*/,
2204 const GeneratorExpressionContent* /*content*/,
2205 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2207 return parameters.front();
2210 int NumExpectedParameters() const override { return 1; }
2214 static const struct TargetObjectsNode : public cmGeneratorExpressionNode
2216 TargetObjectsNode() {} // NOLINT(modernize-use-equals-default)
2218 std::string Evaluate(
2219 const std::vector<std::string>& parameters,
2220 cmGeneratorExpressionContext* context,
2221 const GeneratorExpressionContent* content,
2222 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2224 std::string const& tgtName = parameters.front();
2225 cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
2227 std::ostringstream e;
2228 e << "Objects of target \"" << tgtName
2229 << "\" referenced but no such target exists.";
2230 reportError(context, content->GetOriginalExpression(), e.str());
2231 return std::string();
2233 cmStateEnums::TargetType type = gt->GetType();
2234 if (type != cmStateEnums::EXECUTABLE &&
2235 type != cmStateEnums::STATIC_LIBRARY &&
2236 type != cmStateEnums::SHARED_LIBRARY &&
2237 type != cmStateEnums::MODULE_LIBRARY &&
2238 type != cmStateEnums::OBJECT_LIBRARY) {
2239 std::ostringstream e;
2240 e << "Objects of target \"" << tgtName
2241 << "\" referenced but is not one of the allowed target types "
2242 << "(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT).";
2243 reportError(context, content->GetOriginalExpression(), e.str());
2244 return std::string();
2246 cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
2249 if (!context->EvaluateForBuildsystem &&
2250 !gt->Target->HasKnownObjectFileLocation(&reason)) {
2251 std::ostringstream e;
2252 e << "The evaluation of the TARGET_OBJECTS generator expression "
2253 "is only suitable for consumption by CMake (limited"
2256 "It is not suitable for writing out elsewhere.";
2257 reportError(context, content->GetOriginalExpression(), e.str());
2258 return std::string();
2262 std::vector<std::string> objects;
2264 if (gt->IsImported()) {
2265 cmValue loc = nullptr;
2266 cmValue imp = nullptr;
2268 if (gt->Target->GetMappedConfig(context->Config, loc, imp, suffix)) {
2269 cmExpandList(*loc, objects);
2271 context->HadContextSensitiveCondition = true;
2273 gt->GetTargetObjectNames(context->Config, objects);
2275 std::string obj_dir;
2276 if (context->EvaluateForBuildsystem && !gg->SupportsCrossConfigs()) {
2277 // Use object file directory with buildsystem placeholder.
2278 obj_dir = gt->ObjectDirectory;
2279 context->HadContextSensitiveCondition =
2280 gt->HasContextDependentSources();
2282 // Use object file directory with per-config location.
2283 obj_dir = gt->GetObjectDirectory(context->Config);
2284 context->HadContextSensitiveCondition = true;
2287 for (std::string& o : objects) {
2288 o = cmStrCat(obj_dir, o);
2292 // Create the cmSourceFile instances in the referencing directory.
2293 cmMakefile* mf = context->LG->GetMakefile();
2294 for (std::string const& o : objects) {
2295 mf->AddTargetObject(tgtName, o);
2298 return cmJoin(objects, ";");
2300 } targetObjectsNode;
2302 static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
2304 TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default)
2306 std::string Evaluate(
2307 const std::vector<std::string>& parameters,
2308 cmGeneratorExpressionContext* context,
2309 const GeneratorExpressionContent* content,
2310 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2312 std::string const& tgtName = parameters.front();
2313 cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
2315 std::ostringstream e;
2316 e << "Objects of target \"" << tgtName
2317 << "\" referenced but no such target exists.";
2318 reportError(context, content->GetOriginalExpression(), e.str());
2319 return std::string();
2321 cmStateEnums::TargetType type = gt->GetType();
2322 if (type != cmStateEnums::EXECUTABLE &&
2323 type != cmStateEnums::SHARED_LIBRARY &&
2324 type != cmStateEnums::MODULE_LIBRARY) {
2325 std::ostringstream e;
2326 e << "Objects of target \"" << tgtName
2327 << "\" referenced but is not one of the allowed target types "
2328 << "(EXECUTABLE, SHARED, MODULE).";
2329 reportError(context, content->GetOriginalExpression(), e.str());
2330 return std::string();
2333 if (auto* cli = gt->GetLinkInformation(context->Config)) {
2334 std::vector<std::string> dllPaths;
2335 auto const& dlls = cli->GetRuntimeDLLs();
2337 for (auto const& dll : dlls) {
2338 if (auto loc = dll->MaybeGetLocation(context->Config)) {
2339 dllPaths.emplace_back(*loc);
2343 return cmJoin(dllPaths, ";");
2348 } targetRuntimeDllsNode;
2350 static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
2352 CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default)
2354 int NumExpectedParameters() const override { return OneOrMoreParameters; }
2356 std::string Evaluate(
2357 const std::vector<std::string>& parameters,
2358 cmGeneratorExpressionContext* context,
2359 const GeneratorExpressionContent* content,
2360 cmGeneratorExpressionDAGChecker* dagChecker) const override
2362 cmGeneratorTarget const* target = context->HeadTarget;
2365 context, content->GetOriginalExpression(),
2366 "$<COMPILE_FEATURE> may only be used with binary targets. It may "
2367 "not be used with add_custom_command or add_custom_target.");
2368 return std::string();
2370 context->HadHeadSensitiveCondition = true;
2372 using LangMap = std::map<std::string, std::vector<std::string>>;
2373 static LangMap availableFeatures;
2375 LangMap testedFeatures;
2376 cmStandardLevelResolver standardResolver(context->LG->GetMakefile());
2377 for (std::string const& p : parameters) {
2380 if (!standardResolver.CompileFeatureKnown(
2381 context->HeadTarget->Target->GetName(), p, lang, &error)) {
2382 reportError(context, content->GetOriginalExpression(), error);
2383 return std::string();
2385 testedFeatures[lang].push_back(p);
2387 if (availableFeatures.find(lang) == availableFeatures.end()) {
2388 cmValue featuresKnown =
2389 standardResolver.CompileFeaturesAvailable(lang, &error);
2390 if (!featuresKnown) {
2391 reportError(context, content->GetOriginalExpression(), error);
2392 return std::string();
2394 cmExpandList(featuresKnown, availableFeatures[lang]);
2398 bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries();
2400 for (auto const& lit : testedFeatures) {
2401 std::vector<std::string> const& langAvailable =
2402 availableFeatures[lit.first];
2403 cmValue standardDefault = context->LG->GetMakefile()->GetDefinition(
2404 "CMAKE_" + lit.first + "_STANDARD_DEFAULT");
2405 for (std::string const& it : lit.second) {
2406 if (!cm::contains(langAvailable, it)) {
2409 if (standardDefault && standardDefault->empty()) {
2410 // This compiler has no notion of language standard levels.
2411 // All features known for the language are always available.
2414 if (!standardResolver.HaveStandardAvailable(target, lit.first,
2415 context->Config, it)) {
2418 target->GetLanguageStandard(lit.first, context->Config);
2420 l = standardDefault;
2423 context->MaxLanguageStandard[target][lit.first] = *l;
2432 } compileFeaturesNode;
2434 static const char* targetPolicyWhitelist[] = {
2436 #define TARGET_POLICY_STRING(POLICY) , #POLICY
2438 CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_STRING)
2440 #undef TARGET_POLICY_STRING
2443 static cmPolicies::PolicyStatus statusForTarget(cmGeneratorTarget const* tgt,
2446 #define RETURN_POLICY(POLICY) \
2447 if (strcmp(policy, #POLICY) == 0) { \
2448 return tgt->GetPolicyStatus##POLICY(); \
2451 CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY)
2453 #undef RETURN_POLICY
2455 assert(false && "Unreachable code. Not a valid policy");
2456 return cmPolicies::WARN;
2459 static cmPolicies::PolicyID policyForString(const char* policy_id)
2461 #define RETURN_POLICY_ID(POLICY_ID) \
2462 if (strcmp(policy_id, #POLICY_ID) == 0) { \
2463 return cmPolicies::POLICY_ID; \
2466 CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY_ID)
2468 #undef RETURN_POLICY_ID
2470 assert(false && "Unreachable code. Not a valid policy");
2471 return cmPolicies::CMP0002;
2474 static const struct TargetPolicyNode : public cmGeneratorExpressionNode
2476 TargetPolicyNode() {} // NOLINT(modernize-use-equals-default)
2478 int NumExpectedParameters() const override { return 1; }
2480 std::string Evaluate(
2481 const std::vector<std::string>& parameters,
2482 cmGeneratorExpressionContext* context,
2483 const GeneratorExpressionContent* content,
2484 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2486 if (!context->HeadTarget) {
2488 context, content->GetOriginalExpression(),
2489 "$<TARGET_POLICY:prop> may only be used with binary targets. It "
2490 "may not be used with add_custom_command or add_custom_target.");
2491 return std::string();
2494 context->HadContextSensitiveCondition = true;
2495 context->HadHeadSensitiveCondition = true;
2497 for (size_t i = 1; i < cm::size(targetPolicyWhitelist); ++i) {
2498 const char* policy = targetPolicyWhitelist[i];
2499 if (parameters.front() == policy) {
2500 cmLocalGenerator* lg = context->HeadTarget->GetLocalGenerator();
2501 switch (statusForTarget(context->HeadTarget, policy)) {
2502 case cmPolicies::WARN:
2504 MessageType::AUTHOR_WARNING,
2505 cmPolicies::GetPolicyWarning(policyForString(policy)));
2507 case cmPolicies::REQUIRED_IF_USED:
2508 case cmPolicies::REQUIRED_ALWAYS:
2509 case cmPolicies::OLD:
2511 case cmPolicies::NEW:
2517 context, content->GetOriginalExpression(),
2518 "$<TARGET_POLICY:prop> may only be used with a limited number of "
2519 "policies. Currently it may be used with the following policies:\n"
2521 #define STRINGIFY_HELPER(X) #X
2522 #define STRINGIFY(X) STRINGIFY_HELPER(X)
2524 #define TARGET_POLICY_LIST_ITEM(POLICY) " * " STRINGIFY(POLICY) "\n"
2526 CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_LIST_ITEM)
2528 #undef TARGET_POLICY_LIST_ITEM
2530 return std::string();
2535 static const struct InstallPrefixNode : public cmGeneratorExpressionNode
2537 InstallPrefixNode() {} // NOLINT(modernize-use-equals-default)
2539 bool GeneratesContent() const override { return true; }
2540 int NumExpectedParameters() const override { return 0; }
2542 std::string Evaluate(
2543 const std::vector<std::string>& /*parameters*/,
2544 cmGeneratorExpressionContext* context,
2545 const GeneratorExpressionContent* content,
2546 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
2548 reportError(context, content->GetOriginalExpression(),
2549 "INSTALL_PREFIX is a marker for install(EXPORT) only. It "
2550 "should never be evaluated.");
2551 return std::string();
2554 } installPrefixNode;
2556 class ArtifactDirTag;
2557 class ArtifactLinkerTag;
2558 class ArtifactNameTag;
2559 class ArtifactPathTag;
2560 class ArtifactPdbTag;
2561 class ArtifactSonameTag;
2562 class ArtifactBundleDirTag;
2563 class ArtifactBundleDirNameTag;
2564 class ArtifactBundleContentDirTag;
2566 template <typename ArtifactT, typename ComponentT>
2567 struct TargetFilesystemArtifactDependency
2569 static void AddDependency(cmGeneratorTarget* target,
2570 cmGeneratorExpressionContext* context)
2572 context->DependTargets.insert(target);
2573 context->AllTargets.insert(target);
2577 struct TargetFilesystemArtifactDependencyCMP0112
2579 static void AddDependency(cmGeneratorTarget* target,
2580 cmGeneratorExpressionContext* context)
2582 context->AllTargets.insert(target);
2583 cmLocalGenerator* lg = context->LG;
2584 switch (target->GetPolicyStatusCMP0112()) {
2585 case cmPolicies::WARN:
2586 if (lg->GetMakefile()->PolicyOptionalWarningEnabled(
2587 "CMAKE_POLICY_WARNING_CMP0112")) {
2589 cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0112),
2590 "\nDependency being added to target:\n \"",
2591 target->GetName(), "\"\n");
2592 lg->GetCMakeInstance()->IssueMessage(MessageType ::AUTHOR_WARNING,
2593 err, context->Backtrace);
2596 case cmPolicies::OLD:
2597 context->DependTargets.insert(target);
2599 case cmPolicies::REQUIRED_IF_USED:
2600 case cmPolicies::REQUIRED_ALWAYS:
2601 case cmPolicies::NEW:
2607 template <typename ArtifactT>
2608 struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactNameTag>
2609 : TargetFilesystemArtifactDependencyCMP0112
2612 template <typename ArtifactT>
2613 struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactDirTag>
2614 : TargetFilesystemArtifactDependencyCMP0112
2618 struct TargetFilesystemArtifactDependency<ArtifactBundleDirTag,
2620 : TargetFilesystemArtifactDependencyCMP0112
2624 struct TargetFilesystemArtifactDependency<ArtifactBundleDirNameTag,
2626 : TargetFilesystemArtifactDependencyCMP0112
2630 struct TargetFilesystemArtifactDependency<ArtifactBundleContentDirTag,
2632 : TargetFilesystemArtifactDependencyCMP0112
2636 template <typename ArtifactT>
2637 struct TargetFilesystemArtifactResultCreator
2639 static std::string Create(cmGeneratorTarget* target,
2640 cmGeneratorExpressionContext* context,
2641 const GeneratorExpressionContent* content);
2645 struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag>
2647 static std::string Create(cmGeneratorTarget* target,
2648 cmGeneratorExpressionContext* context,
2649 const GeneratorExpressionContent* content)
2651 // The target soname file (.so.1).
2652 if (target->IsDLLPlatform()) {
2653 ::reportError(context, content->GetOriginalExpression(),
2654 "TARGET_SONAME_FILE is not allowed "
2655 "for DLL target platforms.");
2656 return std::string();
2658 if (target->GetType() != cmStateEnums::SHARED_LIBRARY) {
2659 ::reportError(context, content->GetOriginalExpression(),
2660 "TARGET_SONAME_FILE is allowed only for "
2661 "SHARED libraries.");
2662 return std::string();
2664 std::string result = cmStrCat(target->GetDirectory(context->Config), '/',
2665 target->GetSOName(context->Config));
2671 struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag>
2673 static std::string Create(cmGeneratorTarget* target,
2674 cmGeneratorExpressionContext* context,
2675 const GeneratorExpressionContent* content)
2677 if (target->IsImported()) {
2678 ::reportError(context, content->GetOriginalExpression(),
2679 "TARGET_PDB_FILE not allowed for IMPORTED targets.");
2680 return std::string();
2683 std::string language = target->GetLinkerLanguage(context->Config);
2685 std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB";
2687 if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) {
2688 ::reportError(context, content->GetOriginalExpression(),
2689 "TARGET_PDB_FILE is not supported by the target linker.");
2690 return std::string();
2693 cmStateEnums::TargetType targetType = target->GetType();
2695 if (targetType != cmStateEnums::SHARED_LIBRARY &&
2696 targetType != cmStateEnums::MODULE_LIBRARY &&
2697 targetType != cmStateEnums::EXECUTABLE) {
2698 ::reportError(context, content->GetOriginalExpression(),
2699 "TARGET_PDB_FILE is allowed only for "
2700 "targets with linker created artifacts.");
2701 return std::string();
2704 std::string result = cmStrCat(target->GetPDBDirectory(context->Config),
2705 '/', target->GetPDBName(context->Config));
2711 struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag>
2713 static std::string Create(cmGeneratorTarget* target,
2714 cmGeneratorExpressionContext* context,
2715 const GeneratorExpressionContent* content)
2717 // The file used to link to the target (.so, .lib, .a).
2718 if (!target->IsLinkable()) {
2719 ::reportError(context, content->GetOriginalExpression(),
2720 "TARGET_LINKER_FILE is allowed only for libraries and "
2721 "executables with ENABLE_EXPORTS.");
2722 return std::string();
2724 cmStateEnums::ArtifactType artifact =
2725 target->HasImportLibrary(context->Config)
2726 ? cmStateEnums::ImportLibraryArtifact
2727 : cmStateEnums::RuntimeBinaryArtifact;
2728 return target->GetFullPath(context->Config, artifact);
2733 struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag>
2735 static std::string Create(cmGeneratorTarget* target,
2736 cmGeneratorExpressionContext* context,
2737 const GeneratorExpressionContent* content)
2739 if (target->IsImported()) {
2740 ::reportError(context, content->GetOriginalExpression(),
2741 "TARGET_BUNDLE_DIR not allowed for IMPORTED targets.");
2742 return std::string();
2744 if (!target->IsBundleOnApple()) {
2745 ::reportError(context, content->GetOriginalExpression(),
2746 "TARGET_BUNDLE_DIR is allowed only for Bundle targets.");
2747 return std::string();
2750 std::string outpath = target->GetDirectory(context->Config) + '/';
2751 return target->BuildBundleDirectory(outpath, context->Config,
2752 cmGeneratorTarget::BundleDirLevel);
2757 struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirNameTag>
2759 static std::string Create(cmGeneratorTarget* target,
2760 cmGeneratorExpressionContext* context,
2761 const GeneratorExpressionContent* content)
2763 if (target->IsImported()) {
2765 context, content->GetOriginalExpression(),
2766 "TARGET_BUNDLE_DIR_NAME not allowed for IMPORTED targets.");
2767 return std::string();
2769 if (!target->IsBundleOnApple()) {
2771 context, content->GetOriginalExpression(),
2772 "TARGET_BUNDLE_DIR_NAME is allowed only for Bundle targets.");
2773 return std::string();
2776 auto level = cmGeneratorTarget::BundleDirLevel;
2777 auto config = context->Config;
2778 if (target->IsAppBundleOnApple()) {
2779 return target->GetAppBundleDirectory(config, level);
2781 if (target->IsFrameworkOnApple()) {
2782 return target->GetFrameworkDirectory(config, level);
2784 if (target->IsCFBundleOnApple()) {
2785 return target->GetCFBundleDirectory(config, level);
2787 return std::string();
2792 struct TargetFilesystemArtifactResultCreator<ArtifactBundleContentDirTag>
2794 static std::string Create(cmGeneratorTarget* target,
2795 cmGeneratorExpressionContext* context,
2796 const GeneratorExpressionContent* content)
2798 if (target->IsImported()) {
2800 context, content->GetOriginalExpression(),
2801 "TARGET_BUNDLE_CONTENT_DIR not allowed for IMPORTED targets.");
2802 return std::string();
2804 if (!target->IsBundleOnApple()) {
2806 context, content->GetOriginalExpression(),
2807 "TARGET_BUNDLE_CONTENT_DIR is allowed only for Bundle targets.");
2808 return std::string();
2811 std::string outpath = target->GetDirectory(context->Config) + '/';
2812 return target->BuildBundleDirectory(outpath, context->Config,
2813 cmGeneratorTarget::ContentLevel);
2818 struct TargetFilesystemArtifactResultCreator<ArtifactNameTag>
2820 static std::string Create(cmGeneratorTarget* target,
2821 cmGeneratorExpressionContext* context,
2822 const GeneratorExpressionContent* /*unused*/)
2824 return target->GetFullPath(context->Config,
2825 cmStateEnums::RuntimeBinaryArtifact, true);
2829 template <typename ArtifactT>
2830 struct TargetFilesystemArtifactResultGetter
2832 static std::string Get(const std::string& result);
2836 struct TargetFilesystemArtifactResultGetter<ArtifactNameTag>
2838 static std::string Get(const std::string& result)
2840 return cmSystemTools::GetFilenameName(result);
2845 struct TargetFilesystemArtifactResultGetter<ArtifactDirTag>
2847 static std::string Get(const std::string& result)
2849 return cmSystemTools::GetFilenamePath(result);
2854 struct TargetFilesystemArtifactResultGetter<ArtifactPathTag>
2856 static std::string Get(const std::string& result) { return result; }
2859 struct TargetArtifactBase : public cmGeneratorExpressionNode
2861 TargetArtifactBase() {} // NOLINT(modernize-use-equals-default)
2864 cmGeneratorTarget* GetTarget(
2865 const std::vector<std::string>& parameters,
2866 cmGeneratorExpressionContext* context,
2867 const GeneratorExpressionContent* content,
2868 cmGeneratorExpressionDAGChecker* dagChecker) const
2870 // Lookup the referenced target.
2871 std::string const& name = parameters.front();
2873 if (!cmGeneratorExpression::IsValidTargetName(name)) {
2874 ::reportError(context, content->GetOriginalExpression(),
2875 "Expression syntax not recognized.");
2878 cmGeneratorTarget* target = context->LG->FindGeneratorTargetToUse(name);
2880 ::reportError(context, content->GetOriginalExpression(),
2881 "No target \"" + name + "\"");
2884 if (target->GetType() >= cmStateEnums::OBJECT_LIBRARY &&
2885 target->GetType() != cmStateEnums::UNKNOWN_LIBRARY) {
2886 ::reportError(context, content->GetOriginalExpression(),
2887 "Target \"" + name +
2888 "\" is not an executable or library.");
2892 (dagChecker->EvaluatingLinkLibraries(target) ||
2893 (dagChecker->EvaluatingSources() &&
2894 target == dagChecker->TopTarget()))) {
2895 ::reportError(context, content->GetOriginalExpression(),
2896 "Expressions which require the linker language may not "
2897 "be used while evaluating link libraries");
2905 template <typename ArtifactT, typename ComponentT>
2906 struct TargetFilesystemArtifact : public TargetArtifactBase
2908 TargetFilesystemArtifact() {} // NOLINT(modernize-use-equals-default)
2910 int NumExpectedParameters() const override { return 1; }
2912 std::string Evaluate(
2913 const std::vector<std::string>& parameters,
2914 cmGeneratorExpressionContext* context,
2915 const GeneratorExpressionContent* content,
2916 cmGeneratorExpressionDAGChecker* dagChecker) const override
2918 cmGeneratorTarget* target =
2919 this->GetTarget(parameters, context, content, dagChecker);
2921 return std::string();
2923 // Not a dependent target if we are querying for ArtifactDirTag,
2924 // ArtifactNameTag, ArtifactBundleDirTag, ArtifactBundleDirNameTag,
2925 // and ArtifactBundleContentDirTag
2926 TargetFilesystemArtifactDependency<ArtifactT, ComponentT>::AddDependency(
2929 std::string result =
2930 TargetFilesystemArtifactResultCreator<ArtifactT>::Create(target, context,
2932 if (context->HadError) {
2933 return std::string();
2935 return TargetFilesystemArtifactResultGetter<ComponentT>::Get(result);
2939 template <typename ArtifactT>
2940 struct TargetFilesystemArtifactNodeGroup
2942 TargetFilesystemArtifactNodeGroup() // NOLINT(modernize-use-equals-default)
2946 TargetFilesystemArtifact<ArtifactT, ArtifactPathTag> File;
2947 TargetFilesystemArtifact<ArtifactT, ArtifactNameTag> FileName;
2948 TargetFilesystemArtifact<ArtifactT, ArtifactDirTag> FileDir;
2951 static const TargetFilesystemArtifactNodeGroup<ArtifactNameTag>
2954 static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag>
2955 targetLinkerNodeGroup;
2957 static const TargetFilesystemArtifactNodeGroup<ArtifactSonameTag>
2958 targetSoNameNodeGroup;
2960 static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag>
2963 static const TargetFilesystemArtifact<ArtifactBundleDirTag, ArtifactPathTag>
2964 targetBundleDirNode;
2966 static const TargetFilesystemArtifact<ArtifactBundleDirNameTag,
2968 targetBundleDirNameNode;
2970 static const TargetFilesystemArtifact<ArtifactBundleContentDirTag,
2972 targetBundleContentDirNode;
2975 // To retrieve base name for various artifacts
2977 template <typename ArtifactT>
2978 struct TargetOutputNameArtifactResultGetter
2980 static std::string Get(cmGeneratorTarget* target,
2981 cmGeneratorExpressionContext* context,
2982 const GeneratorExpressionContent* content);
2986 struct TargetOutputNameArtifactResultGetter<ArtifactNameTag>
2988 static std::string Get(cmGeneratorTarget* target,
2989 cmGeneratorExpressionContext* context,
2990 const GeneratorExpressionContent* /*unused*/)
2992 return target->GetOutputName(context->Config,
2993 cmStateEnums::RuntimeBinaryArtifact) +
2994 target->GetFilePostfix(context->Config);
2999 struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
3001 static std::string Get(cmGeneratorTarget* target,
3002 cmGeneratorExpressionContext* context,
3003 const GeneratorExpressionContent* content)
3005 // The file used to link to the target (.so, .lib, .a).
3006 if (!target->IsLinkable()) {
3007 ::reportError(context, content->GetOriginalExpression(),
3008 "TARGET_LINKER_FILE_BASE_NAME is allowed only for "
3009 "libraries and executables with ENABLE_EXPORTS.");
3010 return std::string();
3012 cmStateEnums::ArtifactType artifact =
3013 target->HasImportLibrary(context->Config)
3014 ? cmStateEnums::ImportLibraryArtifact
3015 : cmStateEnums::RuntimeBinaryArtifact;
3016 return target->GetOutputName(context->Config, artifact) +
3017 target->GetFilePostfix(context->Config);
3022 struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag>
3024 static std::string Get(cmGeneratorTarget* target,
3025 cmGeneratorExpressionContext* context,
3026 const GeneratorExpressionContent* content)
3028 if (target->IsImported()) {
3030 context, content->GetOriginalExpression(),
3031 "TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets.");
3032 return std::string();
3035 std::string language = target->GetLinkerLanguage(context->Config);
3037 std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB";
3039 if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) {
3041 context, content->GetOriginalExpression(),
3042 "TARGET_PDB_FILE_BASE_NAME is not supported by the target linker.");
3043 return std::string();
3046 cmStateEnums::TargetType targetType = target->GetType();
3048 if (targetType != cmStateEnums::SHARED_LIBRARY &&
3049 targetType != cmStateEnums::MODULE_LIBRARY &&
3050 targetType != cmStateEnums::EXECUTABLE) {
3051 ::reportError(context, content->GetOriginalExpression(),
3052 "TARGET_PDB_FILE_BASE_NAME is allowed only for "
3053 "targets with linker created artifacts.");
3054 return std::string();
3057 return target->GetPDBOutputName(context->Config) +
3058 target->GetFilePostfix(context->Config);
3062 template <typename ArtifactT>
3063 struct TargetFileBaseNameArtifact : public TargetArtifactBase
3065 TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default)
3067 int NumExpectedParameters() const override { return 1; }
3069 std::string Evaluate(
3070 const std::vector<std::string>& parameters,
3071 cmGeneratorExpressionContext* context,
3072 const GeneratorExpressionContent* content,
3073 cmGeneratorExpressionDAGChecker* dagChecker) const override
3075 cmGeneratorTarget* target =
3076 this->GetTarget(parameters, context, content, dagChecker);
3078 return std::string();
3081 std::string result = TargetOutputNameArtifactResultGetter<ArtifactT>::Get(
3082 target, context, content);
3083 if (context->HadError) {
3084 return std::string();
3090 static const TargetFileBaseNameArtifact<ArtifactNameTag>
3091 targetFileBaseNameNode;
3092 static const TargetFileBaseNameArtifact<ArtifactLinkerTag>
3093 targetLinkerFileBaseNameNode;
3094 static const TargetFileBaseNameArtifact<ArtifactPdbTag>
3095 targetPdbFileBaseNameNode;
3097 class ArtifactFilePrefixTag;
3098 class ArtifactLinkerFilePrefixTag;
3099 class ArtifactFileSuffixTag;
3100 class ArtifactLinkerFileSuffixTag;
3102 template <typename ArtifactT>
3103 struct TargetFileArtifactResultGetter
3105 static std::string Get(cmGeneratorTarget* target,
3106 cmGeneratorExpressionContext* context,
3107 const GeneratorExpressionContent* content);
3111 struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag>
3113 static std::string Get(cmGeneratorTarget* target,
3114 cmGeneratorExpressionContext* context,
3115 const GeneratorExpressionContent*)
3117 return target->GetFilePrefix(context->Config);
3121 struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
3123 static std::string Get(cmGeneratorTarget* target,
3124 cmGeneratorExpressionContext* context,
3125 const GeneratorExpressionContent* content)
3127 if (!target->IsLinkable()) {
3128 ::reportError(context, content->GetOriginalExpression(),
3129 "TARGET_LINKER_PREFIX is allowed only for libraries and "
3130 "executables with ENABLE_EXPORTS.");
3131 return std::string();
3134 cmStateEnums::ArtifactType artifact =
3135 target->HasImportLibrary(context->Config)
3136 ? cmStateEnums::ImportLibraryArtifact
3137 : cmStateEnums::RuntimeBinaryArtifact;
3139 return target->GetFilePrefix(context->Config, artifact);
3143 struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag>
3145 static std::string Get(cmGeneratorTarget* target,
3146 cmGeneratorExpressionContext* context,
3147 const GeneratorExpressionContent*)
3149 return target->GetFileSuffix(context->Config);
3153 struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
3155 static std::string Get(cmGeneratorTarget* target,
3156 cmGeneratorExpressionContext* context,
3157 const GeneratorExpressionContent* content)
3159 if (!target->IsLinkable()) {
3160 ::reportError(context, content->GetOriginalExpression(),
3161 "TARGET_LINKER_SUFFIX is allowed only for libraries and "
3162 "executables with ENABLE_EXPORTS.");
3163 return std::string();
3166 cmStateEnums::ArtifactType artifact =
3167 target->HasImportLibrary(context->Config)
3168 ? cmStateEnums::ImportLibraryArtifact
3169 : cmStateEnums::RuntimeBinaryArtifact;
3171 return target->GetFileSuffix(context->Config, artifact);
3175 template <typename ArtifactT>
3176 struct TargetFileArtifact : public TargetArtifactBase
3178 TargetFileArtifact() {} // NOLINT(modernize-use-equals-default)
3180 int NumExpectedParameters() const override { return 1; }
3182 std::string Evaluate(
3183 const std::vector<std::string>& parameters,
3184 cmGeneratorExpressionContext* context,
3185 const GeneratorExpressionContent* content,
3186 cmGeneratorExpressionDAGChecker* dagChecker) const override
3188 cmGeneratorTarget* target =
3189 this->GetTarget(parameters, context, content, dagChecker);
3191 return std::string();
3194 std::string result =
3195 TargetFileArtifactResultGetter<ArtifactT>::Get(target, context, content);
3196 if (context->HadError) {
3197 return std::string();
3203 static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode;
3204 static const TargetFileArtifact<ArtifactLinkerFilePrefixTag>
3205 targetLinkerFilePrefixNode;
3206 static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode;
3207 static const TargetFileArtifact<ArtifactLinkerFileSuffixTag>
3208 targetLinkerFileSuffixNode;
3210 static const struct ShellPathNode : public cmGeneratorExpressionNode
3212 ShellPathNode() {} // NOLINT(modernize-use-equals-default)
3214 std::string Evaluate(
3215 const std::vector<std::string>& parameters,
3216 cmGeneratorExpressionContext* context,
3217 const GeneratorExpressionContent* content,
3218 cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
3220 std::vector<std::string> listIn = cmExpandedList(parameters.front());
3221 if (listIn.empty()) {
3222 reportError(context, content->GetOriginalExpression(),
3223 "\"\" is not an absolute path.");
3224 return std::string();
3226 cmStateSnapshot snapshot = context->LG->GetStateSnapshot();
3227 cmOutputConverter converter(snapshot);
3228 const char* separator = snapshot.GetState()->UseWindowsShell() ? ";" : ":";
3229 std::vector<std::string> listOut;
3230 listOut.reserve(listIn.size());
3231 for (auto const& in : listIn) {
3232 if (!cmSystemTools::FileIsFullPath(in)) {
3233 reportError(context, content->GetOriginalExpression(),
3234 "\"" + in + "\" is not an absolute path.");
3235 return std::string();
3237 listOut.emplace_back(converter.ConvertDirectorySeparatorsForShell(in));
3239 return cmJoin(listOut, separator);
3243 const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
3244 const std::string& identifier)
3246 static std::map<std::string, cmGeneratorExpressionNode const*> const nodeMap{
3249 { "AND", &andNode },
3251 { "NOT", ¬Node },
3252 { "C_COMPILER_ID", &cCompilerIdNode },
3253 { "CXX_COMPILER_ID", &cxxCompilerIdNode },
3254 { "OBJC_COMPILER_ID", &objcCompilerIdNode },
3255 { "OBJCXX_COMPILER_ID", &objcxxCompilerIdNode },
3256 { "CUDA_COMPILER_ID", &cudaCompilerIdNode },
3257 { "Fortran_COMPILER_ID", &fortranCompilerIdNode },
3258 { "HIP_COMPILER_ID", &hipCompilerIdNode },
3259 { "VERSION_GREATER", &versionGreaterNode },
3260 { "VERSION_GREATER_EQUAL", &versionGreaterEqNode },
3261 { "VERSION_LESS", &versionLessNode },
3262 { "VERSION_LESS_EQUAL", &versionLessEqNode },
3263 { "VERSION_EQUAL", &versionEqualNode },
3264 { "C_COMPILER_VERSION", &cCompilerVersionNode },
3265 { "CXX_COMPILER_VERSION", &cxxCompilerVersionNode },
3266 { "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode },
3267 { "OBJC_COMPILER_VERSION", &objcCompilerVersionNode },
3268 { "OBJCXX_COMPILER_VERSION", &objcxxCompilerVersionNode },
3269 { "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode },
3270 { "HIP_COMPILER_VERSION", &hipCompilerVersionNode },
3271 { "PLATFORM_ID", &platformIdNode },
3272 { "COMPILE_FEATURES", &compileFeaturesNode },
3273 { "CONFIGURATION", &configurationNode },
3274 { "CONFIG", &configurationTestNode },
3275 { "TARGET_FILE", &targetNodeGroup.File },
3276 { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File },
3277 { "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File },
3278 { "TARGET_PDB_FILE", &targetPdbNodeGroup.File },
3279 { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode },
3280 { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode },
3281 { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode },
3282 { "TARGET_FILE_PREFIX", &targetFilePrefixNode },
3283 { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode },
3284 { "TARGET_FILE_SUFFIX", &targetFileSuffixNode },
3285 { "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode },
3286 { "TARGET_FILE_NAME", &targetNodeGroup.FileName },
3287 { "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.FileName },
3288 { "TARGET_SONAME_FILE_NAME", &targetSoNameNodeGroup.FileName },
3289 { "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName },
3290 { "TARGET_FILE_DIR", &targetNodeGroup.FileDir },
3291 { "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.FileDir },
3292 { "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.FileDir },
3293 { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir },
3294 { "TARGET_BUNDLE_DIR", &targetBundleDirNode },
3295 { "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode },
3296 { "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode },
3297 { "STREQUAL", &strEqualNode },
3298 { "EQUAL", &equalNode },
3299 { "IN_LIST", &inListNode },
3300 { "FILTER", &filterNode },
3301 { "REMOVE_DUPLICATES", &removeDuplicatesNode },
3302 { "LOWER_CASE", &lowerCaseNode },
3303 { "UPPER_CASE", &upperCaseNode },
3304 { "PATH", &pathNode },
3305 { "PATH_EQUAL", &pathEqualNode },
3306 { "MAKE_C_IDENTIFIER", &makeCIdentifierNode },
3307 { "BOOL", &boolNode },
3309 { "ANGLE-R", &angle_rNode },
3310 { "COMMA", &commaNode },
3311 { "SEMICOLON", &semicolonNode },
3312 { "TARGET_PROPERTY", &targetPropertyNode },
3313 { "TARGET_NAME", &targetNameNode },
3314 { "TARGET_OBJECTS", &targetObjectsNode },
3315 { "TARGET_POLICY", &targetPolicyNode },
3316 { "TARGET_EXISTS", &targetExistsNode },
3317 { "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode },
3318 { "TARGET_GENEX_EVAL", &targetGenexEvalNode },
3319 { "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode },
3320 { "GENEX_EVAL", &genexEvalNode },
3321 { "BUILD_INTERFACE", &buildInterfaceNode },
3322 { "INSTALL_INTERFACE", &installInterfaceNode },
3323 { "INSTALL_PREFIX", &installPrefixNode },
3324 { "JOIN", &joinNode },
3325 { "LINK_ONLY", &linkOnlyNode },
3326 { "COMPILE_LANG_AND_ID", &languageAndIdNode },
3327 { "COMPILE_LANGUAGE", &languageNode },
3328 { "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
3329 { "LINK_LANGUAGE", &linkLanguageNode },
3330 { "LINK_LIBRARY", &linkLibraryNode },
3331 { "LINK_GROUP", &linkGroupNode },
3332 { "HOST_LINK", &hostLinkNode },
3333 { "DEVICE_LINK", &deviceLinkNode },
3334 { "SHELL_PATH", &shellPathNode }
3338 auto itr = nodeMap.find(identifier);
3339 if (itr != nodeMap.end()) {
3346 void reportError(cmGeneratorExpressionContext* context,
3347 const std::string& expr, const std::string& result)
3349 context->HadError = true;
3350 if (context->Quiet) {
3354 std::ostringstream e;
3355 /* clang-format off */
3356 e << "Error evaluating generator expression:\n"
3357 << " " << expr << "\n"
3359 /* clang-format on */
3360 context->LG->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
3361 e.str(), context->Backtrace);