diff options
Diffstat (limited to 'Source/cmGeneratorExpressionNode.cxx')
| -rw-r--r-- | Source/cmGeneratorExpressionNode.cxx | 2570 |
1 files changed, 2570 insertions, 0 deletions
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx new file mode 100644 index 0000000..e4fb67e --- /dev/null +++ b/Source/cmGeneratorExpressionNode.cxx @@ -0,0 +1,2570 @@ +/* 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/string_view> +#include <cm/vector> +#include <cmext/algorithm> +#include <cmext/string_view> + +#include "cmsys/RegularExpression.hxx" +#include "cmsys/String.h" + +#include "cmAlgorithms.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 "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& 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"); + +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"); + +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& 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 OneOrZeroParameters; } + + 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; + if (context->Config.empty()) { + return parameters.front().empty() ? "1" : "0"; + } + + if (cmsysString_strcasecmp(parameters.front().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); + return cm::contains(mappedConfigs, + cmSystemTools::UpperCase(parameters.front())) + ? "1" + : "0"; + } + } + } + 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()) { + 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& 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()) { + // 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& 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& 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 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 = 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 an allowed library types (EXECUTABLE, " + << "STATIC, SHARED, MODULE, OBJECT)."; + reportError(context, content->GetOriginalExpression(), e.str()); + return std::string(); + } + if (!context->EvaluateForBuildsystem) { + cmGlobalGenerator* gg = context->LG->GetGlobalGenerator(); + std::string reason; + if (!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) { + // Use object file directory with buildsystem placeholder. + obj_dir = gt->ObjectDirectory; + // Here we assume that the set of object files produced + // by an object library does not vary with configuration + // and do not set HadContextSensitiveCondition to true. + } 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& o : objects) { + mf->AddTargetObject(tgtName, o); + } + + return cmJoin(objects, ";"); + } +} targetObjectsNode; + +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; + + for (std::string const& p : parameters) { + std::string error; + std::string lang; + if (!context->LG->GetMakefile()->CompileFeatureKnown( + context->HeadTarget->Target, p, lang, &error)) { + reportError(context, content->GetOriginalExpression(), error); + return std::string(); + } + testedFeatures[lang].push_back(p); + + if (availableFeatures.find(lang) == availableFeatures.end()) { + const char* featuresKnown = + context->LG->GetMakefile()->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()->GetDef( + "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 (!context->LG->GetMakefile()->HaveStandardAvailable( + target->Target, lit.first, it)) { + if (evalLL) { + cmProp l = target->GetProperty(lit.first + "_STANDARD"); + 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> +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(); + } + context->DependTargets.insert(target); + context->AllTargets.insert(target); + + 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 }, + { "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 }, + { "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 }, + { "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); +} |
