summaryrefslogtreecommitdiffstats
path: root/Source/cmGeneratorExpressionNode.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmGeneratorExpressionNode.cxx')
-rw-r--r--Source/cmGeneratorExpressionNode.cxx1892
1 files changed, 1892 insertions, 0 deletions
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
new file mode 100644
index 0000000..3d311d6
--- /dev/null
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -0,0 +1,1892 @@
+/* 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 "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 "cmOutputConverter.h"
+#include "cmPolicies.h"
+#include "cmStateTypes.h"
+#include "cmSystemTools.h"
+#include "cmTarget.h"
+#include "cmake.h"
+
+#include "cmsys/RegularExpression.hxx"
+#include "cmsys/String.h"
+#include <algorithm>
+#include <assert.h>
+#include <errno.h>
+#include <map>
+#include <memory> // IWYU pragma: keep
+#include <set>
+#include <sstream>
+#include <stdlib.h>
+#include <string.h>
+#include <utility>
+
+std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
+ std::string const& prop, cmLocalGenerator* lg,
+ cmGeneratorExpressionContext* context, cmGeneratorTarget const* headTarget,
+ cmGeneratorTarget const* currentTarget,
+ cmGeneratorExpressionDAGChecker* dagChecker)
+{
+ cmGeneratorExpression ge(context->Backtrace);
+ std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop);
+ cge->SetEvaluateForBuildsystem(context->EvaluateForBuildsystem);
+ std::string result =
+ cge->Evaluate(lg, context->Config, context->Quiet, headTarget,
+ currentTarget, dagChecker, context->Language);
+ if (cge->GetHadContextSensitiveCondition()) {
+ context->HadContextSensitiveCondition = true;
+ }
+ if (cge->GetHadHeadSensitiveCondition()) {
+ context->HadHeadSensitiveCondition = true;
+ }
+ return result;
+}
+
+static const struct ZeroNode : public cmGeneratorExpressionNode
+{
+ ZeroNode() {}
+
+ 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() {}
+
+ 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;
+
+#define BOOLEAN_OP_NODE(OPNAME, OP, SUCCESS_VALUE, FAILURE_VALUE) \
+ static const struct OP##Node : public cmGeneratorExpressionNode \
+ { \
+ OP##Node() {} \
+ virtual int NumExpectedParameters() const { return OneOrMoreParameters; } \
+ \
+ std::string Evaluate(const std::vector<std::string>& parameters, \
+ cmGeneratorExpressionContext* context, \
+ const GeneratorExpressionContent* content, \
+ cmGeneratorExpressionDAGChecker*) const \
+ { \
+ std::vector<std::string>::const_iterator it = parameters.begin(); \
+ const std::vector<std::string>::const_iterator end = parameters.end(); \
+ for (; it != end; ++it) { \
+ if (*it == #FAILURE_VALUE) { \
+ return #FAILURE_VALUE; \
+ } \
+ if (*it != #SUCCESS_VALUE) { \
+ reportError(context, content->GetOriginalExpression(), \
+ "Parameters to $<" #OP \
+ "> must resolve to either '0' or '1'."); \
+ return std::string(); \
+ } \
+ } \
+ return #SUCCESS_VALUE; \
+ } \
+ } OPNAME;
+
+BOOLEAN_OP_NODE(andNode, AND, 1, 0)
+BOOLEAN_OP_NODE(orNode, OR, 0, 1)
+
+#undef BOOLEAN_OP_NODE
+
+static const struct NotNode : public cmGeneratorExpressionNode
+{
+ NotNode() {}
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+ {
+ if (*parameters.begin() != "0" && *parameters.begin() != "1") {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<NOT> parameter must resolve to exactly one '0' or '1' value.");
+ return std::string();
+ }
+ return *parameters.begin() == "0" ? "1" : "0";
+ }
+} notNode;
+
+static const struct BoolNode : public cmGeneratorExpressionNode
+{
+ BoolNode() {}
+
+ 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 !cmSystemTools::IsOff(parameters.begin()->c_str()) ? "1" : "0";
+ }
+} boolNode;
+
+static const struct IfNode : public cmGeneratorExpressionNode
+{
+ IfNode() {}
+
+ 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() {}
+
+ 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.begin() == parameters[1] ? "1" : "0";
+ }
+} strEqualNode;
+
+static const struct EqualNode : public cmGeneratorExpressionNode
+{
+ EqualNode() {}
+
+ int NumExpectedParameters() const override { return 2; }
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+ {
+ char* pEnd;
+
+ int base = 0;
+ bool flipSign = false;
+
+ const char* lhs = parameters[0].c_str();
+ if (cmHasLiteralPrefix(lhs, "0b") || cmHasLiteralPrefix(lhs, "0B")) {
+ base = 2;
+ lhs += 2;
+ }
+ if (cmHasLiteralPrefix(lhs, "-0b") || cmHasLiteralPrefix(lhs, "-0B")) {
+ base = 2;
+ lhs += 3;
+ flipSign = true;
+ }
+ if (cmHasLiteralPrefix(lhs, "+0b") || cmHasLiteralPrefix(lhs, "+0B")) {
+ base = 2;
+ lhs += 3;
+ }
+
+ long lnum = strtol(lhs, &pEnd, base);
+ if (pEnd == lhs || *pEnd != '\0' || errno == ERANGE) {
+ reportError(context, content->GetOriginalExpression(),
+ "$<EQUAL> parameter " + parameters[0] +
+ " is not a valid integer.");
+ return std::string();
+ }
+
+ if (flipSign) {
+ lnum = -lnum;
+ }
+
+ base = 0;
+ flipSign = false;
+
+ const char* rhs = parameters[1].c_str();
+ if (cmHasLiteralPrefix(rhs, "0b") || cmHasLiteralPrefix(rhs, "0B")) {
+ base = 2;
+ rhs += 2;
+ }
+ if (cmHasLiteralPrefix(rhs, "-0b") || cmHasLiteralPrefix(rhs, "-0B")) {
+ base = 2;
+ rhs += 3;
+ flipSign = true;
+ }
+ if (cmHasLiteralPrefix(rhs, "+0b") || cmHasLiteralPrefix(rhs, "+0B")) {
+ base = 2;
+ rhs += 3;
+ }
+
+ long rnum = strtol(rhs, &pEnd, base);
+ if (pEnd == rhs || *pEnd != '\0' || errno == ERANGE) {
+ reportError(context, content->GetOriginalExpression(),
+ "$<EQUAL> parameter " + parameters[1] +
+ " is not a valid integer.");
+ return std::string();
+ }
+
+ if (flipSign) {
+ rnum = -rnum;
+ }
+
+ return lnum == rnum ? "1" : "0";
+ }
+} equalNode;
+
+static const struct LowerCaseNode : public cmGeneratorExpressionNode
+{
+ LowerCaseNode() {}
+
+ 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() {}
+
+ 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() {}
+
+ 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;
+
+static const struct Angle_RNode : public cmGeneratorExpressionNode
+{
+ Angle_RNode() {}
+
+ 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 ">";
+ }
+} angle_rNode;
+
+static const struct CommaNode : public cmGeneratorExpressionNode
+{
+ CommaNode() {}
+
+ 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 ",";
+ }
+} commaNode;
+
+static const struct SemicolonNode : public cmGeneratorExpressionNode
+{
+ SemicolonNode() {}
+
+ 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 ";";
+ }
+} semicolonNode;
+
+struct CompilerIdNode : public cmGeneratorExpressionNode
+{
+ CompilerIdNode() {}
+
+ int NumExpectedParameters() const override { return OneOrZeroParameters; }
+
+ std::string EvaluateWithLanguage(const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* /*unused*/,
+ const std::string& lang) const
+ {
+ const char* compilerId = context->LG->GetMakefile()->GetSafeDefinition(
+ "CMAKE_" + lang + "_COMPILER_ID");
+ if (parameters.empty()) {
+ return compilerId ? compilerId : "";
+ }
+ static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$");
+ if (!compilerIdValidator.find(*parameters.begin())) {
+ reportError(context, content->GetOriginalExpression(),
+ "Expression syntax not recognized.");
+ return std::string();
+ }
+ if (!compilerId) {
+ return parameters.front().empty() ? "1" : "0";
+ }
+
+ if (strcmp(parameters.begin()->c_str(), compilerId) == 0) {
+ return "1";
+ }
+
+ if (cmsysString_strcasecmp(parameters.begin()->c_str(), compilerId) == 0) {
+ switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) {
+ case cmPolicies::WARN: {
+ std::ostringstream e;
+ e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0044);
+ context->LG->GetCMakeInstance()->IssueMessage(
+ cmake::AUTHOR_WARNING, e.str(), context->Backtrace);
+ CM_FALLTHROUGH;
+ }
+ case cmPolicies::OLD:
+ return "1";
+ case cmPolicies::NEW:
+ case cmPolicies::REQUIRED_ALWAYS:
+ case cmPolicies::REQUIRED_IF_USED:
+ break;
+ }
+ }
+ return "0";
+ }
+};
+
+static const struct CCompilerIdNode : public CompilerIdNode
+{
+ CCompilerIdNode() {}
+
+ 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(),
+ "$<C_COMPILER_ID> may only be used with binary targets. It may "
+ "not be used with add_custom_command or add_custom_target.");
+ return std::string();
+ }
+ return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
+ "C");
+ }
+} cCompilerIdNode;
+
+static const struct CXXCompilerIdNode : public CompilerIdNode
+{
+ CXXCompilerIdNode() {}
+
+ 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(),
+ "$<CXX_COMPILER_ID> may only be used with binary targets. It may "
+ "not be used with add_custom_command or add_custom_target.");
+ return std::string();
+ }
+ return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
+ "CXX");
+ }
+} cxxCompilerIdNode;
+
+struct CompilerVersionNode : public cmGeneratorExpressionNode
+{
+ CompilerVersionNode() {}
+
+ int NumExpectedParameters() const override { return OneOrZeroParameters; }
+
+ std::string EvaluateWithLanguage(const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* /*unused*/,
+ const std::string& lang) const
+ {
+ const char* compilerVersion =
+ context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
+ "_COMPILER_VERSION");
+ if (parameters.empty()) {
+ return compilerVersion ? compilerVersion : "";
+ }
+
+ static cmsys::RegularExpression compilerIdValidator("^[0-9\\.]*$");
+ if (!compilerIdValidator.find(*parameters.begin())) {
+ reportError(context, content->GetOriginalExpression(),
+ "Expression syntax not recognized.");
+ return std::string();
+ }
+ if (!compilerVersion) {
+ return parameters.front().empty() ? "1" : "0";
+ }
+
+ return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL,
+ parameters.begin()->c_str(),
+ compilerVersion)
+ ? "1"
+ : "0";
+ }
+};
+
+static const struct CCompilerVersionNode : public CompilerVersionNode
+{
+ CCompilerVersionNode() {}
+
+ 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(),
+ "$<C_COMPILER_VERSION> may only be used with binary targets. It "
+ "may not be used with add_custom_command or add_custom_target.");
+ return std::string();
+ }
+ return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
+ "C");
+ }
+} cCompilerVersionNode;
+
+static const struct CxxCompilerVersionNode : public CompilerVersionNode
+{
+ CxxCompilerVersionNode() {}
+
+ 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(),
+ "$<CXX_COMPILER_VERSION> may only be used with binary targets. It "
+ "may not be used with add_custom_command or add_custom_target.");
+ return std::string();
+ }
+ return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
+ "CXX");
+ }
+} cxxCompilerVersionNode;
+
+struct PlatformIdNode : public cmGeneratorExpressionNode
+{
+ PlatformIdNode() {}
+
+ int NumExpectedParameters() const override { return OneOrZeroParameters; }
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* /*content*/,
+ cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+ {
+ const char* platformId =
+ context->LG->GetMakefile()->GetSafeDefinition("CMAKE_SYSTEM_NAME");
+ if (parameters.empty()) {
+ return platformId ? platformId : "";
+ }
+
+ if (!platformId) {
+ return parameters.front().empty() ? "1" : "0";
+ }
+
+ if (strcmp(parameters.begin()->c_str(), platformId) == 0) {
+ return "1";
+ }
+ return "0";
+ }
+} platformIdNode;
+
+static const struct VersionGreaterNode : public cmGeneratorExpressionNode
+{
+ VersionGreaterNode() {}
+
+ 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(cmSystemTools::OP_GREATER,
+ parameters.front().c_str(),
+ parameters[1].c_str())
+ ? "1"
+ : "0";
+ }
+} versionGreaterNode;
+
+static const struct VersionGreaterEqNode : public cmGeneratorExpressionNode
+{
+ VersionGreaterEqNode() {}
+
+ 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(cmSystemTools::OP_GREATER_EQUAL,
+ parameters.front().c_str(),
+ parameters[1].c_str())
+ ? "1"
+ : "0";
+ }
+} versionGreaterEqNode;
+
+static const struct VersionLessNode : public cmGeneratorExpressionNode
+{
+ VersionLessNode() {}
+
+ 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(cmSystemTools::OP_LESS,
+ parameters.front().c_str(),
+ parameters[1].c_str())
+ ? "1"
+ : "0";
+ }
+} versionLessNode;
+
+static const struct VersionLessEqNode : public cmGeneratorExpressionNode
+{
+ VersionLessEqNode() {}
+
+ 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(cmSystemTools::OP_LESS_EQUAL,
+ parameters.front().c_str(),
+ parameters[1].c_str())
+ ? "1"
+ : "0";
+ }
+} versionLessEqNode;
+
+static const struct VersionEqualNode : public cmGeneratorExpressionNode
+{
+ VersionEqualNode() {}
+
+ 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(cmSystemTools::OP_EQUAL,
+ parameters.front().c_str(),
+ parameters[1].c_str())
+ ? "1"
+ : "0";
+ }
+} versionEqualNode;
+
+static const struct LinkOnlyNode : public cmGeneratorExpressionNode
+{
+ LinkOnlyNode() {}
+
+ 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() {}
+
+ 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() {}
+
+ 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.begin())) {
+ 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.begin()->c_str(),
+ context->Config.c_str()) == 0) {
+ return "1";
+ }
+
+ if (context->CurrentTarget && context->CurrentTarget->IsImported()) {
+ const char* loc = nullptr;
+ const char* 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 = "MAP_IMPORTED_CONFIG_";
+ mapProp += cmSystemTools::UpperCase(context->Config);
+ if (const char* mapValue =
+ context->CurrentTarget->GetProperty(mapProp)) {
+ cmSystemTools::ExpandListArgument(cmSystemTools::UpperCase(mapValue),
+ mappedConfigs);
+ return std::find(mappedConfigs.begin(), mappedConfigs.end(),
+ cmSystemTools::UpperCase(parameters.front())) !=
+ mappedConfigs.end()
+ ? "1"
+ : "0";
+ }
+ }
+ }
+ return "0";
+ }
+} configurationTestNode;
+
+static const struct JoinNode : public cmGeneratorExpressionNode
+{
+ JoinNode() {}
+
+ 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;
+ cmSystemTools::ExpandListArgument(parameters.front(), list);
+ return cmJoin(list, parameters[1]);
+ }
+} joinNode;
+
+static const struct CompileLanguageNode : public cmGeneratorExpressionNode
+{
+ CompileLanguageNode() {}
+
+ 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->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();
+ }
+
+ std::vector<std::string> enabledLanguages;
+ cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
+ gg->GetEnabledLanguages(enabledLanguages);
+ if (!parameters.empty() &&
+ std::find(enabledLanguages.begin(), enabledLanguages.end(),
+ parameters.front()) == enabledLanguages.end()) {
+ reportError(context, content->GetOriginalExpression(),
+ "$<COMPILE_LANGUAGE:...> Unknown language.");
+ return std::string();
+ }
+ std::string genName = gg->GetName();
+ if (genName.find("Visual Studio") != std::string::npos) {
+ if (dagChecker && (dagChecker->EvaluatingCompileDefinitions() ||
+ dagChecker->EvaluatingIncludeDirectories())) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS "
+ "and file(GENERATE) with the Visual Studio generator.");
+ return std::string();
+ }
+ } else if (genName.find("Xcode") != std::string::npos) {
+ if (dagChecker && (dagChecker->EvaluatingCompileDefinitions() ||
+ dagChecker->EvaluatingIncludeDirectories())) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS "
+ "and file(GENERATE) with the Xcode generator.");
+ return std::string();
+ }
+ } else {
+ if (genName.find("Makefiles") == std::string::npos &&
+ genName.find("Ninja") == 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;
+ }
+ return context->Language == parameters.front() ? "1" : "0";
+ }
+} languageNode;
+
+#define TRANSITIVE_PROPERTY_NAME(PROPERTY) , "INTERFACE_" #PROPERTY
+
+static const char* targetPropertyTransitiveWhitelist[] = {
+ nullptr CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(TRANSITIVE_PROPERTY_NAME)
+};
+
+#undef TRANSITIVE_PROPERTY_NAME
+
+template <typename T>
+std::string getLinkedTargetsContent(
+ std::vector<T> const& libraries, cmGeneratorTarget const* target,
+ cmGeneratorTarget const* headTarget, cmGeneratorExpressionContext* context,
+ cmGeneratorExpressionDAGChecker* dagChecker,
+ const std::string& interfacePropertyName)
+{
+ std::string linkedTargetsContent;
+ std::string sep;
+ std::string depString;
+ for (T const& l : libraries) {
+ // Broken code can have a target in its own link interface.
+ // Don't follow such link interface entries so as not to create a
+ // self-referencing loop.
+ if (l.Target && l.Target != target) {
+ depString += sep + "$<TARGET_PROPERTY:" + l.Target->GetName() + "," +
+ interfacePropertyName + ">";
+ sep = ";";
+ }
+ }
+ if (!depString.empty()) {
+ linkedTargetsContent =
+ cmGeneratorExpressionNode::EvaluateDependentExpression(
+ depString, target->GetLocalGenerator(), context, headTarget, target,
+ dagChecker);
+ }
+ linkedTargetsContent =
+ cmGeneratorExpression::StripEmptyListElements(linkedTargetsContent);
+ return linkedTargetsContent;
+}
+
+static const struct TargetPropertyNode : public cmGeneratorExpressionNode
+{
+ TargetPropertyNode() {}
+
+ // This node handles errors on parameter count itself.
+ int NumExpectedParameters() const override { return OneOrMoreParameters; }
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
+ {
+ if (parameters.size() != 1 && parameters.size() != 2) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<TARGET_PROPERTY:...> expression requires one or two parameters");
+ return std::string();
+ }
+ static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
+
+ cmGeneratorTarget const* target = context->HeadTarget;
+ std::string propertyName = *parameters.begin();
+
+ if (parameters.size() == 1) {
+ context->HadHeadSensitiveCondition = true;
+ }
+ if (!target && parameters.size() == 1) {
+ 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();
+ }
+
+ if (parameters.size() == 2) {
+ if (parameters.begin()->empty() && parameters[1].empty()) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty "
+ "target name and property name.");
+ return std::string();
+ }
+ if (parameters.begin()->empty()) {
+ reportError(
+ context, content->GetOriginalExpression(),
+ "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty "
+ "target name.");
+ return std::string();
+ }
+
+ std::string targetName = parameters.front();
+ propertyName = parameters[1];
+ if (!cmGeneratorExpression::IsValidTargetName(targetName)) {
+ if (!propertyNameValidator.find(propertyName.c_str())) {
+ ::reportError(context, content->GetOriginalExpression(),
+ "Target name and property name not supported.");
+ return std::string();
+ }
+ ::reportError(context, content->GetOriginalExpression(),
+ "Target name not supported.");
+ return std::string();
+ }
+ if (propertyName == "ALIASED_TARGET") {
+ if (context->LG->GetMakefile()->IsAlias(targetName)) {
+ if (cmGeneratorTarget* tgt =
+ context->LG->FindGeneratorTargetToUse(targetName)) {
+ return tgt->GetName();
+ }
+ }
+ return "";
+ }
+ 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);
+ }
+
+ if (target == context->HeadTarget) {
+ // 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);
+ }
+ 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);
+ }
+
+ cmGeneratorExpressionDAGChecker dagChecker(context->Backtrace,
+ target->GetName(), 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:
+ for (size_t i = 1; i < cm::size(targetPropertyTransitiveWhitelist);
+ ++i) {
+ if (targetPropertyTransitiveWhitelist[i] == propertyName) {
+ // No error. We're not going to find anything new here.
+ return std::string();
+ }
+ }
+ case cmGeneratorExpressionDAGChecker::DAG:
+ break;
+ }
+
+ const char* prop = target->GetProperty(propertyName);
+
+ if (dagCheckerParent) {
+ if (dagCheckerParent->EvaluatingLinkLibraries()) {
+#define TRANSITIVE_PROPERTY_COMPARE(PROPERTY) \
+ (#PROPERTY == propertyName || "INTERFACE_" #PROPERTY == propertyName) ||
+ if (CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(
+ TRANSITIVE_PROPERTY_COMPARE) false) { // NOLINT(clang-tidy)
+ 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();
+ }
+#undef TRANSITIVE_PROPERTY_COMPARE
+
+ if (!prop) {
+ 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
+ }
+ }
+
+ std::string linkedTargetsContent;
+
+ 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.c_str(),
+ "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
+ cmGeneratorTarget const* headTarget =
+ context->HeadTarget && isInterfaceProperty ? context->HeadTarget
+ : target;
+
+ if (isInterfaceProperty) {
+ if (cmLinkInterfaceLibraries const* iface =
+ target->GetLinkInterfaceLibraries(context->Config, headTarget,
+ true)) {
+ linkedTargetsContent =
+ getLinkedTargetsContent(iface->Libraries, target, headTarget,
+ context, &dagChecker, interfacePropertyName);
+ }
+ } else if (!interfacePropertyName.empty()) {
+ if (cmLinkImplementationLibraries const* impl =
+ target->GetLinkImplementationLibraries(context->Config)) {
+ linkedTargetsContent =
+ getLinkedTargetsContent(impl->Libraries, target, target, context,
+ &dagChecker, interfacePropertyName);
+ }
+ }
+
+ if (!prop) {
+ if (target->IsImported() ||
+ target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+ return linkedTargetsContent;
+ }
+ 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 : "";
+ }
+
+ return linkedTargetsContent;
+ }
+
+ 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()) {
+ std::string result = this->EvaluateDependentExpression(
+ prop, context->LG, context, headTarget, target, &dagChecker);
+ if (!linkedTargetsContent.empty()) {
+ result += (result.empty() ? "" : ";") + linkedTargetsContent;
+ }
+ return result;
+ }
+ return prop;
+ }
+} targetPropertyNode;
+
+static const struct TargetNameNode : public cmGeneratorExpressionNode
+{
+ TargetNameNode() {}
+
+ 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() {}
+
+ 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();
+ }
+ if (gt->GetType() != cmStateEnums::OBJECT_LIBRARY) {
+ std::ostringstream e;
+ e << "Objects of target \"" << tgtName
+ << "\" referenced but is not an OBJECT library.";
+ 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()) {
+ const char* loc = nullptr;
+ const char* imp = nullptr;
+ std::string suffix;
+ if (gt->Target->GetMappedConfig(context->Config, &loc, &imp, suffix)) {
+ cmSystemTools::ExpandListArgument(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 = 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() {}
+
+ 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;
+
+ typedef std::map<std::string, std::vector<std::string>> LangMap;
+ 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();
+ }
+ cmSystemTools::ExpandListArgument(featuresKnown,
+ availableFeatures[lang]);
+ }
+ }
+
+ bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries();
+
+ for (auto const& lit : testedFeatures) {
+ std::vector<std::string> const& langAvailable =
+ availableFeatures[lit.first];
+ const char* standardDefault = context->LG->GetMakefile()->GetDefinition(
+ "CMAKE_" + lit.first + "_STANDARD_DEFAULT");
+ for (std::string const& it : lit.second) {
+ if (std::find(langAvailable.begin(), langAvailable.end(), it) ==
+ langAvailable.end()) {
+ return "0";
+ }
+ if (standardDefault && !*standardDefault) {
+ // 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) {
+ const char* 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() {}
+
+ 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(
+ cmake::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() {}
+
+ 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 = target->GetDirectory(context->Config);
+ result += "/";
+ result += 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 = target->GetPDBDirectory(context->Config);
+ result += "/";
+ result += 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()
+ ? 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; }
+};
+
+template <typename ArtifactT, typename ComponentT>
+struct TargetFilesystemArtifact : public cmGeneratorExpressionNode
+{
+ TargetFilesystemArtifact() {}
+
+ int NumExpectedParameters() const override { return 1; }
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* dagChecker) const override
+ {
+ // Lookup the referenced target.
+ std::string name = *parameters.begin();
+
+ if (!cmGeneratorExpression::IsValidTargetName(name)) {
+ ::reportError(context, content->GetOriginalExpression(),
+ "Expression syntax not recognized.");
+ return std::string();
+ }
+ cmGeneratorTarget* target = context->LG->FindGeneratorTargetToUse(name);
+ if (!target) {
+ ::reportError(context, content->GetOriginalExpression(),
+ "No target \"" + name + "\"");
+ return std::string();
+ }
+ if (target->GetType() >= cmStateEnums::OBJECT_LIBRARY &&
+ target->GetType() != cmStateEnums::UNKNOWN_LIBRARY) {
+ ::reportError(context, content->GetOriginalExpression(), "Target \"" +
+ name + "\" is not an executable or library.");
+ return std::string();
+ }
+ if (dagChecker && (dagChecker->EvaluatingLinkLibraries(name.c_str()) ||
+ (dagChecker->EvaluatingSources() &&
+ name == dagChecker->TopTarget()))) {
+ ::reportError(context, content->GetOriginalExpression(),
+ "Expressions which require the linker language may not "
+ "be used while evaluating link libraries");
+ 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() {}
+
+ 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;
+
+static const struct ShellPathNode : public cmGeneratorExpressionNode
+{
+ ShellPathNode() {}
+
+ std::string Evaluate(
+ const std::vector<std::string>& parameters,
+ cmGeneratorExpressionContext* context,
+ const GeneratorExpressionContent* content,
+ cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
+ {
+ if (!cmSystemTools::FileIsFullPath(parameters.front())) {
+ reportError(context, content->GetOriginalExpression(),
+ "\"" + parameters.front() + "\" is not an absolute path.");
+ return std::string();
+ }
+ cmOutputConverter converter(context->LG->GetStateSnapshot());
+ return converter.ConvertDirectorySeparatorsForShell(parameters.front());
+ }
+} shellPathNode;
+
+const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
+ const std::string& identifier)
+{
+ typedef std::map<std::string, const cmGeneratorExpressionNode*> NodeMap;
+ static NodeMap nodeMap;
+ if (nodeMap.empty()) {
+ nodeMap["0"] = &zeroNode;
+ nodeMap["1"] = &oneNode;
+ nodeMap["AND"] = &andNode;
+ nodeMap["OR"] = &orNode;
+ nodeMap["NOT"] = &notNode;
+ nodeMap["C_COMPILER_ID"] = &cCompilerIdNode;
+ nodeMap["CXX_COMPILER_ID"] = &cxxCompilerIdNode;
+ nodeMap["VERSION_GREATER"] = &versionGreaterNode;
+ nodeMap["VERSION_GREATER_EQUAL"] = &versionGreaterEqNode;
+ nodeMap["VERSION_LESS"] = &versionLessNode;
+ nodeMap["VERSION_LESS_EQUAL"] = &versionLessEqNode;
+ nodeMap["VERSION_EQUAL"] = &versionEqualNode;
+ nodeMap["C_COMPILER_VERSION"] = &cCompilerVersionNode;
+ nodeMap["CXX_COMPILER_VERSION"] = &cxxCompilerVersionNode;
+ nodeMap["PLATFORM_ID"] = &platformIdNode;
+ nodeMap["COMPILE_FEATURES"] = &compileFeaturesNode;
+ nodeMap["CONFIGURATION"] = &configurationNode;
+ nodeMap["CONFIG"] = &configurationTestNode;
+ nodeMap["TARGET_FILE"] = &targetNodeGroup.File;
+ nodeMap["TARGET_LINKER_FILE"] = &targetLinkerNodeGroup.File;
+ nodeMap["TARGET_SONAME_FILE"] = &targetSoNameNodeGroup.File;
+ nodeMap["TARGET_PDB_FILE"] = &targetPdbNodeGroup.File;
+ nodeMap["TARGET_FILE_NAME"] = &targetNodeGroup.FileName;
+ nodeMap["TARGET_LINKER_FILE_NAME"] = &targetLinkerNodeGroup.FileName;
+ nodeMap["TARGET_SONAME_FILE_NAME"] = &targetSoNameNodeGroup.FileName;
+ nodeMap["TARGET_PDB_FILE_NAME"] = &targetPdbNodeGroup.FileName;
+ nodeMap["TARGET_FILE_DIR"] = &targetNodeGroup.FileDir;
+ nodeMap["TARGET_LINKER_FILE_DIR"] = &targetLinkerNodeGroup.FileDir;
+ nodeMap["TARGET_SONAME_FILE_DIR"] = &targetSoNameNodeGroup.FileDir;
+ nodeMap["TARGET_PDB_FILE_DIR"] = &targetPdbNodeGroup.FileDir;
+ nodeMap["TARGET_BUNDLE_DIR"] = &targetBundleDirNode;
+ nodeMap["TARGET_BUNDLE_CONTENT_DIR"] = &targetBundleContentDirNode;
+ nodeMap["STREQUAL"] = &strEqualNode;
+ nodeMap["EQUAL"] = &equalNode;
+ nodeMap["LOWER_CASE"] = &lowerCaseNode;
+ nodeMap["UPPER_CASE"] = &upperCaseNode;
+ nodeMap["MAKE_C_IDENTIFIER"] = &makeCIdentifierNode;
+ nodeMap["BOOL"] = &boolNode;
+ nodeMap["IF"] = &ifNode;
+ nodeMap["ANGLE-R"] = &angle_rNode;
+ nodeMap["COMMA"] = &commaNode;
+ nodeMap["SEMICOLON"] = &semicolonNode;
+ nodeMap["TARGET_PROPERTY"] = &targetPropertyNode;
+ nodeMap["TARGET_NAME"] = &targetNameNode;
+ nodeMap["TARGET_OBJECTS"] = &targetObjectsNode;
+ nodeMap["TARGET_POLICY"] = &targetPolicyNode;
+ nodeMap["BUILD_INTERFACE"] = &buildInterfaceNode;
+ nodeMap["INSTALL_INTERFACE"] = &installInterfaceNode;
+ nodeMap["INSTALL_PREFIX"] = &installPrefixNode;
+ nodeMap["JOIN"] = &joinNode;
+ nodeMap["LINK_ONLY"] = &linkOnlyNode;
+ nodeMap["COMPILE_LANGUAGE"] = &languageNode;
+ nodeMap["SHELL_PATH"] = &shellPathNode;
+ }
+ NodeMap::const_iterator i = nodeMap.find(identifier);
+ if (i == nodeMap.end()) {
+ return nullptr;
+ }
+ return i->second;
+}
+
+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(cmake::FATAL_ERROR, e.str(),
+ context->Backtrace);
+}