| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file Copyright.txt or https://cmake.org/licensing for details. */ | |
| #include "cmGeneratorExpressionNode.h" | |
| #include <algorithm> | |
| #include <cassert> | |
| #include <cerrno> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <map> | |
| #include <memory> | |
| #include <set> | |
| #include <sstream> | |
| #include <utility> | |
| #include <cm/iterator> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include <cm/vector> | |
| #include <cmext/algorithm> | |
| #include <cmext/string_view> | |
| #include "cmsys/RegularExpression.hxx" | |
| #include "cmsys/String.h" | |
| #include "cmAlgorithms.h" | |
| #include "cmComputeLinkInformation.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorExpressionContext.h" | |
| #include "cmGeneratorExpressionDAGChecker.h" | |
| #include "cmGeneratorExpressionEvaluator.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmLinkItem.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmOutputConverter.h" | |
| #include "cmPolicies.h" | |
| #include "cmProperty.h" | |
| #include "cmRange.h" | |
| #include "cmStandardLevelResolver.h" | |
| #include "cmState.h" | |
| #include "cmStateSnapshot.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmake.h" | |
| std::string cmGeneratorExpressionNode::EvaluateDependentExpression( | |
| std::string const& prop, cmLocalGenerator* lg, | |
| cmGeneratorExpressionContext* context, cmGeneratorTarget const* headTarget, | |
| cmGeneratorExpressionDAGChecker* dagChecker, | |
| cmGeneratorTarget const* currentTarget) | |
| { | |
| cmGeneratorExpression ge(context->Backtrace); | |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop); | |
| cge->SetEvaluateForBuildsystem(context->EvaluateForBuildsystem); | |
| cge->SetQuiet(context->Quiet); | |
| std::string result = | |
| cge->Evaluate(lg, context->Config, headTarget, dagChecker, currentTarget, | |
| context->Language); | |
| if (cge->GetHadContextSensitiveCondition()) { | |
| context->HadContextSensitiveCondition = true; | |
| } | |
| if (cge->GetHadHeadSensitiveCondition()) { | |
| context->HadHeadSensitiveCondition = true; | |
| } | |
| if (cge->GetHadLinkLanguageSensitiveCondition()) { | |
| context->HadLinkLanguageSensitiveCondition = true; | |
| } | |
| return result; | |
| } | |
| static const struct ZeroNode : public cmGeneratorExpressionNode | |
| { | |
| ZeroNode() {} // NOLINT(modernize-use-equals-default) | |
| bool GeneratesContent() const override { return false; } | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& /*parameters*/, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return std::string(); | |
| } | |
| } zeroNode; | |
| static const struct OneNode : public cmGeneratorExpressionNode | |
| { | |
| OneNode() {} // NOLINT(modernize-use-equals-default) | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return parameters.front(); | |
| } | |
| } oneNode; | |
| static const struct OneNode buildInterfaceNode; | |
| static const struct ZeroNode installInterfaceNode; | |
| struct BooleanOpNode : public cmGeneratorExpressionNode | |
| { | |
| BooleanOpNode(const char* op_, const char* successVal_, | |
| const char* failureVal_) | |
| : op(op_) | |
| , successVal(successVal_) | |
| , failureVal(failureVal_) | |
| { | |
| } | |
| int NumExpectedParameters() const override { return OneOrMoreParameters; } | |
| std::string Evaluate(const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker*) const override | |
| { | |
| for (std::string const& param : parameters) { | |
| if (param == this->failureVal) { | |
| return this->failureVal; | |
| } | |
| if (param != this->successVal) { | |
| std::ostringstream e; | |
| e << "Parameters to $<" << this->op; | |
| e << "> must resolve to either '0' or '1'."; | |
| reportError(context, content->GetOriginalExpression(), e.str()); | |
| return std::string(); | |
| } | |
| } | |
| return this->successVal; | |
| } | |
| const char *const op, *const successVal, *const failureVal; | |
| }; | |
| static const BooleanOpNode andNode("AND", "1", "0"), orNode("OR", "0", "1"); | |
| static const struct NotNode : public cmGeneratorExpressionNode | |
| { | |
| NotNode() {} // NOLINT(modernize-use-equals-default) | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.front() != "0" && parameters.front() != "1") { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<NOT> parameter must resolve to exactly one '0' or '1' value."); | |
| return std::string(); | |
| } | |
| return parameters.front() == "0" ? "1" : "0"; | |
| } | |
| } notNode; | |
| static const struct BoolNode : public cmGeneratorExpressionNode | |
| { | |
| BoolNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return !cmIsOff(parameters.front()) ? "1" : "0"; | |
| } | |
| } boolNode; | |
| static const struct IfNode : public cmGeneratorExpressionNode | |
| { | |
| IfNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 3; } | |
| std::string Evaluate(const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker*) const override | |
| { | |
| if (parameters[0] != "1" && parameters[0] != "0") { | |
| reportError(context, content->GetOriginalExpression(), | |
| "First parameter to $<IF> must resolve to exactly one '0' " | |
| "or '1' value."); | |
| return std::string(); | |
| } | |
| return parameters[0] == "1" ? parameters[1] : parameters[2]; | |
| } | |
| } ifNode; | |
| static const struct StrEqualNode : public cmGeneratorExpressionNode | |
| { | |
| StrEqualNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return parameters.front() == parameters[1] ? "1" : "0"; | |
| } | |
| } strEqualNode; | |
| static const struct EqualNode : public cmGeneratorExpressionNode | |
| { | |
| EqualNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| long numbers[2]; | |
| for (int i = 0; i < 2; ++i) { | |
| if (!ParameterToLong(parameters[i].c_str(), &numbers[i])) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<EQUAL> parameter " + parameters[i] + | |
| " is not a valid integer."); | |
| return {}; | |
| } | |
| } | |
| return numbers[0] == numbers[1] ? "1" : "0"; | |
| } | |
| static bool ParameterToLong(const char* param, long* outResult) | |
| { | |
| const char isNegative = param[0] == '-'; | |
| int base = 0; | |
| if (cmHasLiteralPrefix(param, "0b") || cmHasLiteralPrefix(param, "0B")) { | |
| base = 2; | |
| param += 2; | |
| } else if (cmHasLiteralPrefix(param, "-0b") || | |
| cmHasLiteralPrefix(param, "-0B") || | |
| cmHasLiteralPrefix(param, "+0b") || | |
| cmHasLiteralPrefix(param, "+0B")) { | |
| base = 2; | |
| param += 3; | |
| } | |
| char* pEnd; | |
| long result = strtol(param, &pEnd, base); | |
| if (pEnd == param || *pEnd != '\0' || errno == ERANGE) { | |
| return false; | |
| } | |
| if (isNegative && result > 0) { | |
| result *= -1; | |
| } | |
| *outResult = result; | |
| return true; | |
| } | |
| } equalNode; | |
| static const struct InListNode : public cmGeneratorExpressionNode | |
| { | |
| InListNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| std::vector<std::string> values; | |
| std::vector<std::string> checkValues; | |
| bool check = false; | |
| switch (context->LG->GetPolicyStatus(cmPolicies::CMP0085)) { | |
| case cmPolicies::WARN: | |
| if (parameters.front().empty()) { | |
| check = true; | |
| cmExpandList(parameters[1], checkValues, true); | |
| } | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| cmExpandList(parameters[1], values); | |
| if (check && values != checkValues) { | |
| std::ostringstream e; | |
| e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085) | |
| << "\nSearch Item:\n \"" << parameters.front() | |
| << "\"\nList:\n \"" << parameters[1] << "\"\n"; | |
| context->LG->GetCMakeInstance()->IssueMessage( | |
| MessageType ::AUTHOR_WARNING, e.str(), context->Backtrace); | |
| return "0"; | |
| } | |
| if (values.empty()) { | |
| return "0"; | |
| } | |
| break; | |
| case cmPolicies::REQUIRED_IF_USED: | |
| case cmPolicies::REQUIRED_ALWAYS: | |
| case cmPolicies::NEW: | |
| cmExpandList(parameters[1], values, true); | |
| break; | |
| } | |
| return cm::contains(values, parameters.front()) ? "1" : "0"; | |
| } | |
| } inListNode; | |
| static const struct FilterNode : public cmGeneratorExpressionNode | |
| { | |
| FilterNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 3; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.size() != 3) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<FILTER:...> expression requires three parameters"); | |
| return {}; | |
| } | |
| if (parameters[1] != "INCLUDE" && parameters[1] != "EXCLUDE") { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<FILTER:...> second parameter must be either INCLUDE or EXCLUDE"); | |
| return {}; | |
| } | |
| const bool exclude = parameters[1] == "EXCLUDE"; | |
| cmsys::RegularExpression re; | |
| if (!re.compile(parameters[2])) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<FILTER:...> failed to compile regex"); | |
| return {}; | |
| } | |
| std::vector<std::string> values; | |
| std::vector<std::string> result; | |
| cmExpandList(parameters.front(), values, true); | |
| std::copy_if(values.cbegin(), values.cend(), std::back_inserter(result), | |
| [&re, exclude](std::string const& input) { | |
| return exclude ^ re.find(input); | |
| }); | |
| return cmJoin(cmMakeRange(result.cbegin(), result.cend()), ";"); | |
| } | |
| } filterNode; | |
| static const struct RemoveDuplicatesNode : public cmGeneratorExpressionNode | |
| { | |
| RemoveDuplicatesNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.size() != 1) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<REMOVE_DUPLICATES:...> expression requires one parameter"); | |
| } | |
| std::vector<std::string> values = cmExpandedList(parameters.front(), true); | |
| auto valuesEnd = cmRemoveDuplicates(values); | |
| auto valuesBegin = values.cbegin(); | |
| return cmJoin(cmMakeRange(valuesBegin, valuesEnd), ";"); | |
| } | |
| } removeDuplicatesNode; | |
| static const struct TargetExistsNode : public cmGeneratorExpressionNode | |
| { | |
| TargetExistsNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.size() != 1) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<TARGET_EXISTS:...> expression requires one parameter"); | |
| return std::string(); | |
| } | |
| std::string targetName = parameters.front(); | |
| if (targetName.empty() || | |
| !cmGeneratorExpression::IsValidTargetName(targetName)) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<TARGET_EXISTS:tgt> expression requires a non-empty " | |
| "valid target name."); | |
| return std::string(); | |
| } | |
| return context->LG->GetMakefile()->FindTargetToUse(targetName) ? "1" : "0"; | |
| } | |
| } targetExistsNode; | |
| static const struct TargetNameIfExistsNode : public cmGeneratorExpressionNode | |
| { | |
| TargetNameIfExistsNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.size() != 1) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<TARGET_NAME_IF_EXISTS:...> expression requires one " | |
| "parameter"); | |
| return std::string(); | |
| } | |
| std::string targetName = parameters.front(); | |
| if (targetName.empty() || | |
| !cmGeneratorExpression::IsValidTargetName(targetName)) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<TARGET_NAME_IF_EXISTS:tgt> expression requires a " | |
| "non-empty valid target name."); | |
| return std::string(); | |
| } | |
| return context->LG->GetMakefile()->FindTargetToUse(targetName) | |
| ? targetName | |
| : std::string(); | |
| } | |
| } targetNameIfExistsNode; | |
| struct GenexEvaluator : public cmGeneratorExpressionNode | |
| { | |
| GenexEvaluator() {} // NOLINT(modernize-use-equals-default) | |
| protected: | |
| std::string EvaluateExpression( | |
| const std::string& genexOperator, const std::string& expression, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const | |
| { | |
| if (context->HeadTarget) { | |
| cmGeneratorExpressionDAGChecker dagChecker( | |
| context->Backtrace, context->HeadTarget, | |
| genexOperator + ":" + expression, content, dagCheckerParent); | |
| switch (dagChecker.Check()) { | |
| case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: | |
| case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: { | |
| dagChecker.ReportError(context, content->GetOriginalExpression()); | |
| return std::string(); | |
| } | |
| case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: | |
| case cmGeneratorExpressionDAGChecker::DAG: | |
| break; | |
| } | |
| return this->EvaluateDependentExpression( | |
| expression, context->LG, context, context->HeadTarget, &dagChecker, | |
| context->CurrentTarget); | |
| } | |
| return this->EvaluateDependentExpression( | |
| expression, context->LG, context, context->HeadTarget, dagCheckerParent, | |
| context->CurrentTarget); | |
| } | |
| }; | |
| static const struct TargetGenexEvalNode : public GenexEvaluator | |
| { | |
| TargetGenexEvalNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const override | |
| { | |
| const std::string& targetName = parameters.front(); | |
| if (targetName.empty() || | |
| !cmGeneratorExpression::IsValidTargetName(targetName)) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<TARGET_GENEX_EVAL:tgt, ...> expression requires a " | |
| "non-empty valid target name."); | |
| return std::string(); | |
| } | |
| const auto* target = context->LG->FindGeneratorTargetToUse(targetName); | |
| if (!target) { | |
| std::ostringstream e; | |
| e << "$<TARGET_GENEX_EVAL:tgt, ...> target \"" << targetName | |
| << "\" not found."; | |
| reportError(context, content->GetOriginalExpression(), e.str()); | |
| return std::string(); | |
| } | |
| const std::string& expression = parameters[1]; | |
| if (expression.empty()) { | |
| return expression; | |
| } | |
| cmGeneratorExpressionContext targetContext( | |
| context->LG, context->Config, context->Quiet, target, target, | |
| context->EvaluateForBuildsystem, context->Backtrace, context->Language); | |
| return this->EvaluateExpression("TARGET_GENEX_EVAL", expression, | |
| &targetContext, content, dagCheckerParent); | |
| } | |
| } targetGenexEvalNode; | |
| static const struct GenexEvalNode : public GenexEvaluator | |
| { | |
| GenexEvalNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const override | |
| { | |
| const std::string& expression = parameters[0]; | |
| if (expression.empty()) { | |
| return expression; | |
| } | |
| return this->EvaluateExpression("GENEX_EVAL", expression, context, content, | |
| dagCheckerParent); | |
| } | |
| } genexEvalNode; | |
| static const struct LowerCaseNode : public cmGeneratorExpressionNode | |
| { | |
| LowerCaseNode() {} // NOLINT(modernize-use-equals-default) | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cmSystemTools::LowerCase(parameters.front()); | |
| } | |
| } lowerCaseNode; | |
| static const struct UpperCaseNode : public cmGeneratorExpressionNode | |
| { | |
| UpperCaseNode() {} // NOLINT(modernize-use-equals-default) | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cmSystemTools::UpperCase(parameters.front()); | |
| } | |
| } upperCaseNode; | |
| static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode | |
| { | |
| MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default) | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cmSystemTools::MakeCidentifier(parameters.front()); | |
| } | |
| } makeCIdentifierNode; | |
| template <char C> | |
| struct CharacterNode : public cmGeneratorExpressionNode | |
| { | |
| CharacterNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 0; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& /*parameters*/, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return { C }; | |
| } | |
| }; | |
| static const CharacterNode<'>'> angle_rNode; | |
| static const CharacterNode<','> commaNode; | |
| static const CharacterNode<';'> semicolonNode; | |
| struct CompilerIdNode : public cmGeneratorExpressionNode | |
| { | |
| CompilerIdNode(const char* compilerLang) | |
| : CompilerLanguage(compilerLang) | |
| { | |
| } | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!context->HeadTarget) { | |
| std::ostringstream e; | |
| e << "$<" << this->CompilerLanguage | |
| << "_COMPILER_ID> may only be used with binary targets. It may " | |
| "not be used with add_custom_command or add_custom_target."; | |
| reportError(context, content->GetOriginalExpression(), e.str()); | |
| return {}; | |
| } | |
| return this->EvaluateWithLanguage(parameters, context, content, dagChecker, | |
| this->CompilerLanguage); | |
| } | |
| std::string EvaluateWithLanguage(const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*unused*/, | |
| const std::string& lang) const | |
| { | |
| std::string const& compilerId = | |
| context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang + | |
| "_COMPILER_ID"); | |
| if (parameters.empty()) { | |
| return compilerId; | |
| } | |
| if (compilerId.empty()) { | |
| return parameters.front().empty() ? "1" : "0"; | |
| } | |
| static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$"); | |
| for (auto const& param : parameters) { | |
| if (!compilerIdValidator.find(param)) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return std::string(); | |
| } | |
| if (strcmp(param.c_str(), compilerId.c_str()) == 0) { | |
| return "1"; | |
| } | |
| if (cmsysString_strcasecmp(param.c_str(), compilerId.c_str()) == 0) { | |
| switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) { | |
| case cmPolicies::WARN: { | |
| context->LG->GetCMakeInstance()->IssueMessage( | |
| MessageType::AUTHOR_WARNING, | |
| cmPolicies::GetPolicyWarning(cmPolicies::CMP0044), | |
| context->Backtrace); | |
| CM_FALLTHROUGH; | |
| } | |
| case cmPolicies::OLD: | |
| return "1"; | |
| case cmPolicies::NEW: | |
| case cmPolicies::REQUIRED_ALWAYS: | |
| case cmPolicies::REQUIRED_IF_USED: | |
| break; | |
| } | |
| } | |
| } | |
| return "0"; | |
| } | |
| const char* const CompilerLanguage; | |
| }; | |
| static const CompilerIdNode cCompilerIdNode("C"), cxxCompilerIdNode("CXX"), | |
| cudaCompilerIdNode("CUDA"), objcCompilerIdNode("OBJC"), | |
| objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran"), | |
| hipCompilerIdNode("HIP"), ispcCompilerIdNode("ISPC"); | |
| struct CompilerVersionNode : public cmGeneratorExpressionNode | |
| { | |
| CompilerVersionNode(const char* compilerLang) | |
| : CompilerLanguage(compilerLang) | |
| { | |
| } | |
| int NumExpectedParameters() const override { return OneOrZeroParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!context->HeadTarget) { | |
| std::ostringstream e; | |
| e << "$<" << this->CompilerLanguage | |
| << "_COMPILER_VERSION> may only be used with binary targets. It " | |
| "may not be used with add_custom_command or add_custom_target."; | |
| reportError(context, content->GetOriginalExpression(), e.str()); | |
| return {}; | |
| } | |
| return this->EvaluateWithLanguage(parameters, context, content, dagChecker, | |
| this->CompilerLanguage); | |
| } | |
| std::string EvaluateWithLanguage(const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*unused*/, | |
| const std::string& lang) const | |
| { | |
| std::string const& compilerVersion = | |
| context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang + | |
| "_COMPILER_VERSION"); | |
| if (parameters.empty()) { | |
| return compilerVersion; | |
| } | |
| static cmsys::RegularExpression compilerIdValidator("^[0-9\\.]*$"); | |
| if (!compilerIdValidator.find(parameters.front())) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return {}; | |
| } | |
| if (compilerVersion.empty()) { | |
| return parameters.front().empty() ? "1" : "0"; | |
| } | |
| return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL, | |
| parameters.front().c_str(), | |
| compilerVersion.c_str()) | |
| ? "1" | |
| : "0"; | |
| } | |
| const char* const CompilerLanguage; | |
| }; | |
| static const CompilerVersionNode cCompilerVersionNode("C"), | |
| cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"), | |
| objcCompilerVersionNode("OBJC"), objcxxCompilerVersionNode("OBJCXX"), | |
| fortranCompilerVersionNode("Fortran"), ispcCompilerVersionNode("ISPC"), | |
| hipCompilerVersionNode("HIP"); | |
| struct PlatformIdNode : public cmGeneratorExpressionNode | |
| { | |
| PlatformIdNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| std::string const& platformId = | |
| context->LG->GetMakefile()->GetSafeDefinition("CMAKE_SYSTEM_NAME"); | |
| if (parameters.empty()) { | |
| return platformId; | |
| } | |
| if (platformId.empty()) { | |
| return parameters.front().empty() ? "1" : "0"; | |
| } | |
| for (auto const& param : parameters) { | |
| if (param == platformId) { | |
| return "1"; | |
| } | |
| } | |
| return "0"; | |
| } | |
| } platformIdNode; | |
| template <cmSystemTools::CompareOp Op> | |
| struct VersionNode : public cmGeneratorExpressionNode | |
| { | |
| VersionNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cmSystemTools::VersionCompare(Op, parameters.front().c_str(), | |
| parameters[1].c_str()) | |
| ? "1" | |
| : "0"; | |
| } | |
| }; | |
| static const VersionNode<cmSystemTools::OP_GREATER> versionGreaterNode; | |
| static const VersionNode<cmSystemTools::OP_GREATER_EQUAL> versionGreaterEqNode; | |
| static const VersionNode<cmSystemTools::OP_LESS> versionLessNode; | |
| static const VersionNode<cmSystemTools::OP_LESS_EQUAL> versionLessEqNode; | |
| static const VersionNode<cmSystemTools::OP_EQUAL> versionEqualNode; | |
| static const struct LinkOnlyNode : public cmGeneratorExpressionNode | |
| { | |
| LinkOnlyNode() {} // NOLINT(modernize-use-equals-default) | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!dagChecker) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<LINK_ONLY:...> may only be used for linking"); | |
| return std::string(); | |
| } | |
| if (!dagChecker->GetTransitivePropertiesOnly()) { | |
| return parameters.front(); | |
| } | |
| return std::string(); | |
| } | |
| } linkOnlyNode; | |
| static const struct ConfigurationNode : public cmGeneratorExpressionNode | |
| { | |
| ConfigurationNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 0; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& /*parameters*/, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| context->HadContextSensitiveCondition = true; | |
| return context->Config; | |
| } | |
| } configurationNode; | |
| static const struct ConfigurationTestNode : public cmGeneratorExpressionNode | |
| { | |
| ConfigurationTestNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.empty()) { | |
| return configurationNode.Evaluate(parameters, context, content, nullptr); | |
| } | |
| static cmsys::RegularExpression configValidator("^[A-Za-z0-9_]*$"); | |
| if (!configValidator.find(parameters.front())) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return std::string(); | |
| } | |
| context->HadContextSensitiveCondition = true; | |
| for (auto const& param : parameters) { | |
| if (context->Config.empty()) { | |
| if (param.empty()) { | |
| return "1"; | |
| } | |
| } else if (cmsysString_strcasecmp(param.c_str(), | |
| context->Config.c_str()) == 0) { | |
| return "1"; | |
| } | |
| } | |
| if (context->CurrentTarget && context->CurrentTarget->IsImported()) { | |
| cmProp loc = nullptr; | |
| cmProp imp = nullptr; | |
| std::string suffix; | |
| if (context->CurrentTarget->Target->GetMappedConfig(context->Config, loc, | |
| imp, suffix)) { | |
| // This imported target has an appropriate location | |
| // for this (possibly mapped) config. | |
| // Check if there is a proper config mapping for the tested config. | |
| std::vector<std::string> mappedConfigs; | |
| std::string mapProp = cmStrCat( | |
| "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(context->Config)); | |
| if (cmProp mapValue = context->CurrentTarget->GetProperty(mapProp)) { | |
| cmExpandList(cmSystemTools::UpperCase(*mapValue), mappedConfigs); | |
| for (auto const& param : parameters) { | |
| if (cm::contains(mappedConfigs, cmSystemTools::UpperCase(param))) { | |
| return "1"; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return "0"; | |
| } | |
| } configurationTestNode; | |
| static const struct JoinNode : public cmGeneratorExpressionNode | |
| { | |
| JoinNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| std::vector<std::string> list = cmExpandedList(parameters.front()); | |
| return cmJoin(list, parameters[1]); | |
| } | |
| } joinNode; | |
| static const struct CompileLanguageNode : public cmGeneratorExpressionNode | |
| { | |
| CompileLanguageNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (context->Language.empty() && | |
| (!dagChecker || !dagChecker->EvaluatingCompileExpression())) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<COMPILE_LANGUAGE:...> may only be used to specify include " | |
| "directories, compile definitions, compile options, and to evaluate " | |
| "components of the file(GENERATE) command."); | |
| return std::string(); | |
| } | |
| cmGlobalGenerator* gg = context->LG->GetGlobalGenerator(); | |
| std::string genName = gg->GetName(); | |
| if (genName.find("Makefiles") == std::string::npos && | |
| genName.find("Ninja") == std::string::npos && | |
| genName.find("Visual Studio") == std::string::npos && | |
| genName.find("Xcode") == std::string::npos && | |
| genName.find("Watcom WMake") == std::string::npos) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<COMPILE_LANGUAGE:...> not supported for this generator."); | |
| return std::string(); | |
| } | |
| if (parameters.empty()) { | |
| return context->Language; | |
| } | |
| for (auto const& param : parameters) { | |
| if (context->Language == param) { | |
| return "1"; | |
| } | |
| } | |
| return "0"; | |
| } | |
| } languageNode; | |
| static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode | |
| { | |
| CompileLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return TwoOrMoreParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!context->HeadTarget || | |
| (context->Language.empty() && | |
| (!dagChecker || !dagChecker->EvaluatingCompileExpression()))) { | |
| // reportError(context, content->GetOriginalExpression(), ""); | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<COMPILE_LANG_AND_ID:lang,id> may only be used with binary targets " | |
| "to specify include directories, compile definitions, and compile " | |
| "options. It may not be used with the add_custom_command, " | |
| "add_custom_target, or file(GENERATE) commands."); | |
| return std::string(); | |
| } | |
| cmGlobalGenerator* gg = context->LG->GetGlobalGenerator(); | |
| std::string genName = gg->GetName(); | |
| if (genName.find("Makefiles") == std::string::npos && | |
| genName.find("Ninja") == std::string::npos && | |
| genName.find("Visual Studio") == std::string::npos && | |
| genName.find("Xcode") == std::string::npos && | |
| genName.find("Watcom WMake") == std::string::npos) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<COMPILE_LANG_AND_ID:lang,id> not supported for this generator."); | |
| return std::string(); | |
| } | |
| const std::string& lang = context->Language; | |
| if (lang == parameters.front()) { | |
| std::vector<std::string> idParameter((parameters.cbegin() + 1), | |
| parameters.cend()); | |
| return CompilerIdNode{ lang.c_str() }.EvaluateWithLanguage( | |
| idParameter, context, content, dagChecker, lang); | |
| } | |
| return "0"; | |
| } | |
| } languageAndIdNode; | |
| static const struct LinkLanguageNode : public cmGeneratorExpressionNode | |
| { | |
| LinkLanguageNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!context->HeadTarget || !dagChecker || | |
| !(dagChecker->EvaluatingLinkExpression() || | |
| dagChecker->EvaluatingLinkLibraries())) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<LINK_LANGUAGE:...> may only be used with binary targets " | |
| "to specify link libraries, link directories, link options " | |
| "and link depends."); | |
| return std::string(); | |
| } | |
| if (dagChecker->EvaluatingLinkLibraries() && parameters.empty()) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<LINK_LANGUAGE> is not supported in link libraries expression."); | |
| return std::string(); | |
| } | |
| cmGlobalGenerator* gg = context->LG->GetGlobalGenerator(); | |
| std::string genName = gg->GetName(); | |
| if (genName.find("Makefiles") == std::string::npos && | |
| genName.find("Ninja") == std::string::npos && | |
| genName.find("Visual Studio") == std::string::npos && | |
| genName.find("Xcode") == std::string::npos && | |
| genName.find("Watcom WMake") == std::string::npos) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<LINK_LANGUAGE:...> not supported for this generator."); | |
| return std::string(); | |
| } | |
| if (dagChecker->EvaluatingLinkLibraries()) { | |
| context->HadHeadSensitiveCondition = true; | |
| context->HadLinkLanguageSensitiveCondition = true; | |
| } | |
| if (parameters.empty()) { | |
| return context->Language; | |
| } | |
| for (auto const& param : parameters) { | |
| if (context->Language == param) { | |
| return "1"; | |
| } | |
| } | |
| return "0"; | |
| } | |
| } linkLanguageNode; | |
| namespace { | |
| struct LinkerId | |
| { | |
| static std::string Evaluate(const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| const std::string& lang) | |
| { | |
| std::string const& linkerId = | |
| context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang + | |
| "_COMPILER_ID"); | |
| if (parameters.empty()) { | |
| return linkerId; | |
| } | |
| if (linkerId.empty()) { | |
| return parameters.front().empty() ? "1" : "0"; | |
| } | |
| static cmsys::RegularExpression linkerIdValidator("^[A-Za-z0-9_]*$"); | |
| for (auto const& param : parameters) { | |
| if (!linkerIdValidator.find(param)) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return std::string(); | |
| } | |
| if (param == linkerId) { | |
| return "1"; | |
| } | |
| } | |
| return "0"; | |
| } | |
| }; | |
| } | |
| static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode | |
| { | |
| LinkLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return TwoOrMoreParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!context->HeadTarget || !dagChecker || | |
| !(dagChecker->EvaluatingLinkExpression() || | |
| dagChecker->EvaluatingLinkLibraries())) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets " | |
| "to specify link libraries, link directories, link options, and link " | |
| "depends."); | |
| return std::string(); | |
| } | |
| cmGlobalGenerator* gg = context->LG->GetGlobalGenerator(); | |
| std::string genName = gg->GetName(); | |
| if (genName.find("Makefiles") == std::string::npos && | |
| genName.find("Ninja") == std::string::npos && | |
| genName.find("Visual Studio") == std::string::npos && | |
| genName.find("Xcode") == std::string::npos && | |
| genName.find("Watcom WMake") == std::string::npos) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<LINK_LANG_AND_ID:lang,id> not supported for this generator."); | |
| return std::string(); | |
| } | |
| if (dagChecker->EvaluatingLinkLibraries()) { | |
| context->HadHeadSensitiveCondition = true; | |
| context->HadLinkLanguageSensitiveCondition = true; | |
| } | |
| const std::string& lang = context->Language; | |
| if (lang == parameters.front()) { | |
| std::vector<std::string> idParameter((parameters.cbegin() + 1), | |
| parameters.cend()); | |
| return LinkerId::Evaluate(idParameter, context, content, lang); | |
| } | |
| return "0"; | |
| } | |
| } linkLanguageAndIdNode; | |
| static const struct HostLinkNode : public cmGeneratorExpressionNode | |
| { | |
| HostLinkNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!context->HeadTarget || !dagChecker || | |
| !dagChecker->EvaluatingLinkOptionsExpression()) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<HOST_LINK:...> may only be used with binary targets " | |
| "to specify link options."); | |
| return std::string(); | |
| } | |
| return context->HeadTarget->IsDeviceLink() ? std::string() | |
| : cmJoin(parameters, ";"); | |
| } | |
| } hostLinkNode; | |
| static const struct DeviceLinkNode : public cmGeneratorExpressionNode | |
| { | |
| DeviceLinkNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!context->HeadTarget || !dagChecker || | |
| !dagChecker->EvaluatingLinkOptionsExpression()) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "$<DEVICE_LINK:...> may only be used with binary targets " | |
| "to specify link options."); | |
| return std::string(); | |
| } | |
| if (context->HeadTarget->IsDeviceLink()) { | |
| std::vector<std::string> list; | |
| cmExpandLists(parameters.begin(), parameters.end(), list); | |
| const auto DL_BEGIN = "<DEVICE_LINK>"_s; | |
| const auto DL_END = "</DEVICE_LINK>"_s; | |
| cm::erase_if(list, [&](const std::string& item) { | |
| return item == DL_BEGIN || item == DL_END; | |
| }); | |
| list.insert(list.begin(), static_cast<std::string>(DL_BEGIN)); | |
| list.push_back(static_cast<std::string>(DL_END)); | |
| return cmJoin(list, ";"); | |
| } | |
| return std::string(); | |
| } | |
| } deviceLinkNode; | |
| std::string getLinkedTargetsContent( | |
| cmGeneratorTarget const* target, std::string const& prop, | |
| cmGeneratorExpressionContext* context, | |
| cmGeneratorExpressionDAGChecker* dagChecker) | |
| { | |
| std::string result; | |
| if (cmLinkImplementationLibraries const* impl = | |
| target->GetLinkImplementationLibraries(context->Config)) { | |
| for (cmLinkImplItem const& lib : impl->Libraries) { | |
| if (lib.Target) { | |
| // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our | |
| // caller's property and hand-evaluate it as if it were compiled. | |
| // Create a context as cmCompiledGeneratorExpression::Evaluate does. | |
| cmGeneratorExpressionContext libContext( | |
| target->GetLocalGenerator(), context->Config, context->Quiet, target, | |
| target, context->EvaluateForBuildsystem, lib.Backtrace, | |
| context->Language); | |
| std::string libResult = | |
| lib.Target->EvaluateInterfaceProperty(prop, &libContext, dagChecker); | |
| if (!libResult.empty()) { | |
| if (result.empty()) { | |
| result = std::move(libResult); | |
| } else { | |
| result.reserve(result.size() + 1 + libResult.size()); | |
| result += ";"; | |
| result += libResult; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return result; | |
| } | |
| static const struct TargetPropertyNode : public cmGeneratorExpressionNode | |
| { | |
| TargetPropertyNode() {} // NOLINT(modernize-use-equals-default) | |
| // This node handles errors on parameter count itself. | |
| int NumExpectedParameters() const override { return OneOrMoreParameters; } | |
| static const char* GetErrorText(std::string const& targetName, | |
| std::string const& propertyName) | |
| { | |
| static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$"); | |
| if (targetName.empty() && propertyName.empty()) { | |
| return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " | |
| "target name and property name."; | |
| } | |
| if (targetName.empty()) { | |
| return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " | |
| "target name."; | |
| } | |
| if (!cmGeneratorExpression::IsValidTargetName(targetName)) { | |
| if (!propertyNameValidator.find(propertyName)) { | |
| return "Target name and property name not supported."; | |
| } | |
| return "Target name not supported."; | |
| } | |
| return nullptr; | |
| } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const override | |
| { | |
| static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$"); | |
| cmGeneratorTarget const* target = nullptr; | |
| std::string targetName; | |
| std::string propertyName; | |
| if (parameters.size() == 2) { | |
| targetName = parameters[0]; | |
| propertyName = parameters[1]; | |
| if (const char* e = GetErrorText(targetName, propertyName)) { | |
| reportError(context, content->GetOriginalExpression(), e); | |
| return std::string(); | |
| } | |
| if (propertyName == "ALIASED_TARGET"_s) { | |
| if (context->LG->GetMakefile()->IsAlias(targetName)) { | |
| if (cmGeneratorTarget* tgt = | |
| context->LG->FindGeneratorTargetToUse(targetName)) { | |
| return tgt->GetName(); | |
| } | |
| } | |
| return std::string(); | |
| } | |
| if (propertyName == "ALIAS_GLOBAL"_s) { | |
| if (context->LG->GetMakefile()->IsAlias(targetName)) { | |
| return context->LG->GetGlobalGenerator()->IsAlias(targetName) | |
| ? "TRUE" | |
| : "FALSE"; | |
| } | |
| return std::string(); | |
| } | |
| target = context->LG->FindGeneratorTargetToUse(targetName); | |
| if (!target) { | |
| std::ostringstream e; | |
| e << "Target \"" << targetName << "\" not found."; | |
| reportError(context, content->GetOriginalExpression(), e.str()); | |
| return std::string(); | |
| } | |
| context->AllTargets.insert(target); | |
| } else if (parameters.size() == 1) { | |
| target = context->HeadTarget; | |
| propertyName = parameters[0]; | |
| // Keep track of the properties seen while processing. | |
| // The evaluation of the LINK_LIBRARIES generator expressions | |
| // will check this to ensure that properties have one consistent | |
| // value for all evaluations. | |
| context->SeenTargetProperties.insert(propertyName); | |
| context->HadHeadSensitiveCondition = true; | |
| if (!target) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<TARGET_PROPERTY:prop> may only be used with binary targets. " | |
| "It may not be used with add_custom_command or add_custom_target. " | |
| " " | |
| "Specify the target to read a property from using the " | |
| "$<TARGET_PROPERTY:tgt,prop> signature instead."); | |
| return std::string(); | |
| } | |
| } else { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<TARGET_PROPERTY:...> expression requires one or two parameters"); | |
| return std::string(); | |
| } | |
| if (propertyName == "SOURCES") { | |
| context->SourceSensitiveTargets.insert(target); | |
| } | |
| if (propertyName.empty()) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<TARGET_PROPERTY:...> expression requires a non-empty property " | |
| "name."); | |
| return std::string(); | |
| } | |
| if (!propertyNameValidator.find(propertyName)) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "Property name not supported."); | |
| return std::string(); | |
| } | |
| assert(target); | |
| if (propertyName == "LINKER_LANGUAGE") { | |
| if (target->LinkLanguagePropagatesToDependents() && dagCheckerParent && | |
| (dagCheckerParent->EvaluatingLinkLibraries() || | |
| dagCheckerParent->EvaluatingSources())) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "LINKER_LANGUAGE target property can not be used while evaluating " | |
| "link libraries for a static library"); | |
| return std::string(); | |
| } | |
| return target->GetLinkerLanguage(context->Config); | |
| } | |
| std::string interfacePropertyName; | |
| bool isInterfaceProperty = false; | |
| #define POPULATE_INTERFACE_PROPERTY_NAME(prop) \ | |
| if (propertyName == #prop) { \ | |
| interfacePropertyName = "INTERFACE_" #prop; \ | |
| } else if (propertyName == "INTERFACE_" #prop) { \ | |
| interfacePropertyName = "INTERFACE_" #prop; \ | |
| isInterfaceProperty = true; \ | |
| } else | |
| CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(POPULATE_INTERFACE_PROPERTY_NAME) | |
| // Note that the above macro terminates with an else | |
| /* else */ if (cmHasLiteralPrefix(propertyName, "COMPILE_DEFINITIONS_")) { | |
| cmPolicies::PolicyStatus polSt = | |
| context->LG->GetPolicyStatus(cmPolicies::CMP0043); | |
| if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) { | |
| interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS"; | |
| } | |
| } | |
| #undef POPULATE_INTERFACE_PROPERTY_NAME | |
| bool evaluatingLinkLibraries = false; | |
| if (dagCheckerParent) { | |
| if (dagCheckerParent->EvaluatingGenexExpression() || | |
| dagCheckerParent->EvaluatingPICExpression()) { | |
| // No check required. | |
| } else if (dagCheckerParent->EvaluatingLinkLibraries()) { | |
| evaluatingLinkLibraries = true; | |
| if (!interfacePropertyName.empty()) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<TARGET_PROPERTY:...> expression in link libraries " | |
| "evaluation depends on target property which is transitive " | |
| "over the link libraries, creating a recursion."); | |
| return std::string(); | |
| } | |
| } else { | |
| #define ASSERT_TRANSITIVE_PROPERTY_METHOD(METHOD) dagCheckerParent->METHOD() || | |
| assert(CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD( | |
| ASSERT_TRANSITIVE_PROPERTY_METHOD) false); // NOLINT(clang-tidy) | |
| #undef ASSERT_TRANSITIVE_PROPERTY_METHOD | |
| } | |
| } | |
| if (isInterfaceProperty) { | |
| return cmGeneratorExpression::StripEmptyListElements( | |
| target->EvaluateInterfaceProperty(propertyName, context, | |
| dagCheckerParent)); | |
| } | |
| cmGeneratorExpressionDAGChecker dagChecker( | |
| context->Backtrace, target, propertyName, content, dagCheckerParent); | |
| switch (dagChecker.Check()) { | |
| case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: | |
| dagChecker.ReportError(context, content->GetOriginalExpression()); | |
| return std::string(); | |
| case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: | |
| // No error. We just skip cyclic references. | |
| return std::string(); | |
| case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: | |
| // We handle transitive properties above. For non-transitive | |
| // properties we accept repeats anyway. | |
| case cmGeneratorExpressionDAGChecker::DAG: | |
| break; | |
| } | |
| std::string result; | |
| bool haveProp = false; | |
| if (cmProp p = target->GetProperty(propertyName)) { | |
| result = *p; | |
| haveProp = true; | |
| } else if (evaluatingLinkLibraries) { | |
| return std::string(); | |
| } | |
| if (!haveProp && !target->IsImported() && | |
| target->GetType() != cmStateEnums::INTERFACE_LIBRARY) { | |
| if (target->IsLinkInterfaceDependentBoolProperty(propertyName, | |
| context->Config)) { | |
| context->HadContextSensitiveCondition = true; | |
| return target->GetLinkInterfaceDependentBoolProperty(propertyName, | |
| context->Config) | |
| ? "1" | |
| : "0"; | |
| } | |
| if (target->IsLinkInterfaceDependentStringProperty(propertyName, | |
| context->Config)) { | |
| context->HadContextSensitiveCondition = true; | |
| const char* propContent = | |
| target->GetLinkInterfaceDependentStringProperty(propertyName, | |
| context->Config); | |
| return propContent ? propContent : ""; | |
| } | |
| if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName, | |
| context->Config)) { | |
| context->HadContextSensitiveCondition = true; | |
| const char* propContent = | |
| target->GetLinkInterfaceDependentNumberMinProperty(propertyName, | |
| context->Config); | |
| return propContent ? propContent : ""; | |
| } | |
| if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName, | |
| context->Config)) { | |
| context->HadContextSensitiveCondition = true; | |
| const char* propContent = | |
| target->GetLinkInterfaceDependentNumberMaxProperty(propertyName, | |
| context->Config); | |
| return propContent ? propContent : ""; | |
| } | |
| } | |
| if (!target->IsImported() && dagCheckerParent && | |
| !dagCheckerParent->EvaluatingLinkLibraries()) { | |
| if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName, | |
| context->Config)) { | |
| context->HadContextSensitiveCondition = true; | |
| const char* propContent = | |
| target->GetLinkInterfaceDependentNumberMinProperty(propertyName, | |
| context->Config); | |
| return propContent ? propContent : ""; | |
| } | |
| if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName, | |
| context->Config)) { | |
| context->HadContextSensitiveCondition = true; | |
| const char* propContent = | |
| target->GetLinkInterfaceDependentNumberMaxProperty(propertyName, | |
| context->Config); | |
| return propContent ? propContent : ""; | |
| } | |
| } | |
| if (!interfacePropertyName.empty()) { | |
| result = cmGeneratorExpression::StripEmptyListElements( | |
| this->EvaluateDependentExpression(result, context->LG, context, target, | |
| &dagChecker, target)); | |
| std::string linkedTargetsContent = getLinkedTargetsContent( | |
| target, interfacePropertyName, context, &dagChecker); | |
| if (!linkedTargetsContent.empty()) { | |
| result += (result.empty() ? "" : ";") + linkedTargetsContent; | |
| } | |
| } | |
| return result; | |
| } | |
| } targetPropertyNode; | |
| static const struct TargetNameNode : public cmGeneratorExpressionNode | |
| { | |
| TargetNameNode() {} // NOLINT(modernize-use-equals-default) | |
| bool GeneratesContent() const override { return true; } | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| bool RequiresLiteralInput() const override { return true; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* /*context*/, | |
| const GeneratorExpressionContent* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return parameters.front(); | |
| } | |
| int NumExpectedParameters() const override { return 1; } | |
| } targetNameNode; | |
| static const struct TargetObjectsNode : public cmGeneratorExpressionNode | |
| { | |
| TargetObjectsNode() {} // NOLINT(modernize-use-equals-default) | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| std::string tgtName = parameters.front(); | |
| cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName); | |
| if (!gt) { | |
| std::ostringstream e; | |
| e << "Objects of target \"" << tgtName | |
| << "\" referenced but no such target exists."; | |
| reportError(context, content->GetOriginalExpression(), e.str()); | |
| return std::string(); | |
| } | |
| cmStateEnums::TargetType type = gt->GetType(); | |
| if (type != cmStateEnums::EXECUTABLE && | |
| type != cmStateEnums::STATIC_LIBRARY && | |
| type != cmStateEnums::SHARED_LIBRARY && | |
| type != cmStateEnums::MODULE_LIBRARY && | |
| type != cmStateEnums::OBJECT_LIBRARY) { | |
| std::ostringstream e; | |
| e << "Objects of target \"" << tgtName | |
| << "\" referenced but is not one of the allowed target types " | |
| << "(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT)."; | |
| reportError(context, content->GetOriginalExpression(), e.str()); | |
| return std::string(); | |
| } | |
| cmGlobalGenerator* gg = context->LG->GetGlobalGenerator(); | |
| { | |
| std::string reason; | |
| if (!context->EvaluateForBuildsystem && | |
| !gg->HasKnownObjectFileLocation(&reason)) { | |
| std::ostringstream e; | |
| e << "The evaluation of the TARGET_OBJECTS generator expression " | |
| "is only suitable for consumption by CMake (limited" | |
| << reason | |
| << "). " | |
| "It is not suitable for writing out elsewhere."; | |
| reportError(context, content->GetOriginalExpression(), e.str()); | |
| return std::string(); | |
| } | |
| } | |
| std::vector<std::string> objects; | |
| if (gt->IsImported()) { | |
| cmProp loc = nullptr; | |
| cmProp imp = nullptr; | |
| std::string suffix; | |
| if (gt->Target->GetMappedConfig(context->Config, loc, imp, suffix)) { | |
| cmExpandList(*loc, objects); | |
| } | |
| context->HadContextSensitiveCondition = true; | |
| } else { | |
| gt->GetTargetObjectNames(context->Config, objects); | |
| std::string obj_dir; | |
| if (context->EvaluateForBuildsystem && !gg->SupportsCrossConfigs()) { | |
| // Use object file directory with buildsystem placeholder. | |
| obj_dir = gt->ObjectDirectory; | |
| context->HadContextSensitiveCondition = | |
| gt->HasContextDependentSources(); | |
| } else { | |
| // Use object file directory with per-config location. | |
| obj_dir = gt->GetObjectDirectory(context->Config); | |
| context->HadContextSensitiveCondition = true; | |
| } | |
| for (std::string& o : objects) { | |
| o = cmStrCat(obj_dir, o); | |
| } | |
| } | |
| // Create the cmSourceFile instances in the referencing directory. | |
| cmMakefile* mf = context->LG->GetMakefile(); | |
| for (std::string const& o : objects) { | |
| mf->AddTargetObject(tgtName, o); | |
| } | |
| return cmJoin(objects, ";"); | |
| } | |
| } targetObjectsNode; | |
| static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode | |
| { | |
| TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default) | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| std::string tgtName = parameters.front(); | |
| cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName); | |
| if (!gt) { | |
| std::ostringstream e; | |
| e << "Objects of target \"" << tgtName | |
| << "\" referenced but no such target exists."; | |
| reportError(context, content->GetOriginalExpression(), e.str()); | |
| return std::string(); | |
| } | |
| cmStateEnums::TargetType type = gt->GetType(); | |
| if (type != cmStateEnums::EXECUTABLE && | |
| type != cmStateEnums::SHARED_LIBRARY && | |
| type != cmStateEnums::MODULE_LIBRARY) { | |
| std::ostringstream e; | |
| e << "Objects of target \"" << tgtName | |
| << "\" referenced but is not one of the allowed target types " | |
| << "(EXECUTABLE, SHARED, MODULE)."; | |
| reportError(context, content->GetOriginalExpression(), e.str()); | |
| return std::string(); | |
| } | |
| if (auto* cli = gt->GetLinkInformation(context->Config)) { | |
| std::vector<std::string> dllPaths; | |
| auto const& dlls = cli->GetRuntimeDLLs(); | |
| for (auto const& dll : dlls) { | |
| if (auto loc = dll->MaybeGetLocation(context->Config)) { | |
| dllPaths.emplace_back(*loc); | |
| } | |
| } | |
| return cmJoin(dllPaths, ";"); | |
| } | |
| return ""; | |
| } | |
| } targetRuntimeDllsNode; | |
| static const struct CompileFeaturesNode : public cmGeneratorExpressionNode | |
| { | |
| CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return OneOrMoreParameters; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| cmGeneratorTarget const* target = context->HeadTarget; | |
| if (!target) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<COMPILE_FEATURE> may only be used with binary targets. It may " | |
| "not be used with add_custom_command or add_custom_target."); | |
| return std::string(); | |
| } | |
| context->HadHeadSensitiveCondition = true; | |
| using LangMap = std::map<std::string, std::vector<std::string>>; | |
| static LangMap availableFeatures; | |
| LangMap testedFeatures; | |
| cmStandardLevelResolver standardResolver(context->LG->GetMakefile()); | |
| for (std::string const& p : parameters) { | |
| std::string error; | |
| std::string lang; | |
| if (!standardResolver.CompileFeatureKnown( | |
| context->HeadTarget->Target->GetName(), p, lang, &error)) { | |
| reportError(context, content->GetOriginalExpression(), error); | |
| return std::string(); | |
| } | |
| testedFeatures[lang].push_back(p); | |
| if (availableFeatures.find(lang) == availableFeatures.end()) { | |
| cmProp featuresKnown = | |
| standardResolver.CompileFeaturesAvailable(lang, &error); | |
| if (!featuresKnown) { | |
| reportError(context, content->GetOriginalExpression(), error); | |
| return std::string(); | |
| } | |
| cmExpandList(featuresKnown, availableFeatures[lang]); | |
| } | |
| } | |
| bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries(); | |
| for (auto const& lit : testedFeatures) { | |
| std::vector<std::string> const& langAvailable = | |
| availableFeatures[lit.first]; | |
| cmProp standardDefault = context->LG->GetMakefile()->GetDefinition( | |
| "CMAKE_" + lit.first + "_STANDARD_DEFAULT"); | |
| for (std::string const& it : lit.second) { | |
| if (!cm::contains(langAvailable, it)) { | |
| return "0"; | |
| } | |
| if (standardDefault && standardDefault->empty()) { | |
| // This compiler has no notion of language standard levels. | |
| // All features known for the language are always available. | |
| continue; | |
| } | |
| if (!standardResolver.HaveStandardAvailable(target, lit.first, | |
| context->Config, it)) { | |
| if (evalLL) { | |
| cmProp l = target->GetLanguageStandard(lit.first, context->Config); | |
| if (!l) { | |
| l = standardDefault; | |
| } | |
| assert(l); | |
| context->MaxLanguageStandard[target][lit.first] = *l; | |
| } else { | |
| return "0"; | |
| } | |
| } | |
| } | |
| } | |
| return "1"; | |
| } | |
| } compileFeaturesNode; | |
| static const char* targetPolicyWhitelist[] = { | |
| nullptr | |
| #define TARGET_POLICY_STRING(POLICY) , #POLICY | |
| CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_STRING) | |
| #undef TARGET_POLICY_STRING | |
| }; | |
| cmPolicies::PolicyStatus statusForTarget(cmGeneratorTarget const* tgt, | |
| const char* policy) | |
| { | |
| #define RETURN_POLICY(POLICY) \ | |
| if (strcmp(policy, #POLICY) == 0) { \ | |
| return tgt->GetPolicyStatus##POLICY(); \ | |
| } | |
| CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY) | |
| #undef RETURN_POLICY | |
| assert(false && "Unreachable code. Not a valid policy"); | |
| return cmPolicies::WARN; | |
| } | |
| cmPolicies::PolicyID policyForString(const char* policy_id) | |
| { | |
| #define RETURN_POLICY_ID(POLICY_ID) \ | |
| if (strcmp(policy_id, #POLICY_ID) == 0) { \ | |
| return cmPolicies::POLICY_ID; \ | |
| } | |
| CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY_ID) | |
| #undef RETURN_POLICY_ID | |
| assert(false && "Unreachable code. Not a valid policy"); | |
| return cmPolicies::CMP0002; | |
| } | |
| static const struct TargetPolicyNode : public cmGeneratorExpressionNode | |
| { | |
| TargetPolicyNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (!context->HeadTarget) { | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<TARGET_POLICY:prop> may only be used with binary targets. It " | |
| "may not be used with add_custom_command or add_custom_target."); | |
| return std::string(); | |
| } | |
| context->HadContextSensitiveCondition = true; | |
| context->HadHeadSensitiveCondition = true; | |
| for (size_t i = 1; i < cm::size(targetPolicyWhitelist); ++i) { | |
| const char* policy = targetPolicyWhitelist[i]; | |
| if (parameters.front() == policy) { | |
| cmLocalGenerator* lg = context->HeadTarget->GetLocalGenerator(); | |
| switch (statusForTarget(context->HeadTarget, policy)) { | |
| case cmPolicies::WARN: | |
| lg->IssueMessage( | |
| MessageType::AUTHOR_WARNING, | |
| cmPolicies::GetPolicyWarning(policyForString(policy))); | |
| CM_FALLTHROUGH; | |
| case cmPolicies::REQUIRED_IF_USED: | |
| case cmPolicies::REQUIRED_ALWAYS: | |
| case cmPolicies::OLD: | |
| return "0"; | |
| case cmPolicies::NEW: | |
| return "1"; | |
| } | |
| } | |
| } | |
| reportError( | |
| context, content->GetOriginalExpression(), | |
| "$<TARGET_POLICY:prop> may only be used with a limited number of " | |
| "policies. Currently it may be used with the following policies:\n" | |
| #define STRINGIFY_HELPER(X) #X | |
| #define STRINGIFY(X) STRINGIFY_HELPER(X) | |
| #define TARGET_POLICY_LIST_ITEM(POLICY) " * " STRINGIFY(POLICY) "\n" | |
| CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_LIST_ITEM) | |
| #undef TARGET_POLICY_LIST_ITEM | |
| ); | |
| return std::string(); | |
| } | |
| } targetPolicyNode; | |
| static const struct InstallPrefixNode : public cmGeneratorExpressionNode | |
| { | |
| InstallPrefixNode() {} // NOLINT(modernize-use-equals-default) | |
| bool GeneratesContent() const override { return true; } | |
| int NumExpectedParameters() const override { return 0; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& /*parameters*/, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| reportError(context, content->GetOriginalExpression(), | |
| "INSTALL_PREFIX is a marker for install(EXPORT) only. It " | |
| "should never be evaluated."); | |
| return std::string(); | |
| } | |
| } installPrefixNode; | |
| class ArtifactDirTag; | |
| class ArtifactLinkerTag; | |
| class ArtifactNameTag; | |
| class ArtifactPathTag; | |
| class ArtifactPdbTag; | |
| class ArtifactSonameTag; | |
| class ArtifactBundleDirTag; | |
| class ArtifactBundleContentDirTag; | |
| template <typename ArtifactT, typename ComponentT> | |
| struct TargetFilesystemArtifactDependency | |
| { | |
| static void AddDependency(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context) | |
| { | |
| context->DependTargets.insert(target); | |
| context->AllTargets.insert(target); | |
| } | |
| }; | |
| struct TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| static void AddDependency(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context) | |
| { | |
| context->AllTargets.insert(target); | |
| cmLocalGenerator* lg = context->LG; | |
| switch (target->GetPolicyStatusCMP0112()) { | |
| case cmPolicies::WARN: | |
| if (lg->GetMakefile()->PolicyOptionalWarningEnabled( | |
| "CMAKE_POLICY_WARNING_CMP0112")) { | |
| std::string err = | |
| cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0112), | |
| "\nDependency being added to target:\n \"", | |
| target->GetName(), "\"\n"); | |
| lg->GetCMakeInstance()->IssueMessage(MessageType ::AUTHOR_WARNING, | |
| err, context->Backtrace); | |
| } | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| context->DependTargets.insert(target); | |
| break; | |
| case cmPolicies::REQUIRED_IF_USED: | |
| case cmPolicies::REQUIRED_ALWAYS: | |
| case cmPolicies::NEW: | |
| break; | |
| } | |
| } | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactNameTag> | |
| : TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactDirTag> | |
| : TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactDependency<ArtifactBundleDirTag, | |
| ArtifactPathTag> | |
| : TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactDependency<ArtifactBundleContentDirTag, | |
| ArtifactPathTag> | |
| : TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFilesystemArtifactResultCreator | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content); | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content) | |
| { | |
| // The target soname file (.so.1). | |
| if (target->IsDLLPlatform()) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_SONAME_FILE is not allowed " | |
| "for DLL target platforms."); | |
| return std::string(); | |
| } | |
| if (target->GetType() != cmStateEnums::SHARED_LIBRARY) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_SONAME_FILE is allowed only for " | |
| "SHARED libraries."); | |
| return std::string(); | |
| } | |
| std::string result = cmStrCat(target->GetDirectory(context->Config), '/', | |
| target->GetSOName(context->Config)); | |
| return result; | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content) | |
| { | |
| if (target->IsImported()) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE not allowed for IMPORTED targets."); | |
| return std::string(); | |
| } | |
| std::string language = target->GetLinkerLanguage(context->Config); | |
| std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB"; | |
| if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE is not supported by the target linker."); | |
| return std::string(); | |
| } | |
| cmStateEnums::TargetType targetType = target->GetType(); | |
| if (targetType != cmStateEnums::SHARED_LIBRARY && | |
| targetType != cmStateEnums::MODULE_LIBRARY && | |
| targetType != cmStateEnums::EXECUTABLE) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE is allowed only for " | |
| "targets with linker created artifacts."); | |
| return std::string(); | |
| } | |
| std::string result = cmStrCat(target->GetPDBDirectory(context->Config), | |
| '/', target->GetPDBName(context->Config)); | |
| return result; | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content) | |
| { | |
| // The file used to link to the target (.so, .lib, .a). | |
| if (!target->IsLinkable()) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_LINKER_FILE is allowed only for libraries and " | |
| "executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| cmStateEnums::ArtifactType artifact = | |
| target->HasImportLibrary(context->Config) | |
| ? cmStateEnums::ImportLibraryArtifact | |
| : cmStateEnums::RuntimeBinaryArtifact; | |
| return target->GetFullPath(context->Config, artifact); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content) | |
| { | |
| if (target->IsImported()) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_BUNDLE_DIR not allowed for IMPORTED targets."); | |
| return std::string(); | |
| } | |
| if (!target->IsBundleOnApple()) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_BUNDLE_DIR is allowed only for Bundle targets."); | |
| return std::string(); | |
| } | |
| std::string outpath = target->GetDirectory(context->Config) + '/'; | |
| return target->BuildBundleDirectory(outpath, context->Config, | |
| cmGeneratorTarget::BundleDirLevel); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactBundleContentDirTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content) | |
| { | |
| if (target->IsImported()) { | |
| ::reportError( | |
| context, content->GetOriginalExpression(), | |
| "TARGET_BUNDLE_CONTENT_DIR not allowed for IMPORTED targets."); | |
| return std::string(); | |
| } | |
| if (!target->IsBundleOnApple()) { | |
| ::reportError( | |
| context, content->GetOriginalExpression(), | |
| "TARGET_BUNDLE_CONTENT_DIR is allowed only for Bundle targets."); | |
| return std::string(); | |
| } | |
| std::string outpath = target->GetDirectory(context->Config) + '/'; | |
| return target->BuildBundleDirectory(outpath, context->Config, | |
| cmGeneratorTarget::ContentLevel); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactNameTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* /*unused*/) | |
| { | |
| return target->GetFullPath(context->Config, | |
| cmStateEnums::RuntimeBinaryArtifact, true); | |
| } | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFilesystemArtifactResultGetter | |
| { | |
| static std::string Get(const std::string& result); | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultGetter<ArtifactNameTag> | |
| { | |
| static std::string Get(const std::string& result) | |
| { | |
| return cmSystemTools::GetFilenameName(result); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultGetter<ArtifactDirTag> | |
| { | |
| static std::string Get(const std::string& result) | |
| { | |
| return cmSystemTools::GetFilenamePath(result); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultGetter<ArtifactPathTag> | |
| { | |
| static std::string Get(const std::string& result) { return result; } | |
| }; | |
| struct TargetArtifactBase : public cmGeneratorExpressionNode | |
| { | |
| TargetArtifactBase() {} // NOLINT(modernize-use-equals-default) | |
| protected: | |
| cmGeneratorTarget* GetTarget( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const | |
| { | |
| // Lookup the referenced target. | |
| std::string name = parameters.front(); | |
| if (!cmGeneratorExpression::IsValidTargetName(name)) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return nullptr; | |
| } | |
| cmGeneratorTarget* target = context->LG->FindGeneratorTargetToUse(name); | |
| if (!target) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "No target \"" + name + "\""); | |
| return nullptr; | |
| } | |
| if (target->GetType() >= cmStateEnums::OBJECT_LIBRARY && | |
| target->GetType() != cmStateEnums::UNKNOWN_LIBRARY) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "Target \"" + name + | |
| "\" is not an executable or library."); | |
| return nullptr; | |
| } | |
| if (dagChecker && | |
| (dagChecker->EvaluatingLinkLibraries(target) || | |
| (dagChecker->EvaluatingSources() && | |
| target == dagChecker->TopTarget()))) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "Expressions which require the linker language may not " | |
| "be used while evaluating link libraries"); | |
| return nullptr; | |
| } | |
| return target; | |
| } | |
| }; | |
| template <typename ArtifactT, typename ComponentT> | |
| struct TargetFilesystemArtifact : public TargetArtifactBase | |
| { | |
| TargetFilesystemArtifact() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| cmGeneratorTarget* target = | |
| this->GetTarget(parameters, context, content, dagChecker); | |
| if (!target) { | |
| return std::string(); | |
| } | |
| // Not a dependent target if we are querying for ArtifactDirTag, | |
| // ArtifactNameTag, ArtifactBundleDirTag, and ArtifactBundleContentDirTag | |
| TargetFilesystemArtifactDependency<ArtifactT, ComponentT>::AddDependency( | |
| target, context); | |
| std::string result = | |
| TargetFilesystemArtifactResultCreator<ArtifactT>::Create(target, context, | |
| content); | |
| if (context->HadError) { | |
| return std::string(); | |
| } | |
| return TargetFilesystemArtifactResultGetter<ComponentT>::Get(result); | |
| } | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFilesystemArtifactNodeGroup | |
| { | |
| TargetFilesystemArtifactNodeGroup() // NOLINT(modernize-use-equals-default) | |
| { | |
| } | |
| TargetFilesystemArtifact<ArtifactT, ArtifactPathTag> File; | |
| TargetFilesystemArtifact<ArtifactT, ArtifactNameTag> FileName; | |
| TargetFilesystemArtifact<ArtifactT, ArtifactDirTag> FileDir; | |
| }; | |
| static const TargetFilesystemArtifactNodeGroup<ArtifactNameTag> | |
| targetNodeGroup; | |
| static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag> | |
| targetLinkerNodeGroup; | |
| static const TargetFilesystemArtifactNodeGroup<ArtifactSonameTag> | |
| targetSoNameNodeGroup; | |
| static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag> | |
| targetPdbNodeGroup; | |
| static const TargetFilesystemArtifact<ArtifactBundleDirTag, ArtifactPathTag> | |
| targetBundleDirNode; | |
| static const TargetFilesystemArtifact<ArtifactBundleContentDirTag, | |
| ArtifactPathTag> | |
| targetBundleContentDirNode; | |
| // | |
| // To retrieve base name for various artifacts | |
| // | |
| template <typename ArtifactT> | |
| struct TargetOutputNameArtifactResultGetter | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content); | |
| }; | |
| template <> | |
| struct TargetOutputNameArtifactResultGetter<ArtifactNameTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* /*unused*/) | |
| { | |
| return target->GetOutputName(context->Config, | |
| cmStateEnums::RuntimeBinaryArtifact) + | |
| target->GetFilePostfix(context->Config); | |
| } | |
| }; | |
| template <> | |
| struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content) | |
| { | |
| // The file used to link to the target (.so, .lib, .a). | |
| if (!target->IsLinkable()) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_LINKER_FILE_BASE_NAME is allowed only for " | |
| "libraries and executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| cmStateEnums::ArtifactType artifact = | |
| target->HasImportLibrary(context->Config) | |
| ? cmStateEnums::ImportLibraryArtifact | |
| : cmStateEnums::RuntimeBinaryArtifact; | |
| return target->GetOutputName(context->Config, artifact) + | |
| target->GetFilePostfix(context->Config); | |
| } | |
| }; | |
| template <> | |
| struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content) | |
| { | |
| if (target->IsImported()) { | |
| ::reportError( | |
| context, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets."); | |
| return std::string(); | |
| } | |
| std::string language = target->GetLinkerLanguage(context->Config); | |
| std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB"; | |
| if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) { | |
| ::reportError( | |
| context, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE_BASE_NAME is not supported by the target linker."); | |
| return std::string(); | |
| } | |
| cmStateEnums::TargetType targetType = target->GetType(); | |
| if (targetType != cmStateEnums::SHARED_LIBRARY && | |
| targetType != cmStateEnums::MODULE_LIBRARY && | |
| targetType != cmStateEnums::EXECUTABLE) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE_BASE_NAME is allowed only for " | |
| "targets with linker created artifacts."); | |
| return std::string(); | |
| } | |
| return target->GetPDBOutputName(context->Config) + | |
| target->GetFilePostfix(context->Config); | |
| } | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFileBaseNameArtifact : public TargetArtifactBase | |
| { | |
| TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| cmGeneratorTarget* target = | |
| this->GetTarget(parameters, context, content, dagChecker); | |
| if (!target) { | |
| return std::string(); | |
| } | |
| std::string result = TargetOutputNameArtifactResultGetter<ArtifactT>::Get( | |
| target, context, content); | |
| if (context->HadError) { | |
| return std::string(); | |
| } | |
| return result; | |
| } | |
| }; | |
| static const TargetFileBaseNameArtifact<ArtifactNameTag> | |
| targetFileBaseNameNode; | |
| static const TargetFileBaseNameArtifact<ArtifactLinkerTag> | |
| targetLinkerFileBaseNameNode; | |
| static const TargetFileBaseNameArtifact<ArtifactPdbTag> | |
| targetPdbFileBaseNameNode; | |
| class ArtifactFilePrefixTag; | |
| class ArtifactLinkerFilePrefixTag; | |
| class ArtifactFileSuffixTag; | |
| class ArtifactLinkerFileSuffixTag; | |
| template <typename ArtifactT> | |
| struct TargetFileArtifactResultGetter | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content); | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent*) | |
| { | |
| return target->GetFilePrefix(context->Config); | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content) | |
| { | |
| if (!target->IsLinkable()) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_LINKER_PREFIX is allowed only for libraries and " | |
| "executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| cmStateEnums::ArtifactType artifact = | |
| target->HasImportLibrary(context->Config) | |
| ? cmStateEnums::ImportLibraryArtifact | |
| : cmStateEnums::RuntimeBinaryArtifact; | |
| return target->GetFilePrefix(context->Config, artifact); | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent*) | |
| { | |
| return target->GetFileSuffix(context->Config); | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content) | |
| { | |
| if (!target->IsLinkable()) { | |
| ::reportError(context, content->GetOriginalExpression(), | |
| "TARGET_LINKER_SUFFIX is allowed only for libraries and " | |
| "executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| cmStateEnums::ArtifactType artifact = | |
| target->HasImportLibrary(context->Config) | |
| ? cmStateEnums::ImportLibraryArtifact | |
| : cmStateEnums::RuntimeBinaryArtifact; | |
| return target->GetFileSuffix(context->Config, artifact); | |
| } | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFileArtifact : public TargetArtifactBase | |
| { | |
| TargetFileArtifact() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| cmGeneratorTarget* target = | |
| this->GetTarget(parameters, context, content, dagChecker); | |
| if (!target) { | |
| return std::string(); | |
| } | |
| std::string result = | |
| TargetFileArtifactResultGetter<ArtifactT>::Get(target, context, content); | |
| if (context->HadError) { | |
| return std::string(); | |
| } | |
| return result; | |
| } | |
| }; | |
| static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode; | |
| static const TargetFileArtifact<ArtifactLinkerFilePrefixTag> | |
| targetLinkerFilePrefixNode; | |
| static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode; | |
| static const TargetFileArtifact<ArtifactLinkerFileSuffixTag> | |
| targetLinkerFileSuffixNode; | |
| static const struct ShellPathNode : public cmGeneratorExpressionNode | |
| { | |
| ShellPathNode() {} // NOLINT(modernize-use-equals-default) | |
| std::string Evaluate( | |
| const std::vector<std::string>& parameters, | |
| cmGeneratorExpressionContext* context, | |
| const GeneratorExpressionContent* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| std::vector<std::string> listIn = cmExpandedList(parameters.front()); | |
| if (listIn.empty()) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "\"\" is not an absolute path."); | |
| return std::string(); | |
| } | |
| cmStateSnapshot snapshot = context->LG->GetStateSnapshot(); | |
| cmOutputConverter converter(snapshot); | |
| const char* separator = snapshot.GetState()->UseWindowsShell() ? ";" : ":"; | |
| std::vector<std::string> listOut; | |
| listOut.reserve(listIn.size()); | |
| for (auto const& in : listIn) { | |
| if (!cmSystemTools::FileIsFullPath(in)) { | |
| reportError(context, content->GetOriginalExpression(), | |
| "\"" + in + "\" is not an absolute path."); | |
| return std::string(); | |
| } | |
| listOut.emplace_back(converter.ConvertDirectorySeparatorsForShell(in)); | |
| } | |
| return cmJoin(listOut, separator); | |
| } | |
| } shellPathNode; | |
| const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( | |
| const std::string& identifier) | |
| { | |
| static std::map<std::string, cmGeneratorExpressionNode const*> const nodeMap{ | |
| { "0", &zeroNode }, | |
| { "1", &oneNode }, | |
| { "AND", &andNode }, | |
| { "OR", &orNode }, | |
| { "NOT", ¬Node }, | |
| { "C_COMPILER_ID", &cCompilerIdNode }, | |
| { "CXX_COMPILER_ID", &cxxCompilerIdNode }, | |
| { "OBJC_COMPILER_ID", &objcCompilerIdNode }, | |
| { "OBJCXX_COMPILER_ID", &objcxxCompilerIdNode }, | |
| { "CUDA_COMPILER_ID", &cudaCompilerIdNode }, | |
| { "Fortran_COMPILER_ID", &fortranCompilerIdNode }, | |
| { "HIP_COMPILER_ID", &hipCompilerIdNode }, | |
| { "VERSION_GREATER", &versionGreaterNode }, | |
| { "VERSION_GREATER_EQUAL", &versionGreaterEqNode }, | |
| { "VERSION_LESS", &versionLessNode }, | |
| { "VERSION_LESS_EQUAL", &versionLessEqNode }, | |
| { "VERSION_EQUAL", &versionEqualNode }, | |
| { "C_COMPILER_VERSION", &cCompilerVersionNode }, | |
| { "CXX_COMPILER_VERSION", &cxxCompilerVersionNode }, | |
| { "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode }, | |
| { "OBJC_COMPILER_VERSION", &objcCompilerVersionNode }, | |
| { "OBJCXX_COMPILER_VERSION", &objcxxCompilerVersionNode }, | |
| { "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode }, | |
| { "HIP_COMPILER_VERSION", &hipCompilerVersionNode }, | |
| { "PLATFORM_ID", &platformIdNode }, | |
| { "COMPILE_FEATURES", &compileFeaturesNode }, | |
| { "CONFIGURATION", &configurationNode }, | |
| { "CONFIG", &configurationTestNode }, | |
| { "TARGET_FILE", &targetNodeGroup.File }, | |
| { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File }, | |
| { "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File }, | |
| { "TARGET_PDB_FILE", &targetPdbNodeGroup.File }, | |
| { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode }, | |
| { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode }, | |
| { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode }, | |
| { "TARGET_FILE_PREFIX", &targetFilePrefixNode }, | |
| { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode }, | |
| { "TARGET_FILE_SUFFIX", &targetFileSuffixNode }, | |
| { "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode }, | |
| { "TARGET_FILE_NAME", &targetNodeGroup.FileName }, | |
| { "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.FileName }, | |
| { "TARGET_SONAME_FILE_NAME", &targetSoNameNodeGroup.FileName }, | |
| { "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName }, | |
| { "TARGET_FILE_DIR", &targetNodeGroup.FileDir }, | |
| { "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.FileDir }, | |
| { "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.FileDir }, | |
| { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir }, | |
| { "TARGET_BUNDLE_DIR", &targetBundleDirNode }, | |
| { "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode }, | |
| { "STREQUAL", &strEqualNode }, | |
| { "EQUAL", &equalNode }, | |
| { "IN_LIST", &inListNode }, | |
| { "FILTER", &filterNode }, | |
| { "REMOVE_DUPLICATES", &removeDuplicatesNode }, | |
| { "LOWER_CASE", &lowerCaseNode }, | |
| { "UPPER_CASE", &upperCaseNode }, | |
| { "MAKE_C_IDENTIFIER", &makeCIdentifierNode }, | |
| { "BOOL", &boolNode }, | |
| { "IF", &ifNode }, | |
| { "ANGLE-R", &angle_rNode }, | |
| { "COMMA", &commaNode }, | |
| { "SEMICOLON", &semicolonNode }, | |
| { "TARGET_PROPERTY", &targetPropertyNode }, | |
| { "TARGET_NAME", &targetNameNode }, | |
| { "TARGET_OBJECTS", &targetObjectsNode }, | |
| { "TARGET_POLICY", &targetPolicyNode }, | |
| { "TARGET_EXISTS", &targetExistsNode }, | |
| { "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode }, | |
| { "TARGET_GENEX_EVAL", &targetGenexEvalNode }, | |
| { "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode }, | |
| { "GENEX_EVAL", &genexEvalNode }, | |
| { "BUILD_INTERFACE", &buildInterfaceNode }, | |
| { "INSTALL_INTERFACE", &installInterfaceNode }, | |
| { "INSTALL_PREFIX", &installPrefixNode }, | |
| { "JOIN", &joinNode }, | |
| { "LINK_ONLY", &linkOnlyNode }, | |
| { "COMPILE_LANG_AND_ID", &languageAndIdNode }, | |
| { "COMPILE_LANGUAGE", &languageNode }, | |
| { "LINK_LANG_AND_ID", &linkLanguageAndIdNode }, | |
| { "LINK_LANGUAGE", &linkLanguageNode }, | |
| { "HOST_LINK", &hostLinkNode }, | |
| { "DEVICE_LINK", &deviceLinkNode }, | |
| { "SHELL_PATH", &shellPathNode } | |
| }; | |
| { | |
| auto itr = nodeMap.find(identifier); | |
| if (itr != nodeMap.end()) { | |
| return itr->second; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| void reportError(cmGeneratorExpressionContext* context, | |
| const std::string& expr, const std::string& result) | |
| { | |
| context->HadError = true; | |
| if (context->Quiet) { | |
| return; | |
| } | |
| std::ostringstream e; | |
| /* clang-format off */ | |
| e << "Error evaluating generator expression:\n" | |
| << " " << expr << "\n" | |
| << result; | |
| /* clang-format on */ | |
| context->LG->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, | |
| e.str(), context->Backtrace); | |
| } |