/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cmsys/RegularExpression.hxx" #include "cmsys/String.h" #include "cmCMakePath.h" #include "cmComputeLinkInformation.h" #include "cmGeneratorExpression.h" #include "cmGeneratorExpressionContext.h" #include "cmGeneratorExpressionDAGChecker.h" #include "cmGeneratorExpressionEvaluator.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmLinkItem.h" #include "cmList.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" #include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmRange.h" #include "cmStandardLevelResolver.h" #include "cmState.h" #include "cmStateSnapshot.h" #include "cmStateTypes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" #include "cmValue.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(*lg->GetCMakeInstance(), context->Backtrace); std::unique_ptr 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& /*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& parameters, cmGeneratorExpressionContext* /*context*/, const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { return parameters.front(); } } oneNode; static const struct OneNode buildInterfaceNode; static const struct ZeroNode installInterfaceNode; static const struct OneNode buildLocalInterfaceNode; 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; } bool ShouldEvaluateNextParameter(const std::vector& parameters, std::string& def_value) const override { if (!parameters.empty() && parameters.back() == failureVal) { def_value = failureVal; return false; } return true; } std::string Evaluate(const std::vector& 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { if (parameters.front() != "0" && parameters.front() != "1") { reportError( context, content->GetOriginalExpression(), "$ 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& 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; } bool ShouldEvaluateNextParameter(const std::vector& parameters, std::string&) const override { return (parameters.empty() || parameters[0] != cmStrCat(parameters.size() - 1, "")); } std::string Evaluate(const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker*) const override { if (parameters[0] != "1" && parameters[0] != "0") { reportError(context, content->GetOriginalExpression(), "First parameter to $ 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& 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& 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(), "$ 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { cmList values; cmList checkValues; bool check = false; switch (context->LG->GetPolicyStatus(cmPolicies::CMP0085)) { case cmPolicies::WARN: if (parameters.front().empty()) { check = true; checkValues.assign(parameters[1], cmList::EmptyElements::Yes); } CM_FALLTHROUGH; case cmPolicies::OLD: values.assign(parameters[1]); 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: values.assign(parameters[1], cmList::EmptyElements::Yes); break; } return values.find(parameters.front()) != cmList::npos ? "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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { if (parameters.size() != 3) { reportError(context, content->GetOriginalExpression(), "$ expression requires three parameters"); return {}; } if (parameters[1] != "INCLUDE" && parameters[1] != "EXCLUDE") { reportError( context, content->GetOriginalExpression(), "$ second parameter must be either INCLUDE or EXCLUDE"); return {}; } try { return cmList{ parameters.front(), cmList::EmptyElements::Yes } .filter(parameters[2], parameters[1] == "EXCLUDE" ? cmList::FilterMode::EXCLUDE : cmList::FilterMode::INCLUDE) .to_string(); } catch (std::invalid_argument&) { reportError(context, content->GetOriginalExpression(), "$ failed to compile regex"); return {}; } } } 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { if (parameters.size() != 1) { reportError( context, content->GetOriginalExpression(), "$ expression requires one parameter"); } return cmList{ parameters.front(), cmList::EmptyElements::Yes } .remove_duplicates() .to_string(); } } 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { if (parameters.size() != 1) { reportError(context, content->GetOriginalExpression(), "$ expression requires one parameter"); return std::string(); } std::string const& targetName = parameters.front(); if (targetName.empty() || !cmGeneratorExpression::IsValidTargetName(targetName)) { reportError(context, content->GetOriginalExpression(), "$ 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { if (parameters.size() != 1) { reportError(context, content->GetOriginalExpression(), "$ expression requires one " "parameter"); return std::string(); } std::string const& targetName = parameters.front(); if (targetName.empty() || !cmGeneratorExpression::IsValidTargetName(targetName)) { reportError(context, content->GetOriginalExpression(), "$ 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, context->LG, context->Config); 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& 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(), "$ 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 \"" << targetName << "\" not found."; reportError(context, content->GetOriginalExpression(), e.str()); return std::string(); } const std::string& expression = parameters[1]; if (expression.empty()) { return expression; } // Replace the surrounding context with the named target. 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& 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& 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& parameters, cmGeneratorExpressionContext* /*context*/, const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { return cmSystemTools::UpperCase(parameters.front()); } } upperCaseNode; namespace { template class Range : public cmRange { private: using Base = cmRange; public: using const_iterator = typename Container::const_iterator; using value_type = typename Container::value_type; using size_type = typename Container::size_type; using difference_type = typename Container::difference_type; using const_reference = typename Container::const_reference; Range(const Container& container) : Base(container.begin(), container.end()) { } const_reference operator[](size_type pos) const { return *(this->begin() + pos); } const_reference front() const { return *this->begin(); } const_reference back() const { return *std::prev(this->end()); } Range& advance(difference_type amount) & { Base::advance(amount); return *this; } Range advance(difference_type amount) && { Base::advance(amount); return std::move(*this); } }; using Arguments = Range>; bool CheckGenExParameters(cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, cm::string_view genex, cm::string_view option, std::size_t count, int required = 1, bool exactly = true) { if (static_cast(count) < required || (exactly && static_cast(count) > required)) { std::string nbParameters; switch (required) { case 1: nbParameters = "one parameter"; break; case 2: nbParameters = "two parameters"; break; case 3: nbParameters = "three parameters"; break; case 4: nbParameters = "four parameters"; break; default: nbParameters = cmStrCat(std::to_string(required), " parameters"); } reportError(ctx, cnt->GetOriginalExpression(), cmStrCat("$<", genex, ':', option, "> expression requires ", (exactly ? "exactly" : "at least"), ' ', nbParameters, '.')); return false; } return true; }; bool CheckPathParametersEx(cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, cm::string_view option, std::size_t count, int required = 1, bool exactly = true) { return CheckGenExParameters(ctx, cnt, "PATH"_s, option, count, required, exactly); } bool CheckPathParameters(cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, cm::string_view option, const Arguments& args, int required = 1) { return CheckPathParametersEx(ctx, cnt, option, args.size(), required); }; std::string ToString(bool isTrue) { return isTrue ? "1" : "0"; }; } static const struct PathNode : public cmGeneratorExpressionNode { PathNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return TwoOrMoreParameters; } bool AcceptsArbitraryContentParameter() const override { return true; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { static auto processList = [](std::string const& arg, std::function transform) -> std::string { cmList list{ arg }; std::for_each(list.begin(), list.end(), std::move(transform)); return list.to_string(); }; static std::unordered_map< cm::string_view, std::function> pathCommands{ { "GET_ROOT_NAME"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParameters(ctx, cnt, "GET_ROOT_NAME"_s, args) && !args.front().empty()) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.GetRootName().String(); }); } return std::string{}; } }, { "GET_ROOT_DIRECTORY"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParameters(ctx, cnt, "GET_ROOT_DIRECTORY"_s, args) && !args.front().empty()) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.GetRootDirectory().String(); }); } return std::string{}; } }, { "GET_ROOT_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParameters(ctx, cnt, "GET_ROOT_PATH"_s, args) && !args.front().empty()) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.GetRootPath().String(); }); } return std::string{}; } }, { "GET_FILENAME"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParameters(ctx, cnt, "GET_FILENAME"_s, args) && !args.front().empty()) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.GetFileName().String(); }); } return std::string{}; } }, { "GET_EXTENSION"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { bool lastOnly = args.front() == "LAST_ONLY"_s; if (lastOnly) { args.advance(1); } if (CheckPathParametersEx(ctx, cnt, lastOnly ? "GET_EXTENSION,LAST_ONLY"_s : "GET_EXTENSION"_s, args.size())) { if (args.front().empty()) { return std::string{}; } if (lastOnly) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.GetExtension().String(); }); } return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.GetWideExtension().String(); }); } return std::string{}; } }, { "GET_STEM"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { bool lastOnly = args.front() == "LAST_ONLY"_s; if (lastOnly) { args.advance(1); } if (CheckPathParametersEx( ctx, cnt, lastOnly ? "GET_STEM,LAST_ONLY"_s : "GET_STEM"_s, args.size())) { if (args.front().empty()) { return std::string{}; } if (lastOnly) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.GetStem().String(); }); } return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.GetNarrowStem().String(); }); } return std::string{}; } }, { "GET_RELATIVE_PART"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParameters(ctx, cnt, "GET_RELATIVE_PART"_s, args) && !args.front().empty()) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.GetRelativePath().String(); }); } return std::string{}; } }, { "GET_PARENT_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParameters(ctx, cnt, "GET_PARENT_PATH"_s, args)) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.GetParentPath().String(); }); } return std::string{}; } }, { "HAS_ROOT_NAME"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { return CheckPathParameters(ctx, cnt, "HAS_ROOT_NAME"_s, args) ? ToString(cmCMakePath{ args.front() }.HasRootName()) : std::string{ "0" }; } }, { "HAS_ROOT_DIRECTORY"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { return CheckPathParameters(ctx, cnt, "HAS_ROOT_DIRECTORY"_s, args) ? ToString(cmCMakePath{ args.front() }.HasRootDirectory()) : std::string{ "0" }; } }, { "HAS_ROOT_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { return CheckPathParameters(ctx, cnt, "HAS_ROOT_PATH"_s, args) ? ToString(cmCMakePath{ args.front() }.HasRootPath()) : std::string{ "0" }; } }, { "HAS_FILENAME"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { return CheckPathParameters(ctx, cnt, "HAS_FILENAME"_s, args) ? ToString(cmCMakePath{ args.front() }.HasFileName()) : std::string{ "0" }; } }, { "HAS_EXTENSION"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { return CheckPathParameters(ctx, cnt, "HAS_EXTENSION"_s, args) && !args.front().empty() ? ToString(cmCMakePath{ args.front() }.HasExtension()) : std::string{ "0" }; } }, { "HAS_STEM"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { return CheckPathParameters(ctx, cnt, "HAS_STEM"_s, args) ? ToString(cmCMakePath{ args.front() }.HasStem()) : std::string{ "0" }; } }, { "HAS_RELATIVE_PART"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { return CheckPathParameters(ctx, cnt, "HAS_RELATIVE_PART"_s, args) ? ToString(cmCMakePath{ args.front() }.HasRelativePath()) : std::string{ "0" }; } }, { "HAS_PARENT_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { return CheckPathParameters(ctx, cnt, "HAS_PARENT_PATH"_s, args) ? ToString(cmCMakePath{ args.front() }.HasParentPath()) : std::string{ "0" }; } }, { "IS_ABSOLUTE"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { return CheckPathParameters(ctx, cnt, "IS_ABSOLUTE"_s, args) ? ToString(cmCMakePath{ args.front() }.IsAbsolute()) : std::string{ "0" }; } }, { "IS_RELATIVE"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { return CheckPathParameters(ctx, cnt, "IS_RELATIVE"_s, args) ? ToString(cmCMakePath{ args.front() }.IsRelative()) : std::string{ "0" }; } }, { "IS_PREFIX"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { bool normalize = args.front() == "NORMALIZE"_s; if (normalize) { args.advance(1); } if (CheckPathParametersEx(ctx, cnt, normalize ? "IS_PREFIX,NORMALIZE"_s : "IS_PREFIX"_s, args.size(), 2)) { if (normalize) { return ToString(cmCMakePath{ args[0] }.Normal().IsPrefix( cmCMakePath{ args[1] }.Normal())); } return ToString( cmCMakePath{ args[0] }.IsPrefix(cmCMakePath{ args[1] })); } return std::string{}; } }, { "CMAKE_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { bool normalize = args.front() == "NORMALIZE"_s; if (normalize) { args.advance(1); } if (CheckPathParametersEx(ctx, cnt, normalize ? "CMAKE_PATH,NORMALIZE"_s : "CMAKE_PATH"_s, args.size(), 1)) { return processList( args.front(), [normalize](std::string& value) { auto path = cmCMakePath{ value, cmCMakePath::auto_format }; value = normalize ? path.Normal().GenericString() : path.GenericString(); }); } return std::string{}; } }, { "APPEND"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParametersEx(ctx, cnt, "APPEND"_s, args.size(), 1, false)) { auto const& list = args.front(); args.advance(1); return processList(list, [&args](std::string& value) { cmCMakePath path{ value }; for (const auto& p : args) { path /= p; } value = path.String(); }); } return std::string{}; } }, { "REMOVE_FILENAME"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParameters(ctx, cnt, "REMOVE_FILENAME"_s, args) && !args.front().empty()) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.RemoveFileName().String(); }); } return std::string{}; } }, { "REPLACE_FILENAME"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParameters(ctx, cnt, "REPLACE_FILENAME"_s, args, 2)) { return processList(args.front(), [&args](std::string& value) { value = cmCMakePath{ value } .ReplaceFileName(cmCMakePath{ args[1] }) .String(); }); } return std::string{}; } }, { "REMOVE_EXTENSION"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { bool lastOnly = args.front() == "LAST_ONLY"_s; if (lastOnly) { args.advance(1); } if (CheckPathParametersEx(ctx, cnt, lastOnly ? "REMOVE_EXTENSION,LAST_ONLY"_s : "REMOVE_EXTENSION"_s, args.size())) { if (args.front().empty()) { return std::string{}; } if (lastOnly) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.RemoveExtension().String(); }); } return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.RemoveWideExtension().String(); }); } return std::string{}; } }, { "REPLACE_EXTENSION"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { bool lastOnly = args.front() == "LAST_ONLY"_s; if (lastOnly) { args.advance(1); } if (CheckPathParametersEx(ctx, cnt, lastOnly ? "REPLACE_EXTENSION,LAST_ONLY"_s : "REPLACE_EXTENSION"_s, args.size(), 2)) { if (lastOnly) { return processList(args.front(), [&args](std::string& value) { value = cmCMakePath{ value } .ReplaceExtension(cmCMakePath{ args[1] }) .String(); }); } return processList(args.front(), [&args](std::string& value) { value = cmCMakePath{ value } .ReplaceWideExtension(cmCMakePath{ args[1] }) .String(); }); } return std::string{}; } }, { "NORMAL_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParameters(ctx, cnt, "NORMAL_PATH"_s, args) && !args.front().empty()) { return processList(args.front(), [](std::string& value) { value = cmCMakePath{ value }.Normal().String(); }); } return std::string{}; } }, { "RELATIVE_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckPathParameters(ctx, cnt, "RELATIVE_PATH"_s, args, 2)) { return processList(args.front(), [&args](std::string& value) { value = cmCMakePath{ value }.Relative(args[1]).String(); }); } return std::string{}; } }, { "ABSOLUTE_PATH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { bool normalize = args.front() == "NORMALIZE"_s; if (normalize) { args.advance(1); } if (CheckPathParametersEx(ctx, cnt, normalize ? "ABSOLUTE_PATH,NORMALIZE"_s : "ABSOLUTE_PATH"_s, args.size(), 2)) { return processList( args.front(), [&args, normalize](std::string& value) { auto path = cmCMakePath{ value }.Absolute(args[1]); value = normalize ? path.Normal().String() : path.String(); }); } return std::string{}; } } }; if (cm::contains(pathCommands, parameters.front())) { auto args = Arguments{ parameters }.advance(1); return pathCommands[parameters.front()](context, content, args); } reportError(context, content->GetOriginalExpression(), cmStrCat(parameters.front(), ": invalid option.")); return std::string{}; } } pathNode; static const struct PathEqualNode : public cmGeneratorExpressionNode { PathEqualNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return 2; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* /*context*/, const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { return cmCMakePath{ parameters[0] } == cmCMakePath{ parameters[1] } ? "1" : "0"; } } pathEqualNode; namespace { inline bool CheckListParametersEx(cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, cm::string_view option, std::size_t count, int required = 1, bool exactly = true) { return CheckGenExParameters(ctx, cnt, "LIST"_s, option, count, required, exactly); } inline bool CheckListParameters(cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, cm::string_view option, const Arguments& args, int required = 1) { return CheckListParametersEx(ctx, cnt, option, args.size(), required); }; inline cmList GetList(std::string const& list) { return list.empty() ? cmList{} : cmList{ list, cmList::EmptyElements::Yes }; } bool GetNumericArgument(const std::string& arg, cmList::index_type& value) { try { std::size_t pos; if (sizeof(cmList::index_type) == sizeof(long)) { value = std::stol(arg, &pos); } else { value = std::stoll(arg, &pos); } if (pos != arg.length()) { // this is not a number return false; } } catch (const std::invalid_argument&) { return false; } return true; } bool GetNumericArguments( cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments const& args, std::vector& indexes, cmList::ExpandElements expandElements = cmList::ExpandElements::No) { using IndexRange = cmRange; IndexRange arguments(args.begin(), args.end()); cmList list; if (expandElements == cmList::ExpandElements::Yes) { list = cmList{ args.begin(), args.end(), expandElements }; arguments = IndexRange{ list.begin(), list.end() }; } for (auto const& value : arguments) { cmList::index_type index; if (!GetNumericArgument(value, index)) { reportError(ctx, cnt->GetOriginalExpression(), cmStrCat("index: \"", value, "\" is not a valid index")); return false; } indexes.push_back(index); } return true; } } static const struct ListNode : public cmGeneratorExpressionNode { ListNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return TwoOrMoreParameters; } bool AcceptsArbitraryContentParameter() const override { return true; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { static std::unordered_map< cm::string_view, std::function> listCommands{ { "LENGTH"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParameters(ctx, cnt, "LENGTH"_s, args)) { return std::to_string(GetList(args.front()).size()); } return std::string{}; } }, { "GET"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParametersEx(ctx, cnt, "GET"_s, args.size(), 2, false)) { auto list = GetList(args.front()); if (list.empty()) { reportError(ctx, cnt->GetOriginalExpression(), "given empty list"); return std::string{}; } std::vector indexes; if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes, cmList::ExpandElements::Yes)) { return std::string{}; } try { return list.get_items(indexes.begin(), indexes.end()) .to_string(); } catch (std::out_of_range& e) { reportError(ctx, cnt->GetOriginalExpression(), e.what()); return std::string{}; } } return std::string{}; } }, { "JOIN"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParameters(ctx, cnt, "JOIN"_s, args, 2)) { return GetList(args.front()).join(args[1]); } return std::string{}; } }, { "SUBLIST"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParameters(ctx, cnt, "SUBLIST"_s, args, 3)) { auto list = GetList(args.front()); if (!list.empty()) { std::vector indexes; if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes)) { return std::string{}; } if (indexes[0] < 0) { reportError(ctx, cnt->GetOriginalExpression(), cmStrCat("begin index: ", indexes[0], " is out of range 0 - ", list.size() - 1)); return std::string{}; } if (indexes[1] < -1) { reportError(ctx, cnt->GetOriginalExpression(), cmStrCat("length: ", indexes[1], " should be -1 or greater")); return std::string{}; } try { return list .sublist(static_cast(indexes[0]), static_cast(indexes[1])) .to_string(); } catch (std::out_of_range& e) { reportError(ctx, cnt->GetOriginalExpression(), e.what()); return std::string{}; } } } return std::string{}; } }, { "FIND"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParameters(ctx, cnt, "FIND"_s, args, 2)) { auto list = GetList(args.front()); auto index = list.find(args[1]); return index == cmList::npos ? "-1" : std::to_string(index); } return std::string{}; } }, { "APPEND"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParametersEx(ctx, cnt, "APPEND"_s, args.size(), 2, false)) { auto list = args.front(); args.advance(1); return cmList::append(list, args.begin(), args.end()); } return std::string{}; } }, { "PREPEND"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParametersEx(ctx, cnt, "PREPEND"_s, args.size(), 2, false)) { auto list = args.front(); args.advance(1); return cmList::prepend(list, args.begin(), args.end()); } return std::string{}; } }, { "INSERT"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParametersEx(ctx, cnt, "INSERT"_s, args.size(), 3, false)) { cmList::index_type index; if (!GetNumericArgument(args[1], index)) { reportError( ctx, cnt->GetOriginalExpression(), cmStrCat("index: \"", args[1], "\" is not a valid index")); return std::string{}; } try { auto list = GetList(args.front()); args.advance(2); list.insert_items(index, args.begin(), args.end(), cmList::ExpandElements::No, cmList::EmptyElements::Yes); return list.to_string(); } catch (std::out_of_range& e) { reportError(ctx, cnt->GetOriginalExpression(), e.what()); return std::string{}; } } return std::string{}; } }, { "POP_BACK"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParameters(ctx, cnt, "POP_BACK"_s, args)) { auto list = GetList(args.front()); if (!list.empty()) { list.pop_back(); return list.to_string(); } } return std::string{}; } }, { "POP_FRONT"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParameters(ctx, cnt, "POP_FRONT"_s, args)) { auto list = GetList(args.front()); if (!list.empty()) { list.pop_front(); return list.to_string(); } } return std::string{}; } }, { "REMOVE_DUPLICATES"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParameters(ctx, cnt, "REMOVE_DUPLICATES"_s, args)) { return GetList(args.front()).remove_duplicates().to_string(); } return std::string{}; } }, { "REMOVE_ITEM"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParametersEx(ctx, cnt, "REMOVE_ITEM"_s, args.size(), 2, false)) { auto list = GetList(args.front()); args.advance(1); cmList items{ args.begin(), args.end(), cmList::ExpandElements::Yes }; return list.remove_items(items.begin(), items.end()).to_string(); } return std::string{}; } }, { "REMOVE_AT"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParametersEx(ctx, cnt, "REMOVE_AT"_s, args.size(), 2, false)) { auto list = GetList(args.front()); std::vector indexes; if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes, cmList::ExpandElements::Yes)) { return std::string{}; } try { return list.remove_items(indexes.begin(), indexes.end()) .to_string(); } catch (std::out_of_range& e) { reportError(ctx, cnt->GetOriginalExpression(), e.what()); return std::string{}; } } return std::string{}; } }, { "FILTER"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParameters(ctx, cnt, "FILTER"_s, args, 3)) { auto const& op = args[1]; if (op != "INCLUDE"_s && op != "EXCLUDE"_s) { reportError( ctx, cnt->GetOriginalExpression(), cmStrCat("sub-command FILTER does not recognize operator \"", op, "\". It must be either INCLUDE or EXCLUDE.")); return std::string{}; } try { return GetList(args.front()) .filter(args[2], op == "INCLUDE"_s ? cmList::FilterMode::INCLUDE : cmList::FilterMode::EXCLUDE) .to_string(); } catch (std::invalid_argument&) { reportError( ctx, cnt->GetOriginalExpression(), cmStrCat("sub-command FILTER, failed to compile regex \"", args[2], "\".")); return std::string{}; } } return std::string{}; } }, { "TRANSFORM"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParametersEx(ctx, cnt, "TRANSFORM"_s, args.size(), 2, false)) { auto list = GetList(args.front()); if (!list.empty()) { struct ActionDescriptor { ActionDescriptor(std::string name) : Name(std::move(name)) { } ActionDescriptor(std::string name, cmList::TransformAction action, int arity) : Name(std::move(name)) , Action(action) , Arity(arity) { } operator const std::string&() const { return this->Name; } std::string Name; cmList::TransformAction Action; int Arity = 0; }; static std::set< ActionDescriptor, std::function> descriptors{ { { "APPEND", cmList::TransformAction::APPEND, 1 }, { "PREPEND", cmList::TransformAction::PREPEND, 1 }, { "TOUPPER", cmList::TransformAction::TOUPPER, 0 }, { "TOLOWER", cmList::TransformAction::TOLOWER, 0 }, { "STRIP", cmList::TransformAction::STRIP, 0 }, { "REPLACE", cmList::TransformAction::REPLACE, 2 } }, [](const std::string& x, const std::string& y) { return x < y; } }; auto descriptor = descriptors.find(args.advance(1).front()); if (descriptor == descriptors.end()) { reportError(ctx, cnt->GetOriginalExpression(), cmStrCat(" sub-command TRANSFORM, ", args.front(), " invalid action.")); return std::string{}; } // Action arguments args.advance(1); if (args.size() < descriptor->Arity) { reportError(ctx, cnt->GetOriginalExpression(), cmStrCat("sub-command TRANSFORM, action ", descriptor->Name, " expects ", descriptor->Arity, " argument(s).")); return std::string{}; } std::vector arguments; if (descriptor->Arity > 0) { arguments = std::vector( args.begin(), args.begin() + descriptor->Arity); args.advance(descriptor->Arity); } const std::string REGEX{ "REGEX" }; const std::string AT{ "AT" }; const std::string FOR{ "FOR" }; std::unique_ptr selector; try { // handle optional arguments while (!args.empty()) { if ((args.front() == REGEX || args.front() == AT || args.front() == FOR) && selector) { reportError(ctx, cnt->GetOriginalExpression(), cmStrCat("sub-command TRANSFORM, selector " "already specified (", selector->GetTag(), ").")); return std::string{}; } // REGEX selector if (args.front() == REGEX) { if (args.advance(1).empty()) { reportError( ctx, cnt->GetOriginalExpression(), "sub-command TRANSFORM, selector REGEX expects " "'regular expression' argument."); return std::string{}; } selector = cmList::TransformSelector::New< cmList::TransformSelector::REGEX>(args.front()); args.advance(1); continue; } // AT selector if (args.front() == AT) { args.advance(1); // get all specified indexes std::vector indexes; while (!args.empty()) { cmList indexList{ args.front() }; for (auto const& index : indexList) { cmList::index_type value; if (!GetNumericArgument(index, value)) { // this is not a number, stop processing reportError( ctx, cnt->GetOriginalExpression(), cmStrCat("sub-command TRANSFORM, selector AT: '", index, "': unexpected argument.")); return std::string{}; } indexes.push_back(value); } args.advance(1); } if (indexes.empty()) { reportError(ctx, cnt->GetOriginalExpression(), "sub-command TRANSFORM, selector AT " "expects at least one " "numeric value."); return std::string{}; } selector = cmList::TransformSelector::New< cmList::TransformSelector::AT>(std::move(indexes)); continue; } // FOR selector if (args.front() == FOR) { if (args.advance(1).size() < 2) { reportError(ctx, cnt->GetOriginalExpression(), "sub-command TRANSFORM, selector FOR " "expects, at least," " two arguments."); return std::string{}; } cmList::index_type start = 0; cmList::index_type stop = 0; cmList::index_type step = 1; bool valid = false; if (GetNumericArgument(args.front(), start) && GetNumericArgument(args.advance(1).front(), stop)) { valid = true; } if (!valid) { reportError( ctx, cnt->GetOriginalExpression(), "sub-command TRANSFORM, selector FOR expects, " "at least, two numeric values."); return std::string{}; } // try to read a third numeric value for step if (!args.advance(1).empty()) { if (!GetNumericArgument(args.front(), step)) { // this is not a number step = -1; } args.advance(1); } if (step <= 0) { reportError( ctx, cnt->GetOriginalExpression(), "sub-command TRANSFORM, selector FOR expects " "positive numeric value for ."); return std::string{}; } selector = cmList::TransformSelector::New< cmList::TransformSelector::FOR>({ start, stop, step }); continue; } reportError(ctx, cnt->GetOriginalExpression(), cmStrCat("sub-command TRANSFORM, '", cmJoin(args, ", "), "': unexpected argument(s).")); return std::string{}; } return list .transform(descriptor->Action, arguments, std::move(selector)) .to_string(); } catch (cmList::transform_error& e) { reportError(ctx, cnt->GetOriginalExpression(), e.what()); return std::string{}; } } } return std::string{}; } }, { "REVERSE"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParameters(ctx, cnt, "REVERSE"_s, args)) { return GetList(args.front()).reverse().to_string(); } return std::string{}; } }, { "SORT"_s, [](cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt, Arguments& args) -> std::string { if (CheckListParametersEx(ctx, cnt, "SORT"_s, args.size(), 1, false)) { auto list = GetList(args.front()); args.advance(1); const auto COMPARE = "COMPARE:"_s; const auto CASE = "CASE:"_s; const auto ORDER = "ORDER:"_s; using SortConfig = cmList::SortConfiguration; SortConfig sortConfig; for (auto const& arg : args) { if (cmHasPrefix(arg, COMPARE)) { if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) { reportError(ctx, cnt->GetOriginalExpression(), "sub-command SORT, COMPARE option has been " "specified multiple times."); return std::string{}; } auto option = cm::string_view{ arg.c_str() + COMPARE.length() }; if (option == "STRING"_s) { sortConfig.Compare = SortConfig::CompareMethod::STRING; continue; } if (option == "FILE_BASENAME"_s) { sortConfig.Compare = SortConfig::CompareMethod::FILE_BASENAME; continue; } if (option == "NATURAL"_s) { sortConfig.Compare = SortConfig::CompareMethod::NATURAL; continue; } reportError( ctx, cnt->GetOriginalExpression(), cmStrCat( "sub-command SORT, an invalid COMPARE option has been " "specified: \"", option, "\".")); return std::string{}; } if (cmHasPrefix(arg, CASE)) { if (sortConfig.Case != SortConfig::CaseSensitivity::DEFAULT) { reportError(ctx, cnt->GetOriginalExpression(), "sub-command SORT, CASE option has been " "specified multiple times."); return std::string{}; } auto option = cm::string_view{ arg.c_str() + CASE.length() }; if (option == "SENSITIVE"_s) { sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE; continue; } if (option == "INSENSITIVE"_s) { sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE; continue; } reportError( ctx, cnt->GetOriginalExpression(), cmStrCat( "sub-command SORT, an invalid CASE option has been " "specified: \"", option, "\".")); return std::string{}; } if (cmHasPrefix(arg, ORDER)) { if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) { reportError(ctx, cnt->GetOriginalExpression(), "sub-command SORT, ORDER option has been " "specified multiple times."); return std::string{}; } auto option = cm::string_view{ arg.c_str() + ORDER.length() }; if (option == "ASCENDING"_s) { sortConfig.Order = SortConfig::OrderMode::ASCENDING; continue; } if (option == "DESCENDING"_s) { sortConfig.Order = SortConfig::OrderMode::DESCENDING; continue; } reportError( ctx, cnt->GetOriginalExpression(), cmStrCat( "sub-command SORT, an invalid ORDER option has been " "specified: \"", option, "\".")); return std::string{}; } reportError(ctx, cnt->GetOriginalExpression(), cmStrCat("sub-command SORT, option \"", arg, "\" is invalid.")); return std::string{}; } return list.sort(sortConfig).to_string(); } return std::string{}; } } }; if (cm::contains(listCommands, parameters.front())) { auto args = Arguments{ parameters }.advance(1); return listCommands[parameters.front()](context, content, args); } reportError(context, content->GetOriginalExpression(), cmStrCat(parameters.front(), ": invalid option.")); return std::string{}; } } listNode; static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode { MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default) bool AcceptsArbitraryContentParameter() const override { return true; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* /*context*/, const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { return cmSystemTools::MakeCidentifier(parameters.front()); } } makeCIdentifierNode; template struct CharacterNode : public cmGeneratorExpressionNode { CharacterNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return 0; } std::string Evaluate( const std::vector& /*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; static const CharacterNode<'"'> quoteNode; struct CompilerIdNode : public cmGeneratorExpressionNode { CompilerIdNode(const char* compilerLang) : CompilerLanguage(compilerLang) { } int NumExpectedParameters() const override { return ZeroOrMoreParameters; } std::string Evaluate( const std::vector& 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*unused*/, const std::string& lang) const { std::string const& compilerId = context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID"); if (parameters.empty()) { return compilerId; } if (compilerId.empty()) { return parameters.front().empty() ? "1" : "0"; } static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$"); for (auto const& param : parameters) { if (!compilerIdValidator.find(param)) { reportError(context, content->GetOriginalExpression(), "Expression syntax not recognized."); return std::string(); } if (strcmp(param.c_str(), compilerId.c_str()) == 0) { return "1"; } if (cmsysString_strcasecmp(param.c_str(), compilerId.c_str()) == 0) { switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) { case cmPolicies::WARN: { context->LG->GetCMakeInstance()->IssueMessage( MessageType::AUTHOR_WARNING, cmPolicies::GetPolicyWarning(cmPolicies::CMP0044), context->Backtrace); CM_FALLTHROUGH; } case cmPolicies::OLD: return "1"; case cmPolicies::NEW: case cmPolicies::REQUIRED_ALWAYS: case cmPolicies::REQUIRED_IF_USED: break; } } } return "0"; } const char* const CompilerLanguage; }; static const CompilerIdNode cCompilerIdNode("C"), cxxCompilerIdNode("CXX"), cudaCompilerIdNode("CUDA"), objcCompilerIdNode("OBJC"), objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran"), hipCompilerIdNode("HIP"), ispcCompilerIdNode("ISPC"); struct CompilerVersionNode : public cmGeneratorExpressionNode { CompilerVersionNode(const char* compilerLang) : CompilerLanguage(compilerLang) { } int NumExpectedParameters() const override { return OneOrZeroParameters; } std::string Evaluate( const std::vector& 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& 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(), compilerVersion) ? "1" : "0"; } const char* const CompilerLanguage; }; static const CompilerVersionNode cCompilerVersionNode("C"), cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"), objcCompilerVersionNode("OBJC"), objcxxCompilerVersionNode("OBJCXX"), fortranCompilerVersionNode("Fortran"), ispcCompilerVersionNode("ISPC"), hipCompilerVersionNode("HIP"); struct CompilerFrontendVariantNode : public cmGeneratorExpressionNode { CompilerFrontendVariantNode(const char* compilerLang) : CompilerLanguage(compilerLang) { } int NumExpectedParameters() const override { return ZeroOrMoreParameters; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { if (!context->HeadTarget) { std::ostringstream e; e << "$<" << this->CompilerLanguage << "_COMPILER_FRONTEND_VARIANT> 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*unused*/, const std::string& lang) const { std::string const& compilerFrontendVariant = context->LG->GetMakefile()->GetSafeDefinition( "CMAKE_" + lang + "_COMPILER_FRONTEND_VARIANT"); if (parameters.empty()) { return compilerFrontendVariant; } if (compilerFrontendVariant.empty()) { return parameters.front().empty() ? "1" : "0"; } static cmsys::RegularExpression compilerFrontendVariantValidator( "^[A-Za-z0-9_]*$"); for (auto const& param : parameters) { if (!compilerFrontendVariantValidator.find(param)) { reportError(context, content->GetOriginalExpression(), "Expression syntax not recognized."); return {}; } if (strcmp(param.c_str(), compilerFrontendVariant.c_str()) == 0) { return "1"; } } return "0"; } const char* const CompilerLanguage; }; static const CompilerFrontendVariantNode cCompilerFrontendVariantNode("C"), cxxCompilerFrontendVariantNode("CXX"), cudaCompilerFrontendVariantNode("CUDA"), objcCompilerFrontendVariantNode("OBJC"), objcxxCompilerFrontendVariantNode("OBJCXX"), fortranCompilerFrontendVariantNode("Fortran"), hipCompilerFrontendVariantNode("HIP"), ispcCompilerFrontendVariantNode("ISPC"); struct PlatformIdNode : public cmGeneratorExpressionNode { PlatformIdNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return ZeroOrMoreParameters; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { std::string const& platformId = context->LG->GetMakefile()->GetSafeDefinition("CMAKE_SYSTEM_NAME"); if (parameters.empty()) { return platformId; } if (platformId.empty()) { return parameters.front().empty() ? "1" : "0"; } for (auto const& param : parameters) { if (param == platformId) { return "1"; } } return "0"; } }; static struct PlatformIdNode platformIdNode; template struct VersionNode : public cmGeneratorExpressionNode { VersionNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return 2; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* /*context*/, const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { return cmSystemTools::VersionCompare(Op, parameters.front(), parameters[1]) ? "1" : "0"; } }; static const VersionNode versionGreaterNode; static const VersionNode versionGreaterEqNode; static const VersionNode versionLessNode; static const VersionNode versionLessEqNode; static const VersionNode versionEqualNode; static const struct CompileOnlyNode : public cmGeneratorExpressionNode { CompileOnlyNode() {} // NOLINT(modernize-use-equals-default) std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { if (!dagChecker) { reportError(context, content->GetOriginalExpression(), "$ may only be used for linking"); return std::string(); } if (dagChecker->GetTransitivePropertiesOnly()) { return parameters.front(); } return std::string{}; } } compileOnlyNode; static const struct LinkOnlyNode : public cmGeneratorExpressionNode { LinkOnlyNode() {} // NOLINT(modernize-use-equals-default) std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { if (!dagChecker) { reportError(context, content->GetOriginalExpression(), "$ may only be used for linking"); return std::string(); } if (!dagChecker->GetTransitivePropertiesOnlyCMP0131()) { 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& /*parameters*/, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { context->HadContextSensitiveCondition = true; return context->Config; } } configurationNode; static const struct ConfigurationTestNode : public cmGeneratorExpressionNode { ConfigurationTestNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return ZeroOrMoreParameters; } std::string Evaluate( const std::vector& 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())) { } context->HadContextSensitiveCondition = true; bool firstParam = true; for (auto const& param : parameters) { if (!configValidator.find(param)) { if (firstParam) { reportError(context, content->GetOriginalExpression(), "Expression syntax not recognized."); return std::string(); } // for backwards compat invalid config names are only errors as // the first parameter std::ostringstream e; /* clang-format off */ e << "Warning evaluating generator expression:\n" << " " << content->GetOriginalExpression() << "\n" << "The config name of \"" << param << "\" is invalid"; /* clang-format on */ context->LG->GetCMakeInstance()->IssueMessage( MessageType::WARNING, e.str(), context->Backtrace); } firstParam = false; if (context->Config.empty()) { if (param.empty()) { return "1"; } } else if (cmsysString_strcasecmp(param.c_str(), context->Config.c_str()) == 0) { return "1"; } } if (context->CurrentTarget && context->CurrentTarget->IsImported()) { cmValue loc = nullptr; cmValue 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. cmList mappedConfigs; std::string mapProp = cmStrCat( "MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(context->Config)); if (cmValue mapValue = context->CurrentTarget->GetProperty(mapProp)) { mappedConfigs.assign(cmSystemTools::UpperCase(*mapValue)); for (auto const& param : parameters) { if (cm::contains(mappedConfigs, cmSystemTools::UpperCase(param))) { return "1"; } } } else if (!suffix.empty()) { // There is no explicit mapping for the tested config, so use // the configuration of the imported location that was selected. for (auto const& param : parameters) { if (cmStrCat('_', cmSystemTools::UpperCase(param)) == suffix) { return "1"; } } } } } return "0"; } } configurationTestNode; static const struct JoinNode : public cmGeneratorExpressionNode { JoinNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return 2; } bool AcceptsArbitraryContentParameter() const override { return true; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* /*context*/, const GeneratorExpressionContent* /*content*/, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { return cmList{ parameters.front() }.join(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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { if (context->Language.empty() && (!dagChecker || !dagChecker->EvaluatingCompileExpression())) { reportError( context, content->GetOriginalExpression(), "$ 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 && genName.find("Green Hills MULTI") == std::string::npos) { reportError(context, content->GetOriginalExpression(), "$ not supported for this generator."); return std::string(); } if (parameters.empty()) { return context->Language; } for (auto const& param : parameters) { if (context->Language == param) { return "1"; } } return "0"; } } languageNode; static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode { CompileLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return TwoOrMoreParameters; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { if (!context->HeadTarget || (context->Language.empty() && (!dagChecker || !dagChecker->EvaluatingCompileExpression()))) { // reportError(context, content->GetOriginalExpression(), ""); reportError( context, content->GetOriginalExpression(), "$ 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 && genName.find("Green Hills MULTI") == std::string::npos) { reportError( context, content->GetOriginalExpression(), "$ not supported for this generator."); return std::string(); } const std::string& lang = context->Language; if (lang == parameters.front()) { std::vector 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { if (!context->HeadTarget || !dagChecker || !(dagChecker->EvaluatingLinkExpression() || dagChecker->EvaluatingLinkLibraries() || dagChecker->EvaluatingLinkerLauncher())) { reportError(context, content->GetOriginalExpression(), "$ 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(), "$ 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 && genName.find("Green Hills MULTI") == std::string::npos) { reportError(context, content->GetOriginalExpression(), "$ not supported for this generator."); return std::string(); } if (dagChecker->EvaluatingLinkLibraries()) { context->HadHeadSensitiveCondition = true; context->HadLinkLanguageSensitiveCondition = true; } if (parameters.empty()) { return context->Language; } for (auto const& param : parameters) { if (context->Language == param) { return "1"; } } return "0"; } } linkLanguageNode; namespace { struct LinkerId { static std::string Evaluate(const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, const std::string& lang) { std::string const& linkerId = context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID"); if (parameters.empty()) { return linkerId; } if (linkerId.empty()) { return parameters.front().empty() ? "1" : "0"; } static cmsys::RegularExpression linkerIdValidator("^[A-Za-z0-9_]*$"); for (auto const& param : parameters) { if (!linkerIdValidator.find(param)) { reportError(context, content->GetOriginalExpression(), "Expression syntax not recognized."); return std::string(); } if (param == linkerId) { return "1"; } } return "0"; } }; } static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode { LinkLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return TwoOrMoreParameters; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { if (!context->HeadTarget || !dagChecker || !(dagChecker->EvaluatingLinkExpression() || dagChecker->EvaluatingLinkLibraries() || dagChecker->EvaluatingLinkerLauncher())) { reportError( context, content->GetOriginalExpression(), "$ 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 && genName.find("Green Hills MULTI") == std::string::npos) { reportError( context, content->GetOriginalExpression(), "$ 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 idParameter((parameters.cbegin() + 1), parameters.cend()); return LinkerId::Evaluate(idParameter, context, content, lang); } return "0"; } } linkLanguageAndIdNode; static const struct LinkLibraryNode : public cmGeneratorExpressionNode { LinkLibraryNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return OneOrMoreParameters; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { using ForGenex = cmGeneratorExpressionDAGChecker::ForGenex; if (!context->HeadTarget || !dagChecker || !dagChecker->EvaluatingLinkLibraries(nullptr, ForGenex::LINK_LIBRARY)) { reportError(context, content->GetOriginalExpression(), "$ may only be used with binary targets " "to specify link libraries through 'LINK_LIBRARIES', " "'INTERFACE_LINK_LIBRARIES', and " "'INTERFACE_LINK_LIBRARIES_DIRECT' properties."); return std::string(); } cmList list{ parameters.begin(), parameters.end() }; if (list.empty()) { reportError( context, content->GetOriginalExpression(), "$ expects a feature name as first argument."); return std::string(); } if (list.size() == 1) { // no libraries specified, ignore this genex return std::string(); } static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$"); auto const& feature = list.front(); if (!featureNameValidator.find(feature)) { reportError(context, content->GetOriginalExpression(), cmStrCat("The feature name '", feature, "' contains invalid characters.")); return std::string(); } const auto LL_BEGIN = cmStrCat("'); const auto LL_END = cmStrCat("'); // filter out $ tags with same feature // and raise an error for any different feature cm::erase_if(list, [&](const std::string& item) -> bool { return item == LL_BEGIN || item == LL_END; }); auto it = std::find_if(list.cbegin() + 1, list.cend(), [&feature](const std::string& item) -> bool { return cmHasPrefix(item, "', 14) - 14) != feature; }); if (it != list.cend()) { reportError( context, content->GetOriginalExpression(), "$ with different features cannot be nested."); return std::string(); } // $ must not appear as part of $ it = std::find_if(list.cbegin() + 1, list.cend(), [](const std::string& item) -> bool { return cmHasPrefix(item, "GetOriginalExpression(), "$ cannot be nested inside a " "$ expression."); return std::string(); } list.front() = LL_BEGIN; list.push_back(LL_END); return list.to_string(); } } linkLibraryNode; static const struct LinkGroupNode : public cmGeneratorExpressionNode { LinkGroupNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return OneOrMoreParameters; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { using ForGenex = cmGeneratorExpressionDAGChecker::ForGenex; if (!context->HeadTarget || !dagChecker || !dagChecker->EvaluatingLinkLibraries(nullptr, ForGenex::LINK_GROUP)) { reportError( context, content->GetOriginalExpression(), "$ may only be used with binary targets " "to specify group of link libraries through 'LINK_LIBRARIES', " "'INTERFACE_LINK_LIBRARIES', and " "'INTERFACE_LINK_LIBRARIES_DIRECT' properties."); return std::string(); } cmList list{ parameters.begin(), parameters.end() }; if (list.empty()) { reportError( context, content->GetOriginalExpression(), "$ expects a feature name as first argument."); return std::string(); } // $ cannot be nested if (std::find_if(list.cbegin(), list.cend(), [](const std::string& item) -> bool { return cmHasPrefix(item, "GetOriginalExpression(), "$ cannot be nested."); return std::string(); } if (list.size() == 1) { // no libraries specified, ignore this genex return std::string(); } static cmsys::RegularExpression featureNameValidator("^[A-Za-z0-9_]+$"); auto const& feature = list.front(); if (!featureNameValidator.find(feature)) { reportError(context, content->GetOriginalExpression(), cmStrCat("The feature name '", feature, "' contains invalid characters.")); return std::string(); } const auto LG_BEGIN = cmStrCat( "(list.cbegin() + 1, list.cend()), "|"_s), '>'); const auto LG_END = cmStrCat("'); list.front() = LG_BEGIN; list.push_back(LG_END); return list.to_string(); } } linkGroupNode; static const struct HostLinkNode : public cmGeneratorExpressionNode { HostLinkNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return ZeroOrMoreParameters; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { if (!context->HeadTarget || !dagChecker || !dagChecker->EvaluatingLinkOptionsExpression()) { reportError(context, content->GetOriginalExpression(), "$ may only be used with binary targets " "to specify link options."); return std::string(); } return context->HeadTarget->IsDeviceLink() ? std::string() : cmList::to_string(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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { if (!context->HeadTarget || !dagChecker || !dagChecker->EvaluatingLinkOptionsExpression()) { reportError(context, content->GetOriginalExpression(), "$ may only be used with binary targets " "to specify link options."); return std::string(); } if (context->HeadTarget->IsDeviceLink()) { cmList list{ parameters.begin(), parameters.end() }; const auto DL_BEGIN = ""_s; const auto DL_END = ""_s; cm::erase_if(list, [&](const std::string& item) { return item == DL_BEGIN || item == DL_END; }); list.insert(list.begin(), static_cast(DL_BEGIN)); list.push_back(static_cast(DL_END)); return list.to_string(); } return std::string(); } } deviceLinkNode; static std::string getLinkedTargetsContent( cmGeneratorTarget const* target, std::string const& prop, cmGeneratorExpressionContext* context, cmGeneratorExpressionDAGChecker* dagChecker, cmGeneratorTarget::UseTo usage) { std::string result; if (cmLinkImplementationLibraries const* impl = target->GetLinkImplementationLibraries( context->Config, cmGeneratorTarget::UseTo::Compile)) { for (cmLinkImplItem const& lib : impl->Libraries) { if (lib.Target) { // Pretend $ 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, usage); 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 "$ expression requires a non-empty " "target name and property name."; } if (targetName.empty()) { return "$ 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& 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(); } cmLocalGenerator const* lg = context->CurrentTarget ? context->CurrentTarget->GetLocalGenerator() : context->LG; target = 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(), "$ 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 " "$ signature instead."); return std::string(); } } else { reportError( context, content->GetOriginalExpression(), "$ expression requires one or two parameters"); return std::string(); } if (propertyName == "SOURCES") { context->SourceSensitiveTargets.insert(target); } if (propertyName.empty()) { reportError( context, content->GetOriginalExpression(), "$ 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); } bool const evaluatingLinkLibraries = dagCheckerParent && dagCheckerParent->EvaluatingLinkLibraries(); std::string interfacePropertyName; bool isInterfaceProperty = false; cmGeneratorTarget::UseTo usage = cmGeneratorTarget::UseTo::Compile; if (cm::optional transitiveProp = target->IsTransitiveProperty(propertyName, context->LG, context->Config, evaluatingLinkLibraries)) { interfacePropertyName = std::string(transitiveProp->InterfaceName); isInterfaceProperty = transitiveProp->InterfaceName == propertyName; usage = transitiveProp->Usage; } if (dagCheckerParent) { // This $ node has been reached while evaluating // another target property value. Check that the outermost evaluation // expects such nested evaluations. if (dagCheckerParent->EvaluatingGenexExpression() || dagCheckerParent->EvaluatingPICExpression() || dagCheckerParent->EvaluatingLinkerLauncher()) { // No check required. } else if (evaluatingLinkLibraries) { if (!interfacePropertyName.empty()) { reportError( context, content->GetOriginalExpression(), "$ expression in link libraries " "evaluation depends on target property which is transitive " "over the link libraries, creating a recursion."); return std::string(); } } else { assert(dagCheckerParent->EvaluatingTransitiveProperty()); } } if (isInterfaceProperty) { return cmGeneratorExpression::StripEmptyListElements( target->EvaluateInterfaceProperty( propertyName, context, dagCheckerParent, usage, cmGeneratorTarget::TransitiveClosure::Subgraph)); } cmGeneratorExpressionDAGChecker dagChecker( context->Backtrace, target, propertyName, content, dagCheckerParent, context->LG, context->Config); 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 (cmValue p = target->GetProperty(propertyName)) { result = *p; haveProp = true; } else if (evaluatingLinkLibraries) { return std::string(); } // Properties named by COMPATIBLE_INTERFACE_ properties combine over // the transitive link closure as a single order-independent value. // Imported targets do not themselves have a defined value for these // properties, but they can contribute to the value of a non-imported // dependent. // // For COMPATIBLE_INTERFACE_{BOOL,STRING}: // * If set on this target, use the value directly. It is checked // elsewhere for consistency over the transitive link closure. // * If not set on this target, compute the value from the closure. // // For COMPATIBLE_INTERFACE_NUMBER_{MAX,MIN} we always compute the value // from this target and the transitive link closure to get the max or min. if (!haveProp && !target->IsImported()) { 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 (!evaluatingLinkLibraries && !target->IsImported()) { 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 : ""; } } // Some properties, such as usage requirements, combine over the // transitive link closure as an ordered list. if (!interfacePropertyName.empty()) { result = cmGeneratorExpression::StripEmptyListElements( this->EvaluateDependentExpression(result, context->LG, context, target, &dagChecker, target)); std::string linkedTargetsContent = getLinkedTargetsContent( target, interfacePropertyName, context, &dagChecker, usage); 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& 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { std::string const& tgtName = parameters.front(); cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName); if (!gt) { std::ostringstream e; e << "Objects of target \"" << tgtName << "\" referenced but no such target exists."; reportError(context, content->GetOriginalExpression(), e.str()); return std::string(); } cmStateEnums::TargetType type = gt->GetType(); if (type != cmStateEnums::EXECUTABLE && type != cmStateEnums::STATIC_LIBRARY && type != cmStateEnums::SHARED_LIBRARY && type != cmStateEnums::MODULE_LIBRARY && type != cmStateEnums::OBJECT_LIBRARY) { std::ostringstream e; e << "Objects of target \"" << tgtName << "\" referenced but is not one of the allowed target types " << "(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT)."; reportError(context, content->GetOriginalExpression(), e.str()); return std::string(); } cmGlobalGenerator* gg = context->LG->GetGlobalGenerator(); { std::string reason; if (!context->EvaluateForBuildsystem && !gt->Target->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(); } } cmList objects; if (gt->IsImported()) { cmValue loc = nullptr; cmValue imp = nullptr; std::string suffix; if (gt->Target->GetMappedConfig(context->Config, loc, imp, suffix)) { objects.assign(*loc); } context->HadContextSensitiveCondition = true; } else { gt->GetTargetObjectNames(context->Config, objects); std::string obj_dir; if (context->EvaluateForBuildsystem && !gg->SupportsCrossConfigs()) { // Use object file directory with buildsystem placeholder. obj_dir = gt->ObjectDirectory; context->HadContextSensitiveCondition = gt->HasContextDependentSources(); } else { // Use object file directory with per-config location. obj_dir = gt->GetObjectDirectory(context->Config); context->HadContextSensitiveCondition = true; } for (auto& o : objects) { o = cmStrCat(obj_dir, o); } } // Create the cmSourceFile instances in the referencing directory. cmMakefile* mf = context->LG->GetMakefile(); for (std::string const& o : objects) { mf->AddTargetObject(tgtName, o); } return objects.to_string(); } } targetObjectsNode; struct TargetRuntimeDllsBaseNode : public cmGeneratorExpressionNode { std::vector CollectDlls( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) const { std::string const& 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::vector(); } cmStateEnums::TargetType type = gt->GetType(); if (type != cmStateEnums::EXECUTABLE && type != cmStateEnums::SHARED_LIBRARY && type != cmStateEnums::MODULE_LIBRARY) { std::ostringstream e; e << "Objects of target \"" << tgtName << "\" referenced but is not one of the allowed target types " << "(EXECUTABLE, SHARED, MODULE)."; reportError(context, content->GetOriginalExpression(), e.str()); return std::vector(); } if (auto* cli = gt->GetLinkInformation(context->Config)) { std::vector dllPaths; auto const& dlls = cli->GetRuntimeDLLs(); for (auto const& dll : dlls) { if (auto loc = dll->MaybeGetLocation(context->Config)) { dllPaths.emplace_back(*loc); } } return dllPaths; } return std::vector(); } }; static const struct TargetRuntimeDllsNode : public TargetRuntimeDllsBaseNode { TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default) std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { std::vector dlls = CollectDlls(parameters, context, content); return cmList::to_string(dlls); } } targetRuntimeDllsNode; static const struct TargetRuntimeDllDirsNode : public TargetRuntimeDllsBaseNode { TargetRuntimeDllDirsNode() {} // NOLINT(modernize-use-equals-default) std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { std::vector dlls = CollectDlls(parameters, context, content); std::vector dllDirs; for (const std::string& dll : dlls) { std::string directory = cmSystemTools::GetFilenamePath(dll); if (std::find(dllDirs.begin(), dllDirs.end(), directory) == dllDirs.end()) { dllDirs.push_back(directory); } } return cmList::to_string(dllDirs); } } targetRuntimeDllDirsNode; static const struct CompileFeaturesNode : public cmGeneratorExpressionNode { CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return OneOrMoreParameters; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { cmGeneratorTarget const* target = context->HeadTarget; if (!target) { reportError( context, content->GetOriginalExpression(), "$ 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; static LangMap availableFeatures; LangMap testedFeatures; cmStandardLevelResolver standardResolver(context->LG->GetMakefile()); for (std::string const& p : parameters) { std::string error; std::string lang; if (!standardResolver.CompileFeatureKnown( context->HeadTarget->Target->GetName(), p, lang, &error)) { reportError(context, content->GetOriginalExpression(), error); return std::string(); } testedFeatures[lang].push_back(p); if (availableFeatures.find(lang) == availableFeatures.end()) { cmValue featuresKnown = standardResolver.CompileFeaturesAvailable(lang, &error); if (!featuresKnown) { reportError(context, content->GetOriginalExpression(), error); return std::string(); } availableFeatures[lang].assign(featuresKnown); } } bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries(); for (auto const& lit : testedFeatures) { std::vector const& langAvailable = availableFeatures[lit.first]; cmValue standardDefault = context->LG->GetMakefile()->GetDefinition( "CMAKE_" + lit.first + "_STANDARD_DEFAULT"); for (std::string const& it : lit.second) { if (!cm::contains(langAvailable, it)) { return "0"; } if (standardDefault && standardDefault->empty()) { // This compiler has no notion of language standard levels. // All features known for the language are always available. continue; } if (!standardResolver.HaveStandardAvailable(target, lit.first, context->Config, it)) { if (evalLL) { cmValue l = target->GetLanguageStandard(lit.first, context->Config); if (!l) { l = standardDefault; } assert(l); context->MaxLanguageStandard[target][lit.first] = *l; } else { return "0"; } } } } return "1"; } } compileFeaturesNode; static const char* targetPolicyWhitelist[] = { nullptr #define TARGET_POLICY_STRING(POLICY) , #POLICY CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_STRING) #undef TARGET_POLICY_STRING }; static 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; } static 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { if (!context->HeadTarget) { reportError( context, content->GetOriginalExpression(), "$ 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(), "$ 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& /*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 ArtifactLinkerLibraryTag; class ArtifactLinkerImportTag; class ArtifactNameTag; class ArtifactImportTag; class ArtifactPathTag; class ArtifactPdbTag; class ArtifactSonameTag; class ArtifactSonameImportTag; class ArtifactBundleDirTag; class ArtifactBundleDirNameTag; class ArtifactBundleContentDirTag; template struct TargetFilesystemArtifactDependency { static void AddDependency(cmGeneratorTarget* target, cmGeneratorExpressionContext* context) { context->DependTargets.insert(target); context->AllTargets.insert(target); } }; struct TargetFilesystemArtifactDependencyCMP0112 { static void AddDependency(cmGeneratorTarget* target, cmGeneratorExpressionContext* context) { context->AllTargets.insert(target); cmLocalGenerator* lg = context->LG; switch (target->GetPolicyStatusCMP0112()) { case cmPolicies::WARN: if (lg->GetMakefile()->PolicyOptionalWarningEnabled( "CMAKE_POLICY_WARNING_CMP0112")) { std::string err = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0112), "\nDependency being added to target:\n \"", target->GetName(), "\"\n"); lg->GetCMakeInstance()->IssueMessage(MessageType ::AUTHOR_WARNING, err, context->Backtrace); } CM_FALLTHROUGH; case cmPolicies::OLD: context->DependTargets.insert(target); break; case cmPolicies::REQUIRED_IF_USED: case cmPolicies::REQUIRED_ALWAYS: case cmPolicies::NEW: break; } } }; template struct TargetFilesystemArtifactDependency : TargetFilesystemArtifactDependencyCMP0112 { }; template struct TargetFilesystemArtifactDependency : TargetFilesystemArtifactDependencyCMP0112 { }; template <> struct TargetFilesystemArtifactDependency : TargetFilesystemArtifactDependencyCMP0112 { }; template <> struct TargetFilesystemArtifactDependency : TargetFilesystemArtifactDependencyCMP0112 { }; template <> struct TargetFilesystemArtifactDependency : TargetFilesystemArtifactDependencyCMP0112 { }; template struct TargetFilesystemArtifactResultCreator { static std::string Create(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content); }; template <> struct TargetFilesystemArtifactResultCreator { 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(); } if (target->IsArchivedAIXSharedLibrary()) { ::reportError(context, content->GetOriginalExpression(), "TARGET_SONAME_FILE is not allowed for " "AIX_SHARED_LIBRARY_ARCHIVE libraries."); return std::string(); } std::string result = cmStrCat(target->GetDirectory(context->Config), '/', target->GetSOName(context->Config)); return result; } }; template <> struct TargetFilesystemArtifactResultCreator { 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_IMPORT_FILE is not allowed " "for DLL target platforms."); return std::string(); } if (target->GetType() != cmStateEnums::SHARED_LIBRARY) { ::reportError(context, content->GetOriginalExpression(), "TARGET_SONAME_IMPORT_FILE is allowed only for " "SHARED libraries."); return std::string(); } if (target->IsArchivedAIXSharedLibrary()) { ::reportError(context, content->GetOriginalExpression(), "TARGET_SONAME_IMPORT_FILE is not allowed for " "AIX_SHARED_LIBRARY_ARCHIVE libraries."); return std::string(); } if (target->HasImportLibrary(context->Config)) { return cmStrCat(target->GetDirectory( context->Config, cmStateEnums::ImportLibraryArtifact), '/', target->GetSOName(context->Config, cmStateEnums::ImportLibraryArtifact)); } return std::string{}; } }; template <> struct TargetFilesystemArtifactResultCreator { 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 { static std::string Create(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { // The file used to link to the target (.so, .lib, .a) or import file // (.lib, .tbd). 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 { static std::string Create(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { // The file used to link to the target (.dylib, .so, .a). if (!target->IsLinkable() || target->GetType() == cmStateEnums::EXECUTABLE) { ::reportError(context, content->GetOriginalExpression(), "TARGET_LINKER_LIBRARY_FILE is allowed only for libraries " "with ENABLE_EXPORTS."); return std::string(); } if (!target->IsDLLPlatform() || target->GetType() == cmStateEnums::STATIC_LIBRARY) { return target->GetFullPath(context->Config, cmStateEnums::RuntimeBinaryArtifact); } return std::string{}; } }; template <> struct TargetFilesystemArtifactResultCreator { static std::string Create(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { // The file used to link to the target (.lib, .tbd). if (!target->IsLinkable()) { ::reportError( context, content->GetOriginalExpression(), "TARGET_LINKER_IMPORT_FILE is allowed only for libraries and " "executables with ENABLE_EXPORTS."); return std::string(); } if (target->HasImportLibrary(context->Config)) { return target->GetFullPath(context->Config, cmStateEnums::ImportLibraryArtifact); } return std::string{}; } }; template <> struct TargetFilesystemArtifactResultCreator { 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 { static std::string Create(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { if (target->IsImported()) { ::reportError( context, content->GetOriginalExpression(), "TARGET_BUNDLE_DIR_NAME not allowed for IMPORTED targets."); return std::string(); } if (!target->IsBundleOnApple()) { ::reportError( context, content->GetOriginalExpression(), "TARGET_BUNDLE_DIR_NAME is allowed only for Bundle targets."); return std::string(); } auto level = cmGeneratorTarget::BundleDirLevel; auto config = context->Config; if (target->IsAppBundleOnApple()) { return target->GetAppBundleDirectory(config, level); } if (target->IsFrameworkOnApple()) { return target->GetFrameworkDirectory(config, level); } if (target->IsCFBundleOnApple()) { return target->GetCFBundleDirectory(config, level); } return std::string(); } }; template <> struct TargetFilesystemArtifactResultCreator { 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 { static std::string Create(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* /*unused*/) { return target->GetFullPath(context->Config, cmStateEnums::RuntimeBinaryArtifact, true); } }; template <> struct TargetFilesystemArtifactResultCreator { static std::string Create(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* /*unused*/) { if (target->HasImportLibrary(context->Config)) { return target->GetFullPath(context->Config, cmStateEnums::ImportLibraryArtifact, true); } return std::string{}; } }; template struct TargetFilesystemArtifactResultGetter { static std::string Get(const std::string& result); }; template <> struct TargetFilesystemArtifactResultGetter { static std::string Get(const std::string& result) { return cmSystemTools::GetFilenameName(result); } }; template <> struct TargetFilesystemArtifactResultGetter { static std::string Get(const std::string& result) { return cmSystemTools::GetFilenamePath(result); } }; template <> struct TargetFilesystemArtifactResultGetter { 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& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const { // Lookup the referenced target. std::string const& 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 struct TargetFilesystemArtifact : public TargetArtifactBase { TargetFilesystemArtifact() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return 1; } std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* dagChecker) const override { cmGeneratorTarget* target = this->GetTarget(parameters, context, content, dagChecker); if (!target) { return std::string(); } // Not a dependent target if we are querying for ArtifactDirTag, // ArtifactNameTag, ArtifactBundleDirTag, ArtifactBundleDirNameTag, // and ArtifactBundleContentDirTag TargetFilesystemArtifactDependency::AddDependency( target, context); std::string result = TargetFilesystemArtifactResultCreator::Create(target, context, content); if (context->HadError) { return std::string(); } return TargetFilesystemArtifactResultGetter::Get(result); } }; template struct TargetFilesystemArtifactNodeGroup { TargetFilesystemArtifactNodeGroup() // NOLINT(modernize-use-equals-default) { } TargetFilesystemArtifact File; TargetFilesystemArtifact FileName; TargetFilesystemArtifact FileDir; }; static const TargetFilesystemArtifactNodeGroup targetNodeGroup; static const TargetFilesystemArtifactNodeGroup targetImportNodeGroup; static const TargetFilesystemArtifactNodeGroup targetLinkerNodeGroup; static const TargetFilesystemArtifactNodeGroup targetLinkerLibraryNodeGroup; static const TargetFilesystemArtifactNodeGroup targetLinkerImportNodeGroup; static const TargetFilesystemArtifactNodeGroup targetSoNameNodeGroup; static const TargetFilesystemArtifactNodeGroup targetSoNameImportNodeGroup; static const TargetFilesystemArtifactNodeGroup targetPdbNodeGroup; static const TargetFilesystemArtifact targetBundleDirNode; static const TargetFilesystemArtifact targetBundleDirNameNode; static const TargetFilesystemArtifact targetBundleContentDirNode; // // To retrieve base name for various artifacts // template struct TargetOutputNameArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content); }; template <> struct TargetOutputNameArtifactResultGetter { 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 { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* /*unused*/) { if (target->HasImportLibrary(context->Config)) { return target->GetOutputName(context->Config, cmStateEnums::ImportLibraryArtifact) + target->GetFilePostfix(context->Config); } return std::string{}; } }; template <> struct TargetOutputNameArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { // The library file used to link to the target (.so, .lib, .a) or import // file (.lin, .tbd). 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 { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { // The library file used to link to the target (.so, .lib, .a). if (!target->IsLinkable() || target->GetType() == cmStateEnums::EXECUTABLE) { ::reportError(context, content->GetOriginalExpression(), "TARGET_LINKER_LIBRARY_FILE_BASE_NAME is allowed only for " "libraries with ENABLE_EXPORTS."); return std::string(); } if (!target->IsDLLPlatform() || target->GetType() == cmStateEnums::STATIC_LIBRARY) { return target->GetOutputName(context->Config, cmStateEnums::ImportLibraryArtifact) + target->GetFilePostfix(context->Config); } return std::string{}; } }; template <> struct TargetOutputNameArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { // The import file used to link to the target (.lib, .tbd). if (!target->IsLinkable()) { ::reportError(context, content->GetOriginalExpression(), "TARGET_LINKER_IMPORT_FILE_BASE_NAME is allowed only for " "libraries and executables with ENABLE_EXPORTS."); return std::string(); } if (target->HasImportLibrary(context->Config)) { return target->GetOutputName(context->Config, cmStateEnums::ImportLibraryArtifact) + target->GetFilePostfix(context->Config); } return std::string{}; } }; template <> struct TargetOutputNameArtifactResultGetter { 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 struct TargetFileBaseNameArtifact : public TargetArtifactBase { TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return 1; } std::string Evaluate( const std::vector& 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::Get( target, context, content); if (context->HadError) { return std::string(); } return result; } }; static const TargetFileBaseNameArtifact targetFileBaseNameNode; static const TargetFileBaseNameArtifact targetImportFileBaseNameNode; static const TargetFileBaseNameArtifact targetLinkerFileBaseNameNode; static const TargetFileBaseNameArtifact targetLinkerLibraryFileBaseNameNode; static const TargetFileBaseNameArtifact targetLinkerImportFileBaseNameNode; static const TargetFileBaseNameArtifact targetPdbFileBaseNameNode; class ArtifactFilePrefixTag; class ArtifactImportFilePrefixTag; class ArtifactLinkerFilePrefixTag; class ArtifactLinkerLibraryFilePrefixTag; class ArtifactLinkerImportFilePrefixTag; class ArtifactFileSuffixTag; class ArtifactImportFileSuffixTag; class ArtifactLinkerFileSuffixTag; class ArtifactLinkerLibraryFileSuffixTag; class ArtifactLinkerImportFileSuffixTag; template struct TargetFileArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content); }; template <> struct TargetFileArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent*) { return target->GetFilePrefix(context->Config); } }; template <> struct TargetFileArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent*) { if (target->HasImportLibrary(context->Config)) { return target->GetFilePrefix(context->Config, cmStateEnums::ImportLibraryArtifact); } return std::string{}; } }; template <> struct TargetFileArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { if (!target->IsLinkable()) { ::reportError( context, content->GetOriginalExpression(), "TARGET_LINKER_FILE_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 { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { if (!target->IsLinkable() || target->GetType() == cmStateEnums::EXECUTABLE) { ::reportError( context, content->GetOriginalExpression(), "TARGET_LINKER_LIBRARY_FILE_PREFIX is allowed only for libraries " "with ENABLE_EXPORTS."); return std::string(); } if (!target->IsDLLPlatform() || target->GetType() == cmStateEnums::STATIC_LIBRARY) { return target->GetFilePrefix(context->Config, cmStateEnums::RuntimeBinaryArtifact); } return std::string{}; } }; template <> struct TargetFileArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { if (!target->IsLinkable()) { ::reportError( context, content->GetOriginalExpression(), "TARGET_LINKER_IMPORT_FILE_PREFIX is allowed only for libraries and " "executables with ENABLE_EXPORTS."); return std::string(); } if (target->HasImportLibrary(context->Config)) { return target->GetFilePrefix(context->Config, cmStateEnums::ImportLibraryArtifact); } return std::string{}; } }; template <> struct TargetFileArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent*) { return target->GetFileSuffix(context->Config); } }; template <> struct TargetFileArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent*) { if (target->HasImportLibrary(context->Config)) { return target->GetFileSuffix(context->Config, cmStateEnums::ImportLibraryArtifact); } return std::string{}; } }; template <> struct TargetFileArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { if (!target->IsLinkable()) { ::reportError( context, content->GetOriginalExpression(), "TARGET_LINKER_FILE_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 <> struct TargetFileArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { if (!target->IsLinkable() || target->GetType() == cmStateEnums::STATIC_LIBRARY) { ::reportError(context, content->GetOriginalExpression(), "TARGET_LINKER_LIBRARY_FILE_SUFFIX is allowed only for " "libraries with ENABLE_EXPORTS."); return std::string(); } if (!target->IsDLLPlatform() || target->GetType() == cmStateEnums::STATIC_LIBRARY) { return target->GetFileSuffix(context->Config, cmStateEnums::RuntimeBinaryArtifact); } return std::string{}; } }; template <> struct TargetFileArtifactResultGetter { static std::string Get(cmGeneratorTarget* target, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content) { if (!target->IsLinkable()) { ::reportError( context, content->GetOriginalExpression(), "TARGET_LINKER_IMPORT_FILE_SUFFIX is allowed only for libraries and " "executables with ENABLE_EXPORTS."); return std::string(); } if (target->HasImportLibrary(context->Config)) { return target->GetFileSuffix(context->Config, cmStateEnums::ImportLibraryArtifact); } return std::string{}; } }; template struct TargetFileArtifact : public TargetArtifactBase { TargetFileArtifact() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return 1; } std::string Evaluate( const std::vector& 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::Get(target, context, content); if (context->HadError) { return std::string(); } return result; } }; static const TargetFileArtifact targetFilePrefixNode; static const TargetFileArtifact targetImportFilePrefixNode; static const TargetFileArtifact targetLinkerFilePrefixNode; static const TargetFileArtifact targetLinkerLibraryFilePrefixNode; static const TargetFileArtifact targetLinkerImportFilePrefixNode; static const TargetFileArtifact targetFileSuffixNode; static const TargetFileArtifact targetImportFileSuffixNode; static const TargetFileArtifact targetLinkerFileSuffixNode; static const TargetFileArtifact targetLinkerLibraryFileSuffixNode; static const TargetFileArtifact targetLinkerImportFileSuffixNode; static const struct ShellPathNode : public cmGeneratorExpressionNode { ShellPathNode() {} // NOLINT(modernize-use-equals-default) std::string Evaluate( const std::vector& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { cmList list_in{ parameters.front() }; if (list_in.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 list_out; list_out.reserve(list_in.size()); for (auto const& in : list_in) { if (!cmSystemTools::FileIsFullPath(in)) { reportError(context, content->GetOriginalExpression(), "\"" + in + "\" is not an absolute path."); return std::string(); } list_out.emplace_back(converter.ConvertDirectorySeparatorsForShell(in)); } return cmJoin(list_out, separator); } } shellPathNode; const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( const std::string& identifier) { static std::map const nodeMap{ { "0", &zeroNode }, { "1", &oneNode }, { "AND", &andNode }, { "OR", &orNode }, { "NOT", ¬Node }, { "C_COMPILER_ID", &cCompilerIdNode }, { "CXX_COMPILER_ID", &cxxCompilerIdNode }, { "OBJC_COMPILER_ID", &objcCompilerIdNode }, { "OBJCXX_COMPILER_ID", &objcxxCompilerIdNode }, { "CUDA_COMPILER_ID", &cudaCompilerIdNode }, { "Fortran_COMPILER_ID", &fortranCompilerIdNode }, { "HIP_COMPILER_ID", &hipCompilerIdNode }, { "VERSION_GREATER", &versionGreaterNode }, { "VERSION_GREATER_EQUAL", &versionGreaterEqNode }, { "VERSION_LESS", &versionLessNode }, { "VERSION_LESS_EQUAL", &versionLessEqNode }, { "VERSION_EQUAL", &versionEqualNode }, { "C_COMPILER_VERSION", &cCompilerVersionNode }, { "CXX_COMPILER_VERSION", &cxxCompilerVersionNode }, { "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode }, { "OBJC_COMPILER_VERSION", &objcCompilerVersionNode }, { "OBJCXX_COMPILER_VERSION", &objcxxCompilerVersionNode }, { "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode }, { "HIP_COMPILER_VERSION", &hipCompilerVersionNode }, { "C_COMPILER_FRONTEND_VARIANT", &cCompilerFrontendVariantNode }, { "CXX_COMPILER_FRONTEND_VARIANT", &cxxCompilerFrontendVariantNode }, { "CUDA_COMPILER_FRONTEND_VARIANT", &cudaCompilerFrontendVariantNode }, { "OBJC_COMPILER_FRONTEND_VARIANT", &objcCompilerFrontendVariantNode }, { "OBJCXX_COMPILER_FRONTEND_VARIANT", &objcxxCompilerFrontendVariantNode }, { "Fortran_COMPILER_FRONTEND_VARIANT", &fortranCompilerFrontendVariantNode }, { "HIP_COMPILER_FRONTEND_VARIANT", &hipCompilerFrontendVariantNode }, { "PLATFORM_ID", &platformIdNode }, { "COMPILE_FEATURES", &compileFeaturesNode }, { "CONFIGURATION", &configurationNode }, { "CONFIG", &configurationTestNode }, { "TARGET_FILE", &targetNodeGroup.File }, { "TARGET_IMPORT_FILE", &targetImportNodeGroup.File }, { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File }, { "TARGET_LINKER_LIBRARY_FILE", &targetLinkerLibraryNodeGroup.File }, { "TARGET_LINKER_IMPORT_FILE", &targetLinkerImportNodeGroup.File }, { "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File }, { "TARGET_SONAME_IMPORT_FILE", &targetSoNameImportNodeGroup.File }, { "TARGET_PDB_FILE", &targetPdbNodeGroup.File }, { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode }, { "TARGET_IMPORT_FILE_BASE_NAME", &targetImportFileBaseNameNode }, { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode }, { "TARGET_LINKER_LIBRARY_FILE_BASE_NAME", &targetLinkerLibraryFileBaseNameNode }, { "TARGET_LINKER_IMPORT_FILE_BASE_NAME", &targetLinkerImportFileBaseNameNode }, { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode }, { "TARGET_FILE_PREFIX", &targetFilePrefixNode }, { "TARGET_IMPORT_FILE_PREFIX", &targetImportFilePrefixNode }, { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode }, { "TARGET_LINKER_LIBRARY_FILE_PREFIX", &targetLinkerLibraryFilePrefixNode }, { "TARGET_LINKER_IMPORT_FILE_PREFIX", &targetLinkerImportFilePrefixNode }, { "TARGET_FILE_SUFFIX", &targetFileSuffixNode }, { "TARGET_IMPORT_FILE_SUFFIX", &targetImportFileSuffixNode }, { "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode }, { "TARGET_LINKER_LIBRARY_FILE_SUFFIX", &targetLinkerLibraryFileSuffixNode }, { "TARGET_LINKER_IMPORT_FILE_SUFFIX", &targetLinkerImportFileSuffixNode }, { "TARGET_FILE_NAME", &targetNodeGroup.FileName }, { "TARGET_IMPORT_FILE_NAME", &targetImportNodeGroup.FileName }, { "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.FileName }, { "TARGET_LINKER_LIBRARY_FILE_NAME", &targetLinkerLibraryNodeGroup.FileName }, { "TARGET_LINKER_IMPORT_FILE_NAME", &targetLinkerImportNodeGroup.FileName }, { "TARGET_SONAME_FILE_NAME", &targetSoNameNodeGroup.FileName }, { "TARGET_SONAME_IMPORT_FILE_NAME", &targetSoNameImportNodeGroup.FileName }, { "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName }, { "TARGET_FILE_DIR", &targetNodeGroup.FileDir }, { "TARGET_IMPORT_FILE_DIR", &targetImportNodeGroup.FileDir }, { "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.FileDir }, { "TARGET_LINKER_LIBRARY_FILE_DIR", &targetLinkerLibraryNodeGroup.FileDir }, { "TARGET_LINKER_IMPORT_FILE_DIR", &targetLinkerImportNodeGroup.FileDir }, { "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.FileDir }, { "TARGET_SONAME_IMPORT_FILE_DIR", &targetSoNameImportNodeGroup.FileDir }, { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir }, { "TARGET_BUNDLE_DIR", &targetBundleDirNode }, { "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode }, { "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode }, { "STREQUAL", &strEqualNode }, { "EQUAL", &equalNode }, { "IN_LIST", &inListNode }, { "FILTER", &filterNode }, { "REMOVE_DUPLICATES", &removeDuplicatesNode }, { "LIST", &listNode }, { "LOWER_CASE", &lowerCaseNode }, { "UPPER_CASE", &upperCaseNode }, { "PATH", &pathNode }, { "PATH_EQUAL", &pathEqualNode }, { "MAKE_C_IDENTIFIER", &makeCIdentifierNode }, { "BOOL", &boolNode }, { "IF", &ifNode }, { "ANGLE-R", &angle_rNode }, { "COMMA", &commaNode }, { "SEMICOLON", &semicolonNode }, { "QUOTE", "eNode }, { "TARGET_PROPERTY", &targetPropertyNode }, { "TARGET_NAME", &targetNameNode }, { "TARGET_OBJECTS", &targetObjectsNode }, { "TARGET_POLICY", &targetPolicyNode }, { "TARGET_EXISTS", &targetExistsNode }, { "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode }, { "TARGET_GENEX_EVAL", &targetGenexEvalNode }, { "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode }, { "TARGET_RUNTIME_DLL_DIRS", &targetRuntimeDllDirsNode }, { "GENEX_EVAL", &genexEvalNode }, { "BUILD_INTERFACE", &buildInterfaceNode }, { "INSTALL_INTERFACE", &installInterfaceNode }, { "BUILD_LOCAL_INTERFACE", &buildLocalInterfaceNode }, { "INSTALL_PREFIX", &installPrefixNode }, { "JOIN", &joinNode }, { "COMPILE_ONLY", &compileOnlyNode }, { "LINK_ONLY", &linkOnlyNode }, { "COMPILE_LANG_AND_ID", &languageAndIdNode }, { "COMPILE_LANGUAGE", &languageNode }, { "LINK_LANG_AND_ID", &linkLanguageAndIdNode }, { "LINK_LANGUAGE", &linkLanguageNode }, { "LINK_LIBRARY", &linkLibraryNode }, { "LINK_GROUP", &linkGroupNode }, { "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); }