| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmGeneratorExpressionNode.h" | |
| #include <algorithm> | |
| #include <cassert> | |
| #include <cerrno> | |
| #include <cstdlib> | |
| #include <cstring> | |
| #include <exception> | |
| #include <functional> | |
| #include <map> | |
| #include <memory> | |
| #include <set> | |
| #include <stdexcept> | |
| #include <unordered_map> | |
| #include <utility> | |
| #include <cm/iterator> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include <cmext/algorithm> | |
| #include <cmext/string_view> | |
| #include "cmsys/RegularExpression.hxx" | |
| #include "cmsys/String.h" | |
| #include "cmCMakePath.h" | |
| #include "cmCMakeString.hxx" | |
| #include "cmComputeLinkInformation.h" | |
| #include "cmDirectoryPropertyHelper.h" | |
| #include "cmGenExContext.h" | |
| #include "cmGenExEvaluation.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorExpressionDAGChecker.h" | |
| #include "cmGeneratorExpressionEvaluator.h" | |
| #include "cmGeneratorFileSet.h" | |
| #include "cmGeneratorRule.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmLinkItem.h" | |
| #include "cmList.h" | |
| #include "cmListFileCache.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmOutputConverter.h" | |
| #include "cmPolicies.h" | |
| #include "cmRange.h" | |
| #include "cmRule.h" | |
| #include "cmSourceFile.h" | |
| #include "cmStandardLevelResolver.h" | |
| #include "cmState.h" | |
| #include "cmStateSnapshot.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmUnreachable.h" | |
| #include "cmValue.h" | |
| #include "cmake.h" | |
| namespace { | |
| bool IsValidPropertyName(std::string const& propertyName) | |
| { | |
| static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_:]+$"); | |
| return propertyNameValidator.find(propertyName); | |
| } | |
| bool IsValidPropertyName(cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| std::string const& propertyName) | |
| { | |
| if (!IsValidPropertyName(propertyName)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "Property name not supported."); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool HasKnownObjectFileLocation(cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| std::string const& genex, | |
| cmGeneratorTarget const* target) | |
| { | |
| std::string reason; | |
| if (!eval->EvaluateForBuildsystem && | |
| !target->Target->HasKnownObjectFileLocation(&reason)) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("The evaluation of the ", genex, | |
| " generator expression is only suitable for consumption by " | |
| "CMake (limited", | |
| reason, "). It is not suitable for writing out elsewhere.")); | |
| return false; | |
| } | |
| return true; | |
| } | |
| } // namespace | |
| std::string cmGeneratorExpressionNode::EvaluateDependentExpression( | |
| std::string const& prop, cm::GenEx::Evaluation* eval, | |
| cmGeneratorTarget const* headTarget, | |
| cmGeneratorExpressionDAGChecker* dagChecker, | |
| cmGeneratorTarget const* currentTarget) | |
| { | |
| cmGeneratorExpression ge(*eval->Context.LG->GetCMakeInstance(), | |
| eval->Backtrace); | |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop); | |
| cge->SetEvaluateForBuildsystem(eval->EvaluateForBuildsystem); | |
| cge->SetQuiet(eval->Quiet); | |
| std::string result = | |
| cge->Evaluate(eval->Context, dagChecker, headTarget, currentTarget); | |
| if (cge->GetHadContextSensitiveCondition()) { | |
| eval->HadContextSensitiveCondition = true; | |
| } | |
| if (cge->GetHadHeadSensitiveCondition()) { | |
| eval->HadHeadSensitiveCondition = true; | |
| } | |
| if (cge->GetHadLinkLanguageSensitiveCondition()) { | |
| eval->HadLinkLanguageSensitiveCondition = true; | |
| } | |
| return result; | |
| } | |
| // Re-evaluate the unevaluated <body> subtree of a binding operation with the | |
| // given operands bound (accessible as $<_0>, $<_1>, ...). A fresh Evaluation | |
| // is built from a copied, mutated Context so that nested binding operations | |
| // can shadow the operands and restore them on exit. | |
| static std::string EvaluateBodyWithBoundOperands( | |
| cmGeneratorExpressionEvaluatorVector const& bodyExpr, | |
| std::vector<std::string> operands, cm::GenEx::Evaluation* eval, | |
| cmGeneratorExpressionDAGChecker* dagChecker) | |
| { | |
| cm::GenEx::Context elemContext = eval->Context; // copy | |
| elemContext.SetBoundOperands(std::move(operands)); | |
| cm::GenEx::Evaluation elemEval( | |
| elemContext, eval->Quiet, eval->HeadTarget, eval->CurrentTarget, | |
| eval->EvaluateForBuildsystem, eval->Backtrace); | |
| std::string result; | |
| for (auto const& pExprEval : bodyExpr) { | |
| result += pExprEval->Evaluate(&elemEval, dagChecker); | |
| if (elemEval.HadError) { | |
| eval->HadError = true; | |
| return std::string{}; | |
| } | |
| } | |
| eval->HadContextSensitiveCondition |= elemEval.HadContextSensitiveCondition; | |
| eval->HadHeadSensitiveCondition |= elemEval.HadHeadSensitiveCondition; | |
| eval->HadLinkLanguageSensitiveCondition |= | |
| elemEval.HadLinkLanguageSensitiveCondition; | |
| eval->DependTargets.insert(elemEval.DependTargets.begin(), | |
| elemEval.DependTargets.end()); | |
| eval->AllTargets.insert(elemEval.AllTargets.begin(), | |
| elemEval.AllTargets.end()); | |
| eval->SeenTargetProperties.insert(elemEval.SeenTargetProperties.begin(), | |
| elemEval.SeenTargetProperties.end()); | |
| eval->SourceSensitiveTargets.insert(elemEval.SourceSensitiveTargets.begin(), | |
| elemEval.SourceSensitiveTargets.end()); | |
| for (auto const& entry : elemEval.MaxLanguageStandard) { | |
| eval->MaxLanguageStandard[entry.first].insert(entry.second.begin(), | |
| entry.second.end()); | |
| } | |
| return result; | |
| } | |
| static std::string EvaluateBodyWithBoundOperand( | |
| cmGeneratorExpressionEvaluatorVector const& bodyExpr, | |
| std::string const& operand, cm::GenEx::Evaluation* eval, | |
| cmGeneratorExpressionDAGChecker* dagChecker) | |
| { | |
| return EvaluateBodyWithBoundOperands(bodyExpr, { operand }, eval, | |
| dagChecker); | |
| } | |
| // Evaluate `predicateBody` once per element of `list`, binding `$<_0>` to the | |
| // element (reusing EvaluateBodyWithBoundOperand). Each result must be exactly | |
| // "0" or "1". Returns the per-element boolean mask, or cm::nullopt after | |
| // reporting an error (non-boolean result, or a failure inside the body). | |
| static cm::optional<std::vector<bool>> EvaluatePredicateMask( | |
| cmGeneratorExpressionEvaluatorVector const& predicateBody, | |
| cmList const& list, cm::string_view subCommand, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) | |
| { | |
| cm::optional<std::vector<bool>> result; | |
| std::vector<bool> mask; | |
| mask.reserve(list.size()); | |
| for (auto const& element : list) { | |
| std::string r = | |
| EvaluateBodyWithBoundOperand(predicateBody, element, eval, dagChecker); | |
| if (eval->HadError) { | |
| return result; | |
| } | |
| if (r == "1") { | |
| mask.push_back(true); | |
| } else if (r == "0") { | |
| mask.push_back(false); | |
| } else { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("sub-command ", subCommand, | |
| ", PREDICATE body must evaluate to \"0\" or \"1\", but " | |
| "evaluated to \"", | |
| r, "\".")); | |
| return result; | |
| } | |
| } | |
| result = std::move(mask); | |
| 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( | |
| std::vector<std::string> const& /*parameters*/, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*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( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return parameters.front(); | |
| } | |
| } oneNode; | |
| struct BoundOperandNode : public cmGeneratorExpressionNode | |
| { | |
| explicit BoundOperandNode(std::size_t index) | |
| : Index(index) | |
| { | |
| } | |
| int NumExpectedParameters() const override { return 0; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& /*parameters*/, | |
| cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (!eval->Context.HasBoundOperand(this->Index)) { | |
| std::size_t const count = eval->Context.BoundOperandCount(); | |
| if (count == 0) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("$<_", this->Index, | |
| "> may only be used inside the body of a binding " | |
| "operation.")); | |
| } else { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat( | |
| "$<_", this->Index, | |
| "> is out of range for the current binding operation, which " | |
| "binds only ", | |
| count, " operand(s) (maximum $<_", count - 1, ">).")); | |
| } | |
| return std::string(); | |
| } | |
| return eval->Context.GetBoundOperand(this->Index); | |
| } | |
| std::size_t Index; | |
| }; | |
| static BoundOperandNode const boundOperandNode0{ 0 }; | |
| static BoundOperandNode const boundOperandNode1{ 1 }; | |
| static const struct OneNode buildInterfaceNode; | |
| static const struct ZeroNode installInterfaceNode; | |
| static const struct OneNode buildLocalInterfaceNode; | |
| struct BooleanOpNode : public cmGeneratorExpressionNode | |
| { | |
| BooleanOpNode(char const* op_, char const* successVal_, | |
| char const* failureVal_) | |
| : op(op_) | |
| , successVal(successVal_) | |
| , failureVal(failureVal_) | |
| { | |
| } | |
| int NumExpectedParameters() const override { return OneOrMoreParameters; } | |
| bool ShouldEvaluateNextParameter(std::vector<std::string> const& parameters, | |
| std::string& def_value) const override | |
| { | |
| if (!parameters.empty() && parameters.back() == failureVal) { | |
| def_value = failureVal; | |
| return false; | |
| } | |
| return true; | |
| } | |
| std::string Evaluate(std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker*) const override | |
| { | |
| for (std::string const& param : parameters) { | |
| if (param == this->failureVal) { | |
| return this->failureVal; | |
| } | |
| if (param != this->successVal) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Parameters to $<", this->op, | |
| "> must resolve to either '0' or '1'.")); | |
| return std::string(); | |
| } | |
| } | |
| return this->successVal; | |
| } | |
| char const *const op, *const successVal, *const failureVal; | |
| }; | |
| static BooleanOpNode const andNode("AND", "1", "0"), orNode("OR", "0", "1"); | |
| static const struct NotNode : public cmGeneratorExpressionNode | |
| { | |
| NotNode() {} // NOLINT(modernize-use-equals-default) | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.front() != "0" && parameters.front() != "1") { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<NOT> parameter must resolve to exactly one '0' or '1' value."); | |
| return std::string(); | |
| } | |
| return parameters.front() == "0" ? "1" : "0"; | |
| } | |
| } notNode; | |
| static const struct BoolNode : public cmGeneratorExpressionNode | |
| { | |
| BoolNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return !cmIsOff(parameters.front()) ? "1" : "0"; | |
| } | |
| } boolNode; | |
| static const struct ArgsNode : public cmGeneratorExpressionNode | |
| { | |
| ArgsNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate(std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation*, | |
| GeneratorExpressionContent const*, | |
| cmGeneratorExpressionDAGChecker*) const override | |
| { | |
| if (parameters.empty()) { | |
| return std::string{}; | |
| } | |
| if (parameters.size() == 1 && parameters[0].empty()) { | |
| return parameters.front(); | |
| } | |
| cmList args{ parameters.begin(), parameters.end(), | |
| cmList::ExpandElements::Yes, cmList::EmptyElements::Yes }; | |
| return cmStrCat("<ARGS>", args.join(","_s), "</ARGS>"); | |
| } | |
| } argsNode; | |
| static const struct IfNode : public cmGeneratorExpressionNode | |
| { | |
| IfNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 3; } | |
| bool ShouldEvaluateNextParameter(std::vector<std::string> const& parameters, | |
| std::string&) const override | |
| { | |
| return (parameters.empty() || | |
| parameters[0] != cmStrCat(parameters.size() - 1, "")); | |
| } | |
| std::string Evaluate(std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker*) const override | |
| { | |
| if (parameters[0] != "1" && parameters[0] != "0") { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "First parameter to $<IF> must resolve to exactly one '0' " | |
| "or '1' value."); | |
| return std::string(); | |
| } | |
| return parameters[0] == "1" ? parameters[1] : parameters[2]; | |
| } | |
| } ifNode; | |
| static const struct StrEqualNode : public cmGeneratorExpressionNode | |
| { | |
| StrEqualNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cm::CMakeString{ parameters.front() }.Compare( | |
| cm::CMakeString::CompOperator::EQUAL, parameters[1]) | |
| ? "1" | |
| : "0"; | |
| } | |
| } strEqualNode; | |
| static const struct StrLessNode : public cmGeneratorExpressionNode | |
| { | |
| StrLessNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cm::CMakeString{ parameters.front() }.Compare( | |
| cm::CMakeString::CompOperator::LESS, parameters[1]) | |
| ? "1" | |
| : "0"; | |
| } | |
| } strLessNode; | |
| static const struct StrLessEqualNode : public cmGeneratorExpressionNode | |
| { | |
| StrLessEqualNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cm::CMakeString{ parameters.front() }.Compare( | |
| cm::CMakeString::CompOperator::LESS_EQUAL, parameters[1]) | |
| ? "1" | |
| : "0"; | |
| } | |
| } strLessEqualNode; | |
| static const struct StrGreaterNode : public cmGeneratorExpressionNode | |
| { | |
| StrGreaterNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cm::CMakeString{ parameters.front() }.Compare( | |
| cm::CMakeString::CompOperator::GREATER, parameters[1]) | |
| ? "1" | |
| : "0"; | |
| } | |
| } strGreaterNode; | |
| static const struct StrGreaterEqualNode : public cmGeneratorExpressionNode | |
| { | |
| StrGreaterEqualNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cm::CMakeString{ parameters.front() }.Compare( | |
| cm::CMakeString::CompOperator::GREATER_EQUAL, parameters[1]) | |
| ? "1" | |
| : "0"; | |
| } | |
| } strGreaterEqualNode; | |
| static const struct EqualNode : public cmGeneratorExpressionNode | |
| { | |
| EqualNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| long numbers[2]; | |
| for (int i = 0; i < 2; ++i) { | |
| if (!ParameterToLong(parameters[i].c_str(), &numbers[i])) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("$<EQUAL> parameter ", parameters[i], | |
| " is not a valid integer.")); | |
| return {}; | |
| } | |
| } | |
| return numbers[0] == numbers[1] ? "1" : "0"; | |
| } | |
| static bool ParameterToLong(char const* param, long* outResult) | |
| { | |
| char const 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| cmList values; | |
| cmList checkValues; | |
| bool check = false; | |
| cmLocalGenerator const* const lg = eval->Context.LG; | |
| switch (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) { | |
| lg->IssuePolicyWarning( | |
| cmPolicies::CMP0085, {}, | |
| cmStrCat("Search Item:\n \""_s, parameters.front(), | |
| "\"\nList:\n \""_s, parameters[1], "\"\n"_s), | |
| eval->Backtrace); | |
| return "0"; | |
| } | |
| if (values.empty()) { | |
| return "0"; | |
| } | |
| break; | |
| 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.size() != 3) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<FILTER:...> expression requires three parameters"); | |
| return {}; | |
| } | |
| if (parameters[1] != "INCLUDE" && parameters[1] != "EXCLUDE") { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<FILTER:...> 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(eval, content->GetOriginalExpression(), | |
| "$<FILTER:...> 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.size() != 1) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<REMOVE_DUPLICATES:...> 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.size() != 1) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<TARGET_EXISTS:...> expression requires one parameter"); | |
| return std::string(); | |
| } | |
| std::string const& targetName = parameters.front(); | |
| if (targetName.empty() || | |
| !cmGeneratorExpression::IsValidTargetName(targetName)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<TARGET_EXISTS:tgt> expression requires a non-empty " | |
| "valid target name."); | |
| return std::string(); | |
| } | |
| return eval->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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.size() != 1) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<TARGET_NAME_IF_EXISTS:...> expression requires one " | |
| "parameter"); | |
| return std::string(); | |
| } | |
| std::string const& targetName = parameters.front(); | |
| if (targetName.empty() || | |
| !cmGeneratorExpression::IsValidTargetName(targetName)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<TARGET_NAME_IF_EXISTS:tgt> expression requires a " | |
| "non-empty valid target name."); | |
| return std::string(); | |
| } | |
| return eval->Context.LG->GetMakefile()->FindTargetToUse(targetName) | |
| ? targetName | |
| : std::string(); | |
| } | |
| } targetNameIfExistsNode; | |
| struct GenexEvaluator : public cmGeneratorExpressionNode | |
| { | |
| GenexEvaluator() {} // NOLINT(modernize-use-equals-default) | |
| protected: | |
| std::string EvaluateExpression( | |
| std::string const& genexOperator, std::string const& expression, | |
| cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const | |
| { | |
| if (eval->HeadTarget) { | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| eval->HeadTarget, cmStrCat(genexOperator, ':', expression), | |
| content, dagCheckerParent, | |
| eval->Context, eval->Backtrace, | |
| }; | |
| switch (dagChecker.Check()) { | |
| case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: | |
| case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: { | |
| dagChecker.ReportError(eval, content->GetOriginalExpression()); | |
| return std::string(); | |
| } | |
| case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: | |
| case cmGeneratorExpressionDAGChecker::DAG: | |
| break; | |
| } | |
| return this->EvaluateDependentExpression( | |
| expression, eval, eval->HeadTarget, &dagChecker, eval->CurrentTarget); | |
| } | |
| return this->EvaluateDependentExpression( | |
| expression, eval, eval->HeadTarget, dagCheckerParent, | |
| eval->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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const override | |
| { | |
| std::string const& targetName = parameters.front(); | |
| if (targetName.empty() || | |
| !cmGeneratorExpression::IsValidTargetName(targetName)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<TARGET_GENEX_EVAL:tgt, ...> expression requires a " | |
| "non-empty valid target name."); | |
| return std::string(); | |
| } | |
| auto const* target = | |
| eval->Context.LG->FindGeneratorTargetToUse(targetName); | |
| if (!target) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("$<TARGET_GENEX_EVAL:tgt, ...> target \"", | |
| targetName, "\" not found.")); | |
| return std::string(); | |
| } | |
| std::string const& expression = parameters[1]; | |
| if (expression.empty()) { | |
| return expression; | |
| } | |
| // Replace the surrounding context with the named target. | |
| cm::GenEx::Evaluation targetEval(eval->Context, eval->Quiet, target, | |
| target, eval->EvaluateForBuildsystem, | |
| eval->Backtrace); | |
| return this->EvaluateExpression("TARGET_GENEX_EVAL", expression, | |
| &targetEval, 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const override | |
| { | |
| std::string const& expression = parameters[0]; | |
| if (expression.empty()) { | |
| return expression; | |
| } | |
| return this->EvaluateExpression("GENEX_EVAL", expression, eval, content, | |
| dagCheckerParent); | |
| } | |
| } genexEvalNode; | |
| static const struct LowerCaseNode : public cmGeneratorExpressionNode | |
| { | |
| LowerCaseNode() {} // NOLINT(modernize-use-equals-default) | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*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( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cmSystemTools::UpperCase(parameters.front()); | |
| } | |
| } upperCaseNode; | |
| namespace { | |
| template <typename Container> | |
| class Range : public cmRange<typename Container::const_iterator> | |
| { | |
| private: | |
| using Base = cmRange<typename Container::const_iterator>; | |
| 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(Container const& 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<std::vector<std::string>>; | |
| bool CheckGenExParameters(cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* cnt, | |
| cm::string_view genex, cm::string_view option, | |
| std::size_t count, int required = 1, | |
| bool exactly = true) | |
| { | |
| if (static_cast<int>(count) < required || | |
| (exactly && static_cast<int>(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(required, " parameters"); | |
| } | |
| reportError(eval, cnt->GetOriginalExpression(), | |
| cmStrCat("$<", genex, ':', option, "> expression requires ", | |
| (exactly ? "exactly" : "at least"), ' ', nbParameters, | |
| '.')); | |
| return false; | |
| } | |
| return true; | |
| }; | |
| template <typename IndexType> | |
| bool GetNumericArgument(std::string const& arg, IndexType& value) | |
| { | |
| try { | |
| std::size_t pos; | |
| if (sizeof(IndexType) == 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 (std::invalid_argument const&) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| template <typename IndexType> | |
| bool GetNumericArguments( | |
| cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* cnt, | |
| Arguments args, std::vector<IndexType>& indexes, | |
| cmList::ExpandElements expandElements = cmList::ExpandElements::No) | |
| { | |
| using IndexRange = cmRange<Arguments::const_iterator>; | |
| 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) { | |
| IndexType index; | |
| if (!GetNumericArgument(value, index)) { | |
| reportError(eval, cnt->GetOriginalExpression(), | |
| cmStrCat("index: \"", value, "\" is not a valid index")); | |
| return false; | |
| } | |
| indexes.push_back(index); | |
| } | |
| return true; | |
| } | |
| // CMP0223: an empty path used to be a prefix of every path. | |
| bool IsPrefixCMP0223(cmCMakePath const& prefix, cm::GenEx::Evaluation* eval) | |
| { | |
| if (!prefix.IsEmpty()) { | |
| return false; | |
| } | |
| cmLocalGenerator const* const lg = eval->Context.LG; | |
| switch (lg->GetPolicyStatus(cmPolicies::CMP0223)) { | |
| case cmPolicies::WARN: | |
| lg->IssuePolicyWarning(cmPolicies::CMP0223, {}, {}, eval->Backtrace); | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| return true; | |
| case cmPolicies::NEW: | |
| break; | |
| } | |
| return false; | |
| } | |
| bool CheckPathParametersEx(cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* cnt, | |
| cm::string_view option, std::size_t count, | |
| int required = 1, bool exactly = true) | |
| { | |
| return CheckGenExParameters(eval, cnt, "PATH"_s, option, count, required, | |
| exactly); | |
| } | |
| bool CheckPathParameters(cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* cnt, | |
| cm::string_view option, Arguments args, | |
| int required = 1) | |
| { | |
| return CheckPathParametersEx(eval, 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| static auto processList = | |
| [](std::string const& arg, | |
| std::function<void(std::string&)> 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<std::string(cm::GenEx::Evaluation*, | |
| GeneratorExpressionContent const*, | |
| Arguments&)>> | |
| pathCommands{ | |
| { "GET_ROOT_NAME"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| bool lastOnly = args.front() == "LAST_ONLY"_s; | |
| if (lastOnly) { | |
| args.advance(1); | |
| } | |
| if (CheckPathParametersEx(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| bool lastOnly = args.front() == "LAST_ONLY"_s; | |
| if (lastOnly) { | |
| args.advance(1); | |
| } | |
| if (CheckPathParametersEx( | |
| ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| return CheckPathParameters(ev, cnt, "HAS_ROOT_NAME"_s, args) | |
| ? ToString(cmCMakePath{ args.front() }.HasRootName()) | |
| : std::string{ "0" }; | |
| } }, | |
| { "HAS_ROOT_DIRECTORY"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| return CheckPathParameters(ev, cnt, "HAS_ROOT_DIRECTORY"_s, args) | |
| ? ToString(cmCMakePath{ args.front() }.HasRootDirectory()) | |
| : std::string{ "0" }; | |
| } }, | |
| { "HAS_ROOT_PATH"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| return CheckPathParameters(ev, cnt, "HAS_ROOT_PATH"_s, args) | |
| ? ToString(cmCMakePath{ args.front() }.HasRootPath()) | |
| : std::string{ "0" }; | |
| } }, | |
| { "HAS_FILENAME"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| return CheckPathParameters(ev, cnt, "HAS_FILENAME"_s, args) | |
| ? ToString(cmCMakePath{ args.front() }.HasFileName()) | |
| : std::string{ "0" }; | |
| } }, | |
| { "HAS_EXTENSION"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| return CheckPathParameters(ev, cnt, "HAS_EXTENSION"_s, args) && | |
| !args.front().empty() | |
| ? ToString(cmCMakePath{ args.front() }.HasExtension()) | |
| : std::string{ "0" }; | |
| } }, | |
| { "HAS_STEM"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| return CheckPathParameters(ev, cnt, "HAS_STEM"_s, args) | |
| ? ToString(cmCMakePath{ args.front() }.HasStem()) | |
| : std::string{ "0" }; | |
| } }, | |
| { "HAS_RELATIVE_PART"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| return CheckPathParameters(ev, cnt, "HAS_RELATIVE_PART"_s, args) | |
| ? ToString(cmCMakePath{ args.front() }.HasRelativePath()) | |
| : std::string{ "0" }; | |
| } }, | |
| { "HAS_PARENT_PATH"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| return CheckPathParameters(ev, cnt, "HAS_PARENT_PATH"_s, args) | |
| ? ToString(cmCMakePath{ args.front() }.HasParentPath()) | |
| : std::string{ "0" }; | |
| } }, | |
| { "IS_ABSOLUTE"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| return CheckPathParameters(ev, cnt, "IS_ABSOLUTE"_s, args) | |
| ? ToString(cmCMakePath{ args.front() }.IsAbsolute()) | |
| : std::string{ "0" }; | |
| } }, | |
| { "IS_RELATIVE"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| return CheckPathParameters(ev, cnt, "IS_RELATIVE"_s, args) | |
| ? ToString(cmCMakePath{ args.front() }.IsRelative()) | |
| : std::string{ "0" }; | |
| } }, | |
| { "IS_PREFIX"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| bool normalize = args.front() == "NORMALIZE"_s; | |
| if (normalize) { | |
| args.advance(1); | |
| } | |
| if (CheckPathParametersEx( | |
| ev, cnt, normalize ? "IS_PREFIX,NORMALIZE"_s : "IS_PREFIX"_s, | |
| args.size(), 2)) { | |
| cmCMakePath prefix{ args[0] }; | |
| cmCMakePath value{ args[1] }; | |
| if (normalize) { | |
| prefix = prefix.Normal(); | |
| value = value.Normal(); | |
| } | |
| return ToString(prefix.IsPrefix(value) || | |
| IsPrefixCMP0223(prefix, ev)); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "CMAKE_PATH"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| bool normalize = args.front() == "NORMALIZE"_s; | |
| if (normalize) { | |
| args.advance(1); | |
| } | |
| if (CheckPathParametersEx(ev, 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{}; | |
| } }, | |
| { "NATIVE_PATH"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| bool normalize = args.front() == "NORMALIZE"_s; | |
| if (normalize) { | |
| args.advance(1); | |
| } | |
| if (CheckPathParametersEx(ev, cnt, | |
| normalize ? "NATIVE_PATH,NORMALIZE"_s | |
| : "NATIVE_PATH"_s, | |
| args.size(), 1)) { | |
| return processList( | |
| args.front(), [normalize](std::string& value) { | |
| auto path = cmCMakePath{ value }; | |
| value = normalize ? path.Normal().NativeString() | |
| : path.NativeString(); | |
| }); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "APPEND"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParametersEx(ev, 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 (auto const& p : args) { | |
| path /= p; | |
| } | |
| value = path.String(); | |
| }); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "REMOVE_FILENAME"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| bool lastOnly = args.front() == "LAST_ONLY"_s; | |
| if (lastOnly) { | |
| args.advance(1); | |
| } | |
| if (CheckPathParametersEx(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| bool lastOnly = args.front() == "LAST_ONLY"_s; | |
| if (lastOnly) { | |
| args.advance(1); | |
| } | |
| if (CheckPathParametersEx(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckPathParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| bool normalize = args.front() == "NORMALIZE"_s; | |
| if (normalize) { | |
| args.advance(1); | |
| } | |
| if (CheckPathParametersEx(ev, 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()](eval, content, args); | |
| } | |
| reportError(eval, 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( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cmCMakePath{ parameters[0] } == cmCMakePath{ parameters[1] } ? "1" | |
| : "0"; | |
| } | |
| } pathEqualNode; | |
| namespace { | |
| inline bool CheckStringParametersEx(cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* cnt, | |
| cm::string_view option, std::size_t count, | |
| int required = 1, bool exactly = true) | |
| { | |
| return CheckGenExParameters(eval, cnt, "STRING"_s, option, count, required, | |
| exactly); | |
| } | |
| inline bool CheckStringParameters(cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* cnt, | |
| cm::string_view option, Arguments args, | |
| int required = 1) | |
| { | |
| return CheckStringParametersEx(eval, cnt, option, args.size(), required); | |
| }; | |
| } | |
| static const struct StringNode : public cmGeneratorExpressionNode | |
| { | |
| StringNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return OneOrMoreParameters; } | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| static std::unordered_map< | |
| cm::string_view, | |
| std::function<std::string(cm::GenEx::Evaluation*, | |
| GeneratorExpressionContent const*, | |
| Arguments&)>> | |
| stringCommands{ | |
| { "LENGTH"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParameters(ev, cnt, "LENGTH"_s, args)) { | |
| return std::to_string(cm::CMakeString{ args.front() }.Length()); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "SUBSTRING"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParameters(ev, cnt, "SUBSTRING"_s, args, 3)) { | |
| cm::CMakeString str{ args.front() }; | |
| std::vector<long> indexes; | |
| if (GetNumericArguments(ev, cnt, args.advance(1), indexes)) { | |
| try { | |
| return str.Substring(indexes.front(), indexes.back()); | |
| } catch (std::out_of_range const& e) { | |
| reportError(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "FIND"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParametersEx(ev, cnt, "FIND"_s, args.size(), 2, | |
| false)) { | |
| if (args.size() > 3) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| "$<STRING:FIND> expression expects at " | |
| "most three parameters."); | |
| return std::string{}; | |
| } | |
| auto const FROM = "FROM:"_s; | |
| cm::CMakeString str{ args.front() }; | |
| cm::CMakeString::FindFrom from = | |
| cm::CMakeString::FindFrom::Begin; | |
| cm::string_view substring; | |
| args.advance(1); | |
| if (args.size() == 2) { | |
| cm::CMakeString::FindFrom opt = | |
| static_cast<cm::CMakeString::FindFrom>(-1); | |
| for (auto const& arg : args) { | |
| if (cmHasPrefix(arg, FROM)) { | |
| if (arg != "FROM:BEGIN"_s && arg != "FROM:END"_s) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("Invalid value for '", FROM, | |
| "' option. 'BEGIN' or 'END' expected.")); | |
| return std::string{}; | |
| } | |
| opt = arg == "FROM:BEGIN"_s | |
| ? cm::CMakeString::FindFrom::Begin | |
| : cm::CMakeString::FindFrom::End; | |
| } else { | |
| substring = arg; | |
| } | |
| } | |
| if (opt == static_cast<cm::CMakeString::FindFrom>(-1)) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("Expected option '", FROM, "' is missing.")); | |
| return std::string{}; | |
| } | |
| from = opt; | |
| } else { | |
| substring = args.front(); | |
| } | |
| auto pos = str.Find(substring, from); | |
| return pos == cm::CMakeString::npos ? "-1" : std::to_string(pos); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "MATCH"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParametersEx(ev, cnt, "MATCH"_s, args.size(), 2, | |
| false)) { | |
| if (args.size() > 3) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| "$<STRING:MATCH> expression expects at " | |
| "most three parameters."); | |
| return std::string{}; | |
| } | |
| auto const SEEK = "SEEK:"_s; | |
| cm::CMakeString str{ args.front() }; | |
| cm::CMakeString::MatchItems seek = | |
| cm::CMakeString::MatchItems::Once; | |
| auto const* regex = &args[1]; | |
| args.advance(1); | |
| if (args.size() == 2) { | |
| cm::CMakeString::MatchItems opt = | |
| static_cast<cm::CMakeString::MatchItems>(-1); | |
| for (auto const& arg : args) { | |
| if (cmHasPrefix(arg, SEEK)) { | |
| if (arg != "SEEK:ONCE"_s && arg != "SEEK:ALL"_s) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("Invalid value for '", SEEK, | |
| "' option. 'ONCE' or 'ALL' expected.")); | |
| return std::string{}; | |
| } | |
| opt = arg == "SEEK:ONCE"_s | |
| ? cm::CMakeString::MatchItems::Once | |
| : cm::CMakeString::MatchItems::All; | |
| } else { | |
| regex = &arg; | |
| } | |
| } | |
| if (opt == static_cast<cm::CMakeString::MatchItems>(-1)) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("Expected option '", SEEK, "' is missing.")); | |
| return std::string{}; | |
| } | |
| seek = opt; | |
| } | |
| try { | |
| return str.Match(*regex, seek).to_string(); | |
| } catch (std::invalid_argument const& e) { | |
| reportError(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "JOIN"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParametersEx(ev, cnt, "JOIN"_s, args.size(), 2, | |
| false)) { | |
| auto const& glue = args.front(); | |
| return cm::CMakeString{ args.advance(1), glue }; | |
| } | |
| return std::string{}; | |
| } }, | |
| { "ASCII"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParametersEx(ev, cnt, "ASCII"_s, args.size(), 1, | |
| false)) { | |
| try { | |
| return cm::CMakeString{}.FromASCII(args); | |
| } catch (std::invalid_argument const& e) { | |
| reportError(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "TIMESTAMP"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| cm::string_view format; | |
| cm::CMakeString::UTC utc = cm::CMakeString::UTC::No; | |
| if (args.size() == 2 && args.front() != "UTC"_s && | |
| args.back() != "UTC"_s) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| "'UTC' option is expected."); | |
| return std::string{}; | |
| } | |
| if (args.size() > 2) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| "$<STRING:TIMESTAMP> expression expects at most two " | |
| "parameters."); | |
| return std::string{}; | |
| } | |
| for (auto const& arg : args) { | |
| if (arg == "UTC"_s) { | |
| utc = cm::CMakeString::UTC::Yes; | |
| } else { | |
| format = arg; | |
| } | |
| } | |
| return cm::CMakeString{}.Timestamp(format, utc); | |
| } }, | |
| { "RANDOM"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| auto const ALPHABET = "ALPHABET:"_s; | |
| auto const LENGTH = "LENGTH:"_s; | |
| auto const RANDOM_SEED = "RANDOM_SEED:"_s; | |
| if (args.size() > 3) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| "$<STRING:RANDOM> expression expects at most three " | |
| "parameters."); | |
| return std::string{}; | |
| } | |
| cm::string_view alphabet; | |
| std::size_t length = 5; | |
| bool seed_specified = false; | |
| unsigned int seed = 0; | |
| for (auto const& arg : args) { | |
| if (cmHasPrefix(arg, ALPHABET)) { | |
| alphabet = cm::string_view{ arg.c_str() + ALPHABET.length() }; | |
| continue; | |
| } | |
| if (cmHasPrefix(arg, LENGTH)) { | |
| try { | |
| length = std::stoul(arg.substr(LENGTH.size())); | |
| } catch (std::exception const&) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat(arg, ": invalid numeric value for '", | |
| LENGTH, "' option.")); | |
| return std::string{}; | |
| } | |
| continue; | |
| } | |
| if (cmHasPrefix(arg, RANDOM_SEED)) { | |
| try { | |
| seed_specified = true; | |
| seed = static_cast<unsigned int>( | |
| std::stoul(arg.substr(RANDOM_SEED.size()))); | |
| } catch (std::exception const&) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat(arg, ": invalid numeric value for '", | |
| RANDOM_SEED, "' option.")); | |
| return std::string{}; | |
| } | |
| continue; | |
| } | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat(arg, ": invalid parameter.")); | |
| return std::string{}; | |
| } | |
| try { | |
| if (seed_specified) { | |
| return cm::CMakeString{}.Random(seed, length, alphabet); | |
| } | |
| return cm::CMakeString{}.Random(length, alphabet); | |
| } catch (std::exception const& e) { | |
| reportError(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } }, | |
| { "UUID"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParametersEx(ev, cnt, "UUID"_s, args.size(), 2, | |
| false)) { | |
| auto const NAMESPACE = "NAMESPACE:"_s; | |
| auto const NAME = "NAME:"_s; | |
| auto const TYPE = "TYPE:"_s; | |
| auto const CASE = "CASE:"_s; | |
| if (args.size() > 4) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| "$<STRING:UUID> expression expects at most four " | |
| "parameters."); | |
| return std::string{}; | |
| } | |
| cm::string_view nameSpace; | |
| cm::string_view name; | |
| cm::CMakeString::UUIDType type = | |
| static_cast<cm::CMakeString::UUIDType>(-1); | |
| cm::CMakeString::Case uuidCase = cm::CMakeString::Case::Lower; | |
| for (auto const& arg : args) { | |
| if (cmHasPrefix(arg, NAMESPACE)) { | |
| nameSpace = | |
| cm::string_view{ arg.c_str() + NAMESPACE.length() }; | |
| if (nameSpace.empty()) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("Invalid value for '", NAMESPACE, "' option.")); | |
| return std::string{}; | |
| } | |
| continue; | |
| } | |
| if (cmHasPrefix(arg, NAME)) { | |
| name = cm::string_view{ arg.c_str() + NAME.length() }; | |
| continue; | |
| } | |
| if (cmHasPrefix(arg, TYPE)) { | |
| auto value = cm::string_view{ arg.c_str() + TYPE.length() }; | |
| if (value != "MD5"_s && value != "SHA1"_s) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("Invalid value for '", TYPE, | |
| "' option. 'MD5' or 'SHA1' expected.")); | |
| return std::string{}; | |
| } | |
| type = value == "MD5"_s ? cm::CMakeString::UUIDType::MD5 | |
| : cm::CMakeString::UUIDType::SHA1; | |
| continue; | |
| } | |
| if (cmHasPrefix(arg, CASE)) { | |
| auto value = cm::string_view{ arg.c_str() + CASE.length() }; | |
| if (value != "UPPER"_s && value != "LOWER"_s) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("Invalid value for '", CASE, | |
| "' option. 'UPPER' or 'LOWER' expected.")); | |
| return std::string{}; | |
| } | |
| uuidCase = value == "UPPER"_s ? cm::CMakeString::Case::Upper | |
| : cm::CMakeString::Case::Lower; | |
| continue; | |
| } | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat(arg, ": invalid parameter.")); | |
| return std::string{}; | |
| } | |
| if (nameSpace.empty()) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("Required option '", NAMESPACE, "' is missing.")); | |
| return std::string{}; | |
| } | |
| if (type == static_cast<cm::CMakeString::UUIDType>(-1)) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("Required option '", TYPE, "' is missing.")); | |
| return std::string{}; | |
| } | |
| try { | |
| return cm::CMakeString{}.UUID(nameSpace, name, type, uuidCase); | |
| } catch (std::exception const& e) { | |
| reportError(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "REPLACE"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParametersEx(ev, cnt, "REPLACE"_s, args.size(), 3, | |
| false)) { | |
| if (args.size() > 4) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| "$<STRING:REPLACE> expression expects at " | |
| "most four parameters."); | |
| return std::string{}; | |
| } | |
| cm::CMakeString::Regex isRegex = cm::CMakeString::Regex::No; | |
| if (args.size() == 4) { | |
| cm::string_view type = args.front(); | |
| if (type != "STRING"_s && type != "REGEX"_s) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat( | |
| '\'', type, | |
| "' is unexpected. 'STRING' or 'REGEX' expected.")); | |
| return std::string{}; | |
| } | |
| isRegex = type == "STRING"_s ? cm::CMakeString::Regex::No | |
| : cm::CMakeString::Regex::Yes; | |
| args.advance(1); | |
| } | |
| try { | |
| return cm::CMakeString{ args.front() }.Replace( | |
| args[1], args[2], isRegex); | |
| } catch (std::invalid_argument const& e) { | |
| reportError(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "APPEND"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParametersEx(ev, cnt, "APPEND"_s, args.size(), 2, | |
| false)) { | |
| cm::CMakeString data{ args.front() }; | |
| return data.Append(args.advance(1)); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "PREPEND"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParametersEx(ev, cnt, "PREPEND "_s, args.size(), 2, | |
| false)) { | |
| cm::CMakeString data{ args.front() }; | |
| return data.Prepend(args.advance(1)); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "TOLOWER"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParameters(ev, cnt, "TOLOWER"_s, args, 1)) { | |
| return cm::CMakeString{}.ToLower(args.front()); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "TOUPPER"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParameters(ev, cnt, "TOUPPER"_s, args, 1)) { | |
| return cm::CMakeString{}.ToUpper(args.front()); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "STRIP"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParameters(ev, cnt, "STRIP"_s, args, 2)) { | |
| if (args.front() != "SPACES"_s) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat('\'', args.front(), | |
| "' is unexpected. 'SPACES' expected.")); | |
| return std::string{}; | |
| } | |
| return cm::CMakeString{ args[1] }.Strip(); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "QUOTE"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParameters(ev, cnt, "QUOTE"_s, args, 2)) { | |
| if (args.front() != "REGEX"_s) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat('\'', args.front(), | |
| "' is unexpected. 'REGEX' expected.")); | |
| return std::string{}; | |
| } | |
| return cm::CMakeString{ args[1] }.Quote(); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "HEX"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParameters(ev, cnt, "HEX"_s, args, 1)) { | |
| return cm::CMakeString{ args.front() }.ToHexadecimal(); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "HASH"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParameters(ev, cnt, "HASH"_s, args, 2)) { | |
| auto const ALGORITHM = "ALGORITHM:"_s; | |
| if (cmHasPrefix(args[1], ALGORITHM)) { | |
| try { | |
| auto const algo = | |
| cm::string_view{ args[1].c_str() + ALGORITHM.length() }; | |
| if (algo.empty()) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("Missing value for '", ALGORITHM, "' option.")); | |
| return std::string{}; | |
| } | |
| return cm::CMakeString{ args.front() }.Hash(algo); | |
| } catch (std::exception const& e) { | |
| reportError(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat(args[1], ": invalid parameter. Option '", | |
| ALGORITHM, "' expected.")); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "MAKE_C_IDENTIFIER"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckStringParameters(ev, cnt, "MAKE_C_IDENTIFIER"_s, args, | |
| 1)) { | |
| return cm::CMakeString{ args.front() }.MakeCIdentifier(); | |
| } | |
| return std::string{}; | |
| } } | |
| }; | |
| if (parameters.front().empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<STRING> expression requires at least one parameter."); | |
| return std::string{}; | |
| } | |
| if (cm::contains(stringCommands, parameters.front())) { | |
| auto args = Arguments{ parameters }.advance(1); | |
| return stringCommands[parameters.front()](eval, content, args); | |
| } | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat(parameters.front(), ": invalid option.")); | |
| return std::string{}; | |
| } | |
| } stringNode; | |
| namespace { | |
| inline bool CheckListParametersEx(cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* cnt, | |
| cm::string_view option, std::size_t count, | |
| int required = 1, bool exactly = true) | |
| { | |
| return CheckGenExParameters(eval, cnt, "LIST"_s, option, count, required, | |
| exactly); | |
| } | |
| inline bool CheckListParameters(cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* cnt, | |
| cm::string_view option, Arguments args, | |
| int required = 1) | |
| { | |
| return CheckListParametersEx(eval, cnt, option, args.size(), required); | |
| }; | |
| inline cmList GetList(std::string const& list) | |
| { | |
| return list.empty() ? cmList{} : cmList{ list, cmList::EmptyElements::Yes }; | |
| } | |
| struct TransformActionDescriptor | |
| { | |
| cmList::TransformAction Action; | |
| int Arity; | |
| }; | |
| // Look up a canned TRANSFORM action by name (APPLY is handled separately). | |
| cm::optional<TransformActionDescriptor> FindTransformActionDescriptor( | |
| std::string const& name) | |
| { | |
| static std::unordered_map<cm::string_view, TransformActionDescriptor> const | |
| descriptors{ | |
| { "APPEND"_s, { cmList::TransformAction::APPEND, 1 } }, | |
| { "PREPEND"_s, { cmList::TransformAction::PREPEND, 1 } }, | |
| { "TOUPPER"_s, { cmList::TransformAction::TOUPPER, 0 } }, | |
| { "TOLOWER"_s, { cmList::TransformAction::TOLOWER, 0 } }, | |
| { "STRIP"_s, { cmList::TransformAction::STRIP, 0 } }, | |
| { "REPLACE"_s, { cmList::TransformAction::REPLACE, 2 } }, | |
| }; | |
| auto it = descriptors.find(name); | |
| if (it == descriptors.end()) { | |
| return cm::nullopt; | |
| } | |
| return it->second; | |
| } | |
| // Index in `parameters` at which a TRANSFORM action's selector region begins, | |
| // or nullopt if this is not a TRANSFORM or the action is unknown. For the | |
| // APPLY action the <body> occupies slot 3, so the selector starts at slot 4. | |
| cm::optional<std::size_t> TransformSelectorStart( | |
| std::vector<std::string> const& parameters) | |
| { | |
| if (parameters.size() < 3 || parameters[0] != "TRANSFORM") { | |
| return cm::nullopt; | |
| } | |
| std::string const& action = parameters[2]; | |
| if (action == "APPLY") { | |
| return std::size_t{ 4 }; | |
| } | |
| if (auto d = FindTransformActionDescriptor(action)) { | |
| return std::size_t{ 3 } + static_cast<std::size_t>(d->Arity); | |
| } | |
| return cm::nullopt; | |
| } | |
| // Handle $<LIST:TRANSFORM,...,PREDICATE,<body>>. `predIndex` is the index of | |
| // the PREDICATE token in `parameters`; the <body> follows it. PREDICATE is | |
| // the sole selector: only elements whose predicate is "1" are transformed. | |
| std::string EvaluateTransformPredicate( | |
| std::vector<std::string> const& parameters, std::size_t predIndex, | |
| cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) | |
| { | |
| // PREDICATE must take exactly one <body> and not be combined with another | |
| // selector (AT/FOR/REGEX) or trailing tokens. | |
| if (parameters.size() < predIndex + 2) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command TRANSFORM, selector PREDICATE expects a <body> " | |
| "argument."); | |
| return std::string(); | |
| } | |
| if (parameters.size() > predIndex + 2) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command TRANSFORM, selector PREDICATE expects a single " | |
| "<body> argument and cannot be combined with another " | |
| "selector."); | |
| return std::string(); | |
| } | |
| cmList list = GetList(parameters[1]); | |
| if (list.empty()) { | |
| return std::string(); | |
| } | |
| cmGeneratorExpressionEvaluatorVector const& predicateBody = | |
| content->GetParamChildren()[predIndex + 1]; | |
| auto mask = EvaluatePredicateMask(predicateBody, list, "TRANSFORM"_s, eval, | |
| content, dagChecker); | |
| if (!mask) { | |
| return std::string(); | |
| } | |
| if (parameters[2] == "APPLY") { | |
| cmGeneratorExpressionEvaluatorVector const& applyBody = | |
| content->GetParamChildren()[3]; | |
| std::vector<std::string> out; | |
| out.reserve(list.size()); | |
| std::size_t i = 0; | |
| for (auto const& element : list) { | |
| if ((*mask)[i]) { | |
| out.push_back( | |
| EvaluateBodyWithBoundOperand(applyBody, element, eval, dagChecker)); | |
| if (eval->HadError) { | |
| return std::string(); | |
| } | |
| } else { | |
| out.push_back(element); | |
| } | |
| ++i; | |
| } | |
| return cmList{ out.begin(), out.end(), cmList::ExpandElements::No, | |
| cmList::EmptyElements::Yes } | |
| .to_string(); | |
| } | |
| std::string const& action = parameters[2]; | |
| auto descriptor = FindTransformActionDescriptor(action); | |
| if (!descriptor) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat(" sub-command TRANSFORM, ", action, " invalid action.")); | |
| return std::string(); | |
| } | |
| // Action arguments occupy parameters[3 .. predIndex); TransformSelectorStart | |
| // guarantees there are exactly descriptor->Arity of them. | |
| std::vector<std::string> arguments(parameters.begin() + 3, | |
| parameters.begin() + predIndex); | |
| std::vector<cmList::index_type> indices; | |
| for (std::size_t i = 0; i < mask->size(); ++i) { | |
| if ((*mask)[i]) { | |
| indices.push_back(static_cast<cmList::index_type>(i)); | |
| } | |
| } | |
| if (indices.empty()) { | |
| // No element selected: TRANSFORM is a no-op. | |
| return list.to_string(); | |
| } | |
| auto selector = | |
| cmList::TransformSelector::New<cmList::TransformSelector::AT>( | |
| std::move(indices)); | |
| selector->Makefile = eval->Context.LG->GetMakefile(); | |
| try { | |
| return list.transform(descriptor->Action, arguments, std::move(selector)) | |
| .to_string(); | |
| } catch (cmList::transform_error& e) { | |
| reportError(eval, content->GetOriginalExpression(), e.what()); | |
| return std::string(); | |
| } | |
| } | |
| enum class SortOptionResult | |
| { | |
| NotRecognized, // `arg` is not a SORT option keyword | |
| Parsed, // recognized and applied to `sortConfig` | |
| Error, // recognized but malformed or duplicate (already reported) | |
| }; | |
| // Parse one $<LIST:SORT> colon-option (COMPARE:/CASE:/ORDER:) into sortConfig. | |
| SortOptionResult ParseSortOption(std::string const& arg, | |
| cmList::SortConfiguration& sortConfig, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| using SortConfig = cmList::SortConfiguration; | |
| auto const COMPARE = "COMPARE:"_s; | |
| auto const CASE = "CASE:"_s; | |
| auto const ORDER = "ORDER:"_s; | |
| if (cmHasPrefix(arg, COMPARE)) { | |
| if (sortConfig.Compare != SortConfig::CompareMethod::DEFAULT) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command SORT, COMPARE option has been specified " | |
| "multiple times."); | |
| return SortOptionResult::Error; | |
| } | |
| auto option = cm::string_view{ arg.c_str() + COMPARE.length() }; | |
| if (option == "STRING"_s) { | |
| sortConfig.Compare = SortConfig::CompareMethod::STRING; | |
| } else if (option == "FILE_BASENAME"_s) { | |
| sortConfig.Compare = SortConfig::CompareMethod::FILE_BASENAME; | |
| } else if (option == "NATURAL"_s) { | |
| sortConfig.Compare = SortConfig::CompareMethod::NATURAL; | |
| } else { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("sub-command SORT, an invalid COMPARE option has " | |
| "been specified: \"", | |
| option, "\".")); | |
| return SortOptionResult::Error; | |
| } | |
| return SortOptionResult::Parsed; | |
| } | |
| if (cmHasPrefix(arg, CASE)) { | |
| if (sortConfig.Case != SortConfig::CaseSensitivity::DEFAULT) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command SORT, CASE option has been specified multiple " | |
| "times."); | |
| return SortOptionResult::Error; | |
| } | |
| auto option = cm::string_view{ arg.c_str() + CASE.length() }; | |
| if (option == "SENSITIVE"_s) { | |
| sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE; | |
| } else if (option == "INSENSITIVE"_s) { | |
| sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE; | |
| } else { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("sub-command SORT, an invalid CASE option has been " | |
| "specified: \"", | |
| option, "\".")); | |
| return SortOptionResult::Error; | |
| } | |
| return SortOptionResult::Parsed; | |
| } | |
| if (cmHasPrefix(arg, ORDER)) { | |
| if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command SORT, ORDER option has been specified multiple " | |
| "times."); | |
| return SortOptionResult::Error; | |
| } | |
| auto option = cm::string_view{ arg.c_str() + ORDER.length() }; | |
| if (option == "ASCENDING"_s) { | |
| sortConfig.Order = SortConfig::OrderMode::ASCENDING; | |
| } else if (option == "DESCENDING"_s) { | |
| sortConfig.Order = SortConfig::OrderMode::DESCENDING; | |
| } else { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("sub-command SORT, an invalid ORDER option has been " | |
| "specified: \"", | |
| option, "\".")); | |
| return SortOptionResult::Error; | |
| } | |
| return SortOptionResult::Parsed; | |
| } | |
| return SortOptionResult::NotRecognized; | |
| } | |
| // $<LIST:SORT,...,COMPARATOR,body>: sort with a per-comparison genex body, the | |
| // two elements bound to $<_0> and $<_1>; body must yield "0" or "1". | |
| std::string EvaluateSortComparator(std::vector<std::string> const& parameters, | |
| std::size_t comparatorIndex, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) | |
| { | |
| if (comparatorIndex + 1 >= parameters.size()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command SORT, COMPARATOR expects a <body> argument."); | |
| return std::string(); | |
| } | |
| cmGeneratorExpressionEvaluatorVector const& bodyExpr = | |
| content->GetParamChildren()[comparatorIndex + 1]; | |
| using SortConfig = cmList::SortConfiguration; | |
| SortConfig sortConfig; | |
| sortConfig.Compare = SortConfig::CompareMethod::COMPARATOR; | |
| for (std::size_t i = 2; i < parameters.size(); ++i) { | |
| if (i == comparatorIndex || i == comparatorIndex + 1) { | |
| continue; // COMPARATOR keyword + its (empty) body slot | |
| } | |
| std::string const& arg = parameters[i]; | |
| // COMPARATOR defines the ordering, so reject COMPARE:; CASE:/ORDER: are | |
| // accepted as in list(SORT ... COMPARATOR) (CASE: folds the body | |
| // operands). | |
| if (cmHasPrefix(arg, "COMPARE:"_s)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command SORT, option \"COMPARE\" is incompatible with " | |
| "\"COMPARATOR\"."); | |
| return std::string(); | |
| } | |
| switch (ParseSortOption(arg, sortConfig, eval, content)) { | |
| case SortOptionResult::Parsed: | |
| break; | |
| case SortOptionResult::Error: | |
| return std::string(); | |
| case SortOptionResult::NotRecognized: | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("sub-command SORT, option \"", arg, "\" is invalid.")); | |
| return std::string(); | |
| } | |
| } | |
| cmList list = GetList(parameters[1]); | |
| if (list.size() < 2) { | |
| return list.to_string(); | |
| } | |
| // The strict-weak-ordering guard in cmList::sort may call this twice per | |
| // pair, so the body can be evaluated up to twice per comparison. | |
| auto comparator = [&](std::string const& a, std::string const& b) -> bool { | |
| std::string r = | |
| EvaluateBodyWithBoundOperands(bodyExpr, { a, b }, eval, dagChecker); | |
| if (eval->HadError) { | |
| throw cmList::transform_error(std::string{}); // body already reported | |
| } | |
| if (r == "1") { | |
| return true; | |
| } | |
| if (r == "0") { | |
| return false; | |
| } | |
| throw cmList::transform_error( | |
| cmStrCat("sub-command SORT, COMPARATOR body must evaluate to \"0\" or " | |
| "\"1\", but evaluated to \"", | |
| r, "\".")); | |
| }; | |
| try { | |
| list.sort(sortConfig, comparator); | |
| } catch (std::invalid_argument& e) { | |
| if (!eval->HadError) { | |
| reportError(eval, content->GetOriginalExpression(), e.what()); | |
| } | |
| return std::string(); | |
| } | |
| return list.to_string(); | |
| } | |
| // Parse the optional trailing selector of a $<LIST:TRANSFORM,...> action | |
| // (AT <i>... / FOR <start> <stop> [<step>] / REGEX <re>) into a | |
| // cmList::TransformSelector. Returns nullptr (after reporting via `eval`) on | |
| // a malformed selector; empty `tokens` yields a select-all selector. | |
| std::unique_ptr<cmList::TransformSelector> ParseTransformSelector( | |
| std::vector<std::string> const& tokens, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| static std::string const REGEX{ "REGEX" }; | |
| static std::string const AT{ "AT" }; | |
| static std::string const FOR{ "FOR" }; | |
| std::unique_ptr<cmList::TransformSelector> selector; | |
| std::size_t i = 0; | |
| while (i < tokens.size()) { | |
| std::string const& tok = tokens[i]; | |
| if ((tok == REGEX || tok == AT || tok == FOR) && selector) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("sub-command TRANSFORM, selector already specified (", | |
| selector->GetTag(), ").")); | |
| return nullptr; | |
| } | |
| if (tok == REGEX) { | |
| if (i + 1 >= tokens.size()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command TRANSFORM, selector REGEX expects " | |
| "'regular expression' argument."); | |
| return nullptr; | |
| } | |
| selector = | |
| cmList::TransformSelector::New<cmList::TransformSelector::REGEX>( | |
| tokens[i + 1]); | |
| i += 2; | |
| continue; | |
| } | |
| if (tok == AT) { | |
| ++i; | |
| std::vector<cmList::index_type> indexes; | |
| for (; i < tokens.size(); ++i) { | |
| cmList indexList{ tokens[i] }; | |
| for (auto const& index : indexList) { | |
| cmList::index_type value; | |
| if (!GetNumericArgument(index, value)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("sub-command TRANSFORM, selector AT: '", | |
| index, "': unexpected argument.")); | |
| return nullptr; | |
| } | |
| indexes.push_back(value); | |
| } | |
| } | |
| if (indexes.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command TRANSFORM, selector AT expects at least one " | |
| "numeric value."); | |
| return nullptr; | |
| } | |
| selector = cmList::TransformSelector::New<cmList::TransformSelector::AT>( | |
| std::move(indexes)); | |
| continue; | |
| } | |
| if (tok == FOR) { | |
| if (i + 2 >= tokens.size()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command TRANSFORM, selector FOR expects, at least, " | |
| "two arguments."); | |
| return nullptr; | |
| } | |
| cmList::index_type start = 0; | |
| cmList::index_type stop = 0; | |
| cmList::index_type step = 1; | |
| if (!GetNumericArgument(tokens[i + 1], start) || | |
| !GetNumericArgument(tokens[i + 2], stop)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command TRANSFORM, selector FOR expects, at least, " | |
| "two numeric values."); | |
| return nullptr; | |
| } | |
| i += 3; | |
| if (i < tokens.size()) { | |
| if (!GetNumericArgument(tokens[i], step)) { | |
| step = -1; | |
| } | |
| ++i; | |
| } | |
| if (step <= 0) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command TRANSFORM, selector FOR expects positive " | |
| "numeric value for <step>."); | |
| return nullptr; | |
| } | |
| selector = | |
| cmList::TransformSelector::New<cmList::TransformSelector::FOR>( | |
| { start, stop, step }); | |
| continue; | |
| } | |
| std::vector<std::string> const rest(tokens.begin() + i, tokens.end()); | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("sub-command TRANSFORM, '", cmJoin(rest, ", "), | |
| "': unexpected argument(s).")); | |
| return nullptr; | |
| } | |
| if (!selector) { | |
| selector = cmList::TransformSelector::New(); | |
| } | |
| return selector; | |
| } | |
| } | |
| static const struct ListNode : public cmGeneratorExpressionNode | |
| { | |
| ListNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return TwoOrMoreParameters; } | |
| bool AcceptsArbitraryContentParameter() const override { return true; } | |
| bool ShouldEvaluateNextParameter(std::vector<std::string> const& parameters, | |
| std::string&) const override | |
| { | |
| // Leave the FILTER PREDICATE <body> (slot 4) unevaluated so $<_0> is never | |
| // evaluated unbound. | |
| if (parameters.size() == 4 && parameters[0] == "FILTER" && | |
| parameters[3] == "PREDICATE") { | |
| return false; | |
| } | |
| // Leave a TRANSFORM PREDICATE selector's <body> unevaluated. PREDICATE is | |
| // the selector keyword only when it sits exactly at the selector position | |
| // (not when it is a literal action argument such as APPEND PREDICATE). | |
| if (auto start = TransformSelectorStart(parameters)) { | |
| if (parameters.size() == *start + 1 && | |
| parameters.back() == "PREDICATE") { | |
| return false; | |
| } | |
| } | |
| // Leave the SORT COMPARATOR <body> unevaluated; a bare COMPARATOR token is | |
| // unambiguous since SORT's other options are colon-style. | |
| if (parameters.size() >= 3 && parameters[0] == "SORT" && | |
| parameters.back() == "COMPARATOR") { | |
| return false; | |
| } | |
| // Skip the APPLY <body> (4th parameter) so $<_0> is not evaluated unbound; | |
| // selector args (5th+) evaluate normally. | |
| return !(parameters.size() == 3 && parameters[0] == "TRANSFORM" && | |
| parameters[2] == "APPLY"); | |
| } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| // TRANSFORM ... PREDICATE <body>: genex-native predicate selector, usable | |
| // with any action (canned or APPLY). Handled here (not in the | |
| // listCommands lambda) because the predicate <body> needs the DAG checker. | |
| if (parameters.size() >= 3 && parameters[0] == "TRANSFORM") { | |
| if (auto start = TransformSelectorStart(parameters)) { | |
| if (*start < parameters.size() && parameters[*start] == "PREDICATE") { | |
| return EvaluateTransformPredicate(parameters, *start, eval, content, | |
| dagChecker); | |
| } | |
| } | |
| } | |
| if (parameters.size() >= 3 && parameters[0] == "TRANSFORM" && | |
| parameters[2] == "APPLY") { | |
| if (parameters.size() < 4) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "sub-command TRANSFORM, action APPLY expects a <body> argument."); | |
| return std::string(); | |
| } | |
| cmList list = GetList(parameters[1]); | |
| if (list.empty()) { | |
| return std::string{}; | |
| } | |
| cmGeneratorExpressionEvaluatorVector const& bodyExpr = | |
| content->GetParamChildren()[3]; | |
| std::vector<std::string> const selectorTokens(parameters.begin() + 4, | |
| parameters.end()); | |
| std::unique_ptr<cmList::TransformSelector> selector = | |
| ParseTransformSelector(selectorTokens, eval, content); | |
| if (!selector) { | |
| return std::string(); | |
| } | |
| // selector->Makefile is left unset: the REGEX/AT/FOR selectors never | |
| // consult it, and the only one that does (list()'s PREDICATE) cannot | |
| // reach here. | |
| std::vector<bool> selected; | |
| try { | |
| selected = list.GetTransformSelection(*selector); | |
| } catch (cmList::transform_error& e) { | |
| reportError(eval, content->GetOriginalExpression(), e.what()); | |
| return std::string(); | |
| } | |
| std::vector<std::string> out; | |
| out.reserve(list.size()); | |
| std::size_t i = 0; | |
| for (auto const& element : list) { | |
| if (i < selected.size() && selected[i]) { | |
| out.push_back( | |
| EvaluateBodyWithBoundOperand(bodyExpr, element, eval, dagChecker)); | |
| if (eval->HadError) { | |
| return std::string(); | |
| } | |
| } else { | |
| out.push_back(element); | |
| } | |
| ++i; | |
| } | |
| // Join per-element results with ';'; keep empty elements so a body that | |
| // yields "" is not dropped. | |
| return cmList{ out.begin(), out.end(), cmList::ExpandElements::No, | |
| cmList::EmptyElements::Yes } | |
| .to_string(); | |
| } | |
| // FILTER ... PREDICATE <body>: genex-native predicate filter. | |
| if (parameters.size() >= 4 && parameters[0] == "FILTER" && | |
| parameters[3] == "PREDICATE") { | |
| if (parameters.size() != 5) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "sub-command FILTER, PREDICATE expects a single <body> " | |
| "argument."); | |
| return std::string(); | |
| } | |
| std::string const& op = parameters[2]; | |
| if (op != "INCLUDE" && op != "EXCLUDE") { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("sub-command FILTER does not recognize operator \"", op, | |
| "\". It must be either INCLUDE or EXCLUDE.")); | |
| return std::string(); | |
| } | |
| cmList list = GetList(parameters[1]); | |
| if (list.empty()) { | |
| return std::string(); | |
| } | |
| cmGeneratorExpressionEvaluatorVector const& predicateBody = | |
| content->GetParamChildren()[4]; | |
| auto mask = EvaluatePredicateMask(predicateBody, list, "FILTER"_s, eval, | |
| content, dagChecker); | |
| if (!mask) { | |
| return std::string(); | |
| } | |
| bool const keepWhenTrue = (op == "INCLUDE"); | |
| std::vector<std::string> out; | |
| std::size_t i = 0; | |
| for (auto const& element : list) { | |
| if ((*mask)[i] == keepWhenTrue) { | |
| out.push_back(element); | |
| } | |
| ++i; | |
| } | |
| return cmList{ out.begin(), out.end(), cmList::ExpandElements::No, | |
| cmList::EmptyElements::Yes } | |
| .to_string(); | |
| } | |
| // SORT COMPARATOR is handled here, not the listCommands SORT lambda, | |
| // because the body needs the DAG checker. | |
| if (parameters.size() >= 3 && parameters[0] == "SORT") { | |
| for (std::size_t i = 2; i < parameters.size(); ++i) { | |
| if (parameters[i] == "COMPARATOR") { | |
| return EvaluateSortComparator(parameters, i, eval, content, | |
| dagChecker); | |
| } | |
| } | |
| } | |
| static std::unordered_map< | |
| cm::string_view, | |
| std::function<std::string(cm::GenEx::Evaluation*, | |
| GeneratorExpressionContent const*, | |
| Arguments&)>> | |
| listCommands{ | |
| { "LENGTH"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParameters(ev, cnt, "LENGTH"_s, args)) { | |
| return std::to_string(GetList(args.front()).size()); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "GET"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParametersEx(ev, cnt, "GET"_s, args.size(), 2, | |
| false)) { | |
| auto list = GetList(args.front()); | |
| if (list.empty()) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| "given empty list"); | |
| return std::string{}; | |
| } | |
| std::vector<cmList::index_type> indexes; | |
| if (!GetNumericArguments(ev, 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(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "JOIN"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParameters(ev, cnt, "JOIN"_s, args, 2)) { | |
| return GetList(args.front()).join(args[1]); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "SUBLIST"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParameters(ev, cnt, "SUBLIST"_s, args, 3)) { | |
| auto list = GetList(args.front()); | |
| if (!list.empty()) { | |
| std::vector<cmList::index_type> indexes; | |
| if (!GetNumericArguments(ev, cnt, args.advance(1), indexes)) { | |
| return std::string{}; | |
| } | |
| if (indexes[0] < 0) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat("begin index: ", indexes[0], | |
| " is out of range 0 - ", | |
| list.size() - 1)); | |
| return std::string{}; | |
| } | |
| if (indexes[1] < -1) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat("length: ", indexes[1], | |
| " should be -1 or greater")); | |
| return std::string{}; | |
| } | |
| try { | |
| return list | |
| .sublist(static_cast<cmList::size_type>(indexes[0]), | |
| static_cast<cmList::size_type>(indexes[1])) | |
| .to_string(); | |
| } catch (std::out_of_range& e) { | |
| reportError(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "FIND"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParametersEx(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParametersEx(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParametersEx(ev, cnt, "INSERT"_s, args.size(), 3, | |
| false)) { | |
| cmList::index_type index; | |
| if (!GetNumericArgument(args[1], index)) { | |
| reportError( | |
| ev, 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(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "POP_BACK"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParameters(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParameters(ev, cnt, "REMOVE_DUPLICATES"_s, args)) { | |
| return GetList(args.front()).remove_duplicates().to_string(); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "REMOVE_ITEM"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParametersEx(ev, 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, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParametersEx(ev, cnt, "REMOVE_AT"_s, args.size(), 2, | |
| false)) { | |
| auto list = GetList(args.front()); | |
| std::vector<cmList::index_type> indexes; | |
| if (!GetNumericArguments(ev, 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(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "FILTER"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| // args = [list, INCLUDE|EXCLUDE, <regex> | REGEX <regex>]. | |
| // (PREDICATE is handled up-front in Evaluate and never reaches | |
| // here.) | |
| bool const explicitRegex = | |
| args.size() >= 3 && args[2] == "REGEX"_s; | |
| int const required = explicitRegex ? 4 : 3; | |
| if (CheckListParameters(ev, cnt, "FILTER"_s, args, required)) { | |
| auto const& op = args[1]; | |
| if (op != "INCLUDE"_s && op != "EXCLUDE"_s) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("sub-command FILTER does not recognize operator \"", | |
| op, "\". It must be either INCLUDE or EXCLUDE.")); | |
| return std::string{}; | |
| } | |
| auto const& regex = explicitRegex ? args[3] : args[2]; | |
| try { | |
| return GetList(args.front()) | |
| .filter(regex, | |
| op == "INCLUDE"_s ? cmList::FilterMode::INCLUDE | |
| : cmList::FilterMode::EXCLUDE) | |
| .to_string(); | |
| } catch (std::invalid_argument&) { | |
| reportError( | |
| ev, cnt->GetOriginalExpression(), | |
| cmStrCat("sub-command FILTER, failed to compile regex \"", | |
| regex, "\".")); | |
| return std::string{}; | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "TRANSFORM"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParametersEx(ev, cnt, "TRANSFORM"_s, args.size(), 2, | |
| false)) { | |
| auto list = GetList(args.front()); | |
| if (!list.empty()) { | |
| std::string const actionName = args.advance(1).front(); | |
| auto descriptor = FindTransformActionDescriptor(actionName); | |
| if (!descriptor) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat(" sub-command TRANSFORM, ", actionName, | |
| " invalid action.")); | |
| return std::string{}; | |
| } | |
| // Action arguments | |
| args.advance(1); | |
| if (args.size() < descriptor->Arity) { | |
| reportError(ev, cnt->GetOriginalExpression(), | |
| cmStrCat("sub-command TRANSFORM, action ", | |
| actionName, " expects ", | |
| descriptor->Arity, " argument(s).")); | |
| return std::string{}; | |
| } | |
| std::vector<std::string> arguments; | |
| if (descriptor->Arity > 0) { | |
| arguments = std::vector<std::string>( | |
| args.begin(), args.begin() + descriptor->Arity); | |
| args.advance(descriptor->Arity); | |
| } | |
| std::unique_ptr<cmList::TransformSelector> selector; | |
| try { | |
| std::vector<std::string> const tokens(args.begin(), | |
| args.end()); | |
| selector = ParseTransformSelector(tokens, ev, cnt); | |
| if (!selector) { | |
| return std::string{}; | |
| } | |
| selector->Makefile = ev->Context.LG->GetMakefile(); | |
| return list | |
| .transform(descriptor->Action, arguments, | |
| std::move(selector)) | |
| .to_string(); | |
| } catch (cmList::transform_error& e) { | |
| reportError(ev, cnt->GetOriginalExpression(), e.what()); | |
| return std::string{}; | |
| } | |
| } | |
| } | |
| return std::string{}; | |
| } }, | |
| { "REVERSE"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParameters(ev, cnt, "REVERSE"_s, args)) { | |
| return GetList(args.front()).reverse().to_string(); | |
| } | |
| return std::string{}; | |
| } }, | |
| { "SORT"_s, | |
| [](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt, | |
| Arguments& args) -> std::string { | |
| if (CheckListParametersEx(ev, cnt, "SORT"_s, args.size(), 1, | |
| false)) { | |
| auto list = GetList(args.front()); | |
| args.advance(1); | |
| cmList::SortConfiguration sortConfig; | |
| for (auto const& arg : args) { | |
| switch (ParseSortOption(arg, sortConfig, ev, cnt)) { | |
| case SortOptionResult::Parsed: | |
| break; | |
| case SortOptionResult::Error: | |
| return std::string{}; | |
| case SortOptionResult::NotRecognized: | |
| reportError(ev, 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()](eval, content, args); | |
| } | |
| reportError(eval, 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( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cmSystemTools::MakeCidentifier(parameters.front()); | |
| } | |
| } makeCIdentifierNode; | |
| template <char C> | |
| struct CharacterNode : public cmGeneratorExpressionNode | |
| { | |
| CharacterNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 0; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& /*parameters*/, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return { C }; | |
| } | |
| }; | |
| static CharacterNode<'>'> const angle_rNode; | |
| static CharacterNode<','> const commaNode; | |
| static CharacterNode<';'> const semicolonNode; | |
| static CharacterNode<'"'> const quoteNode; | |
| struct CompilerIdNode : public cmGeneratorExpressionNode | |
| { | |
| CompilerIdNode(char const* compilerLang) | |
| : CompilerLanguage(compilerLang) | |
| { | |
| } | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!eval->HeadTarget) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("$<", this->CompilerLanguage, | |
| "_COMPILER_ID> may only be used with binary targets. It may " | |
| "not be used with add_custom_command or add_custom_target.")); | |
| return {}; | |
| } | |
| return this->EvaluateWithLanguage(parameters, eval, content, dagChecker, | |
| this->CompilerLanguage); | |
| } | |
| std::string EvaluateWithLanguage(std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*unused*/, | |
| std::string const& lang) const | |
| { | |
| std::string const& compilerId = | |
| eval->Context.LG->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("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(eval, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return std::string(); | |
| } | |
| if (strcmp(param.c_str(), compilerId.c_str()) == 0) { | |
| return "1"; | |
| } | |
| } | |
| return "0"; | |
| } | |
| char const* const CompilerLanguage; | |
| }; | |
| static CompilerIdNode const cCompilerIdNode("C"), cxxCompilerIdNode("CXX"), | |
| cudaCompilerIdNode("CUDA"), objcCompilerIdNode("OBJC"), | |
| objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran"), | |
| hipCompilerIdNode("HIP"), ispcCompilerIdNode("ISPC"), | |
| syclCompilerIdNode("SYCL"); | |
| struct CompilerVersionNode : public cmGeneratorExpressionNode | |
| { | |
| CompilerVersionNode(char const* compilerLang) | |
| : CompilerLanguage(compilerLang) | |
| { | |
| } | |
| int NumExpectedParameters() const override { return OneOrZeroParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!eval->HeadTarget) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("$<", this->CompilerLanguage, | |
| "_COMPILER_VERSION> may only be used with binary " | |
| "targets. It may not be used with " | |
| "add_custom_command or add_custom_target.")); | |
| return {}; | |
| } | |
| return this->EvaluateWithLanguage(parameters, eval, content, dagChecker, | |
| this->CompilerLanguage); | |
| } | |
| std::string EvaluateWithLanguage(std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*unused*/, | |
| std::string const& lang) const | |
| { | |
| std::string const& compilerVersion = | |
| eval->Context.LG->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILER_VERSION")); | |
| if (parameters.empty()) { | |
| return compilerVersion; | |
| } | |
| static cmsys::RegularExpression compilerIdValidator("^[0-9\\.]*$"); | |
| if (!compilerIdValidator.find(parameters.front())) { | |
| reportError(eval, 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"; | |
| } | |
| char const* const CompilerLanguage; | |
| }; | |
| static CompilerVersionNode const cCompilerVersionNode("C"), | |
| cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"), | |
| objcCompilerVersionNode("OBJC"), objcxxCompilerVersionNode("OBJCXX"), | |
| fortranCompilerVersionNode("Fortran"), ispcCompilerVersionNode("ISPC"), | |
| hipCompilerVersionNode("HIP"), syclCompilerVersionNode("SYCL"); | |
| struct CompilerFrontendVariantNode : public cmGeneratorExpressionNode | |
| { | |
| CompilerFrontendVariantNode(char const* compilerLang) | |
| : CompilerLanguage(compilerLang) | |
| { | |
| } | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!eval->HeadTarget) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("$<", 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.")); | |
| return {}; | |
| } | |
| return this->EvaluateWithLanguage(parameters, eval, content, dagChecker, | |
| this->CompilerLanguage); | |
| } | |
| std::string EvaluateWithLanguage(std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*unused*/, | |
| std::string const& lang) const | |
| { | |
| std::string const& compilerFrontendVariant = | |
| eval->Context.LG->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("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(eval, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return {}; | |
| } | |
| if (strcmp(param.c_str(), compilerFrontendVariant.c_str()) == 0) { | |
| return "1"; | |
| } | |
| } | |
| return "0"; | |
| } | |
| char const* const CompilerLanguage; | |
| }; | |
| static CompilerFrontendVariantNode const cCompilerFrontendVariantNode("C"), | |
| cxxCompilerFrontendVariantNode("CXX"), | |
| cudaCompilerFrontendVariantNode("CUDA"), | |
| objcCompilerFrontendVariantNode("OBJC"), | |
| objcxxCompilerFrontendVariantNode("OBJCXX"), | |
| fortranCompilerFrontendVariantNode("Fortran"), | |
| hipCompilerFrontendVariantNode("HIP"), | |
| ispcCompilerFrontendVariantNode("ISPC"), | |
| syclCompilerFrontendVariantNode("SYCL"); | |
| struct PlatformIdNode : public cmGeneratorExpressionNode | |
| { | |
| PlatformIdNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| std::string const& platformId = | |
| eval->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 <cmSystemTools::CompareOp Op> | |
| struct VersionNode : public cmGeneratorExpressionNode | |
| { | |
| VersionNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| return cmSystemTools::VersionCompare(Op, parameters.front(), parameters[1]) | |
| ? "1" | |
| : "0"; | |
| } | |
| }; | |
| static VersionNode<cmSystemTools::OP_GREATER> const versionGreaterNode; | |
| static VersionNode<cmSystemTools::OP_GREATER_EQUAL> const versionGreaterEqNode; | |
| static VersionNode<cmSystemTools::OP_LESS> const versionLessNode; | |
| static VersionNode<cmSystemTools::OP_LESS_EQUAL> const versionLessEqNode; | |
| static VersionNode<cmSystemTools::OP_EQUAL> const versionEqualNode; | |
| static const struct CompileOnlyNode : public cmGeneratorExpressionNode | |
| { | |
| CompileOnlyNode() {} // NOLINT(modernize-use-equals-default) | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!dagChecker) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<COMPILE_ONLY:...> 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!dagChecker) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<LINK_ONLY:...> 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( | |
| std::vector<std::string> const& /*parameters*/, | |
| cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* /*content*/, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| eval->HadContextSensitiveCondition = true; | |
| return eval->Context.Config; | |
| } | |
| } configurationNode; | |
| static const struct ConfigurationTestNode : public cmGeneratorExpressionNode | |
| { | |
| ConfigurationTestNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (parameters.empty()) { | |
| return configurationNode.Evaluate(parameters, eval, content, nullptr); | |
| } | |
| eval->HadContextSensitiveCondition = true; | |
| // First, validate our arguments. | |
| static cmsys::RegularExpression configValidator("^[A-Za-z0-9_]*$"); | |
| bool firstParam = true; | |
| for (auto const& param : parameters) { | |
| if (!configValidator.find(param)) { | |
| if (firstParam) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return std::string(); | |
| } | |
| // for backwards compat invalid config names are only errors as | |
| // the first parameter | |
| eval->Context.LG->GetCMakeInstance()->IssueMessage( | |
| MessageType::WARNING, | |
| cmStrCat("Warning evaluating generator expression:\n" | |
| " ", | |
| content->GetOriginalExpression(), | |
| "\n" | |
| "The config name of \"", | |
| param, "\" is invalid"), | |
| eval->Backtrace); | |
| } | |
| firstParam = false; | |
| } | |
| // Partially determine the context(s) in which the expression should be | |
| // evaluated. | |
| // | |
| // If CMPxxxx is NEW, the context is exactly one of the imported target's | |
| // selected configuration, if applicable and if the target was imported | |
| // from CPS, or the consuming target's configuration otherwise. Here, we | |
| // determine if we are in that 'otherwise' branch. | |
| // | |
| // Longer term, we need a way for non-CPS users to match the selected | |
| // configuration of the imported target. At that time, CPS should switch | |
| // to that mechanism and the CPS-specific logic here should be dropped. | |
| // (We can do that because CPS doesn't use generator expressions directly; | |
| // rather, CMake generates them on import.) | |
| bool const targetIsImported = | |
| (eval->CurrentTarget && eval->CurrentTarget->IsImported()); | |
| bool const useConsumerConfig = | |
| (targetIsImported && | |
| eval->CurrentTarget->Target->GetOrigin() != cmTarget::Origin::Cps); | |
| if (!targetIsImported || useConsumerConfig) { | |
| // Does the consuming target's configuration match any of the arguments? | |
| for (auto const& param : parameters) { | |
| if (eval->Context.Config.empty()) { | |
| if (param.empty()) { | |
| return "1"; | |
| } | |
| } else if (cmsysString_strcasecmp(param.c_str(), | |
| eval->Context.Config.c_str()) == 0) { | |
| return "1"; | |
| } | |
| } | |
| } | |
| if (targetIsImported) { | |
| cmValue loc = nullptr; | |
| cmValue imp = nullptr; | |
| std::string suffix; | |
| if (eval->CurrentTarget->Target->GetMappedConfig(eval->Context.Config, | |
| loc, imp, suffix)) { | |
| // Finish determine the context(s) in which the expression should be | |
| // evaluated. Note that we use the consumer's policy, so that end users | |
| // can override the imported target's policy. This may be needed if | |
| // upstream has changed their policy version without realizing that | |
| // consumers were depending on the OLD behavior. | |
| bool const oldPolicy = [&] { | |
| if (!useConsumerConfig) { | |
| // Targets imported from CPS shall use only the selected | |
| // configuration of the imported target. | |
| return false; | |
| } | |
| cmLocalGenerator const* const lg = eval->Context.LG; | |
| switch (eval->HeadTarget->GetPolicyStatusCMP0199()) { | |
| case cmPolicies::WARN: | |
| if (lg->GetMakefile()->PolicyOptionalWarningEnabled( | |
| "CMAKE_POLICY_WARNING_CMP0199")) { | |
| lg->IssuePolicyWarning( | |
| cmPolicies::CMP0199, {}, | |
| cmStrCat("Evaluation of $<CONFIG> for imported target \""_s, | |
| eval->CurrentTarget->GetName(), "\", used by \""_s, | |
| eval->HeadTarget->GetName(), | |
| "\", may match multiple configurations."_s), | |
| eval->Backtrace); | |
| } | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| return true; | |
| case cmPolicies::NEW: | |
| return false; | |
| } | |
| CM_UNREACHABLE; | |
| return false; | |
| }(); | |
| if (oldPolicy) { | |
| // If CMPxxxx is OLD (and we aren't dealing with a target imported | |
| // form CPS), we already evaluated in the context of the consuming | |
| // target. Next, for imported targets, we will evaluate based on the | |
| // mapped configurations. | |
| // | |
| // If the target has a MAP_IMPORTED_CONFIG_<CONFIG> property for the | |
| // consumer's <CONFIG>, we will match *any* config in that list, | |
| // regardless of whether it's valid or of what GetMappedConfig | |
| // actually picked. This will result in $<CONFIG> producing '1' for | |
| // multiple configs, and is almost certainly wrong, but it's what | |
| // CMake did for a very long time, and... Hyrum's Law. | |
| cmList mappedConfigs; | |
| std::string mapProp = | |
| cmStrCat("MAP_IMPORTED_CONFIG_", | |
| cmSystemTools::UpperCase(eval->Context.Config)); | |
| if (cmValue mapValue = eval->CurrentTarget->GetProperty(mapProp)) { | |
| mappedConfigs.assign(cmSystemTools::UpperCase(*mapValue)); | |
| for (auto const& param : parameters) { | |
| if (cm::contains(mappedConfigs, | |
| cmSystemTools::UpperCase(param))) { | |
| return "1"; | |
| } | |
| } | |
| return "0"; | |
| } | |
| } | |
| // Finally, check if we selected (possibly via mapping) a configuration | |
| // for this imported target, and if we should evaluate the expression | |
| // in the context of the same. | |
| // | |
| // For targets imported from CPS, this is the only context we evaluate | |
| // the expression. | |
| if (!suffix.empty()) { | |
| 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( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (eval->Context.Language.empty() && | |
| (!dagChecker || !dagChecker->EvaluatingCompileExpression())) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<COMPILE_LANGUAGE:...> may only be used to specify include " | |
| "directories, compile definitions, compile options, compile wrappers, " | |
| "and to evaluate components of the file(GENERATE) command."); | |
| return std::string(); | |
| } | |
| cmGlobalGenerator const* gg = eval->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("FASTBuild") == std::string::npos && | |
| genName.find("Green Hills MULTI") == std::string::npos) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<COMPILE_LANGUAGE:...> not supported for this generator."); | |
| return std::string(); | |
| } | |
| if (parameters.empty()) { | |
| return eval->Context.Language; | |
| } | |
| for (auto const& param : parameters) { | |
| if (eval->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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!eval->HeadTarget || | |
| (eval->Context.Language.empty() && | |
| (!dagChecker || !dagChecker->EvaluatingCompileExpression()))) { | |
| // reportError(eval, content->GetOriginalExpression(), ""); | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<COMPILE_LANG_AND_ID:lang,id> may only be used with binary " | |
| "targets " | |
| "to specify include directories, compile definitions, compile " | |
| "options, and compile wrappers. It may not be used with the " | |
| "add_custom_command, add_custom_target, or file(GENERATE) commands."); | |
| return std::string(); | |
| } | |
| cmGlobalGenerator const* gg = eval->Context.LG->GetGlobalGenerator(); | |
| std::string genName = gg->GetName(); | |
| if (genName.find("Makefiles") == std::string::npos && | |
| genName.find("Ninja") == std::string::npos && | |
| genName.find("FASTBuild") == 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( | |
| eval, content->GetOriginalExpression(), | |
| "$<COMPILE_LANG_AND_ID:lang,id> not supported for this generator."); | |
| return std::string(); | |
| } | |
| std::string const& lang = eval->Context.Language; | |
| if (lang == parameters.front()) { | |
| std::vector<std::string> idParameter((parameters.cbegin() + 1), | |
| parameters.cend()); | |
| return CompilerIdNode{ lang.c_str() }.EvaluateWithLanguage( | |
| idParameter, eval, 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!eval->HeadTarget || !dagChecker || | |
| !(dagChecker->EvaluatingLinkExpression() || | |
| dagChecker->EvaluatingLinkLibraries() || | |
| dagChecker->EvaluatingLinkerLauncher())) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<LINK_LANGUAGE:...> may only be used with binary targets " | |
| "to specify link libraries, link directories, link options, " | |
| "link depends, and link wrappers."); | |
| return std::string(); | |
| } | |
| if (dagChecker->EvaluatingLinkLibraries() && parameters.empty()) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<LINK_LANGUAGE> is not supported in link libraries expression."); | |
| return std::string(); | |
| } | |
| cmGlobalGenerator const* gg = eval->Context.LG->GetGlobalGenerator(); | |
| std::string genName = gg->GetName(); | |
| if (genName.find("Makefiles") == std::string::npos && | |
| genName.find("Ninja") == std::string::npos && | |
| genName.find("FASTBuild") == 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(eval, content->GetOriginalExpression(), | |
| "$<LINK_LANGUAGE:...> not supported for this generator."); | |
| return std::string(); | |
| } | |
| if (dagChecker->EvaluatingLinkLibraries()) { | |
| eval->HadHeadSensitiveCondition = true; | |
| eval->HadLinkLanguageSensitiveCondition = true; | |
| } | |
| if (parameters.empty()) { | |
| return eval->Context.Language; | |
| } | |
| for (auto const& param : parameters) { | |
| if (eval->Context.Language == param) { | |
| return "1"; | |
| } | |
| } | |
| return "0"; | |
| } | |
| } linkLanguageNode; | |
| namespace { | |
| struct LinkerId | |
| { | |
| static std::string Evaluate(std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| std::string const& lang) | |
| { | |
| std::string const& linkerId = | |
| eval->Context.LG->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("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(eval, 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!eval->HeadTarget || !dagChecker || | |
| !(dagChecker->EvaluatingLinkExpression() || | |
| dagChecker->EvaluatingLinkLibraries() || | |
| dagChecker->EvaluatingLinkerLauncher())) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<LINK_LANG_AND_ID:lang,id> may only be used with binary targets " | |
| "to specify link libraries, link directories, link options, link " | |
| "depends, and link wrappers."); | |
| return std::string(); | |
| } | |
| cmGlobalGenerator const* gg = eval->Context.LG->GetGlobalGenerator(); | |
| std::string genName = gg->GetName(); | |
| if (genName.find("Makefiles") == std::string::npos && | |
| genName.find("Ninja") == std::string::npos && | |
| genName.find("FASTBuild") == 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( | |
| eval, content->GetOriginalExpression(), | |
| "$<LINK_LANG_AND_ID:lang,id> not supported for this generator."); | |
| return std::string(); | |
| } | |
| if (dagChecker->EvaluatingLinkLibraries()) { | |
| eval->HadHeadSensitiveCondition = true; | |
| eval->HadLinkLanguageSensitiveCondition = true; | |
| } | |
| std::string const& lang = eval->Context.Language; | |
| if (lang == parameters.front()) { | |
| std::vector<std::string> idParameter((parameters.cbegin() + 1), | |
| parameters.cend()); | |
| return LinkerId::Evaluate(idParameter, eval, content, lang); | |
| } | |
| return "0"; | |
| } | |
| } linkLanguageAndIdNode; | |
| struct CompilerLinkerIdNode : public cmGeneratorExpressionNode | |
| { | |
| CompilerLinkerIdNode(char const* lang) | |
| : Language(lang) | |
| { | |
| } | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!eval->HeadTarget) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat( | |
| "$<", this->Language, | |
| "_COMPILER_LINKER_ID> may only be used with binary targets. It may " | |
| "not be used with add_custom_command or add_custom_target.")); | |
| return {}; | |
| } | |
| return this->EvaluateWithLanguage(parameters, eval, content, dagChecker, | |
| this->Language); | |
| } | |
| std::string EvaluateWithLanguage(std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*unused*/, | |
| std::string const& lang) const | |
| { | |
| std::string const& compilerLinkerId = | |
| eval->Context.LG->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILER_LINKER_ID")); | |
| if (parameters.empty()) { | |
| return compilerLinkerId; | |
| } | |
| if (compilerLinkerId.empty()) { | |
| return parameters.front().empty() ? "1" : "0"; | |
| } | |
| static cmsys::RegularExpression compilerLinkerIdValidator( | |
| "^[A-Za-z0-9_]*$"); | |
| for (auto const& param : parameters) { | |
| if (!compilerLinkerIdValidator.find(param)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return std::string(); | |
| } | |
| if (param == compilerLinkerId) { | |
| return "1"; | |
| } | |
| } | |
| return "0"; | |
| } | |
| char const* const Language; | |
| }; | |
| static CompilerLinkerIdNode const cCompilerLinkerIdNode("C"), | |
| cxxCompilerLinkerIdNode("CXX"), cudaCompilerLinkerIdNode("CUDA"), | |
| objcCompilerLinkerIdNode("OBJC"), objcxxCompilerLinkerIdNode("OBJCXX"), | |
| fortranCompilerLinkerIdNode("Fortran"), hipCompilerLinkerIdNode("HIP"), | |
| syclCompilerLinkerIdNode("SYCL"); | |
| struct CompilerLinkerFrontendVariantNode : public cmGeneratorExpressionNode | |
| { | |
| CompilerLinkerFrontendVariantNode(char const* lang) | |
| : Language(lang) | |
| { | |
| } | |
| int NumExpectedParameters() const override { return ZeroOrMoreParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!eval->HeadTarget) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat( | |
| "$<", this->Language, | |
| "_COMPILER_LINKER_FRONTEND_VARIANT> may only be used with binary " | |
| "targets. It may not be used with add_custom_command or " | |
| "add_custom_target.")); | |
| return {}; | |
| } | |
| return this->EvaluateWithLanguage(parameters, eval, content, dagChecker, | |
| this->Language); | |
| } | |
| std::string EvaluateWithLanguage(std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*unused*/, | |
| std::string const& lang) const | |
| { | |
| std::string const& compilerLinkerFrontendVariant = | |
| eval->Context.LG->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILER_LINKER_FRONTEND_VARIANT")); | |
| if (parameters.empty()) { | |
| return compilerLinkerFrontendVariant; | |
| } | |
| if (compilerLinkerFrontendVariant.empty()) { | |
| return parameters.front().empty() ? "1" : "0"; | |
| } | |
| static cmsys::RegularExpression compilerLinkerFrontendVariantValidator( | |
| "^[A-Za-z0-9_]*$"); | |
| for (auto const& param : parameters) { | |
| if (!compilerLinkerFrontendVariantValidator.find(param)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return {}; | |
| } | |
| if (param == compilerLinkerFrontendVariant) { | |
| return "1"; | |
| } | |
| } | |
| return "0"; | |
| } | |
| char const* const Language; | |
| }; | |
| static CompilerLinkerFrontendVariantNode const | |
| cCompilerLinkerFrontendVariantNode("C"), | |
| cxxCompilerLinkerFrontendVariantNode("CXX"), | |
| cudaCompilerLinkerFrontendVariantNode("CUDA"), | |
| objcCompilerLinkerFrontendVariantNode("OBJC"), | |
| objcxxCompilerLinkerFrontendVariantNode("OBJCXX"), | |
| fortranCompilerLinkerFrontendVariantNode("Fortran"), | |
| hipCompilerLinkerFrontendVariantNode("HIP"), | |
| syclCompilerLinkerFrontendVariantNode("SYCL"); | |
| static const struct LinkLibraryNode : public cmGeneratorExpressionNode | |
| { | |
| LinkLibraryNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return OneOrMoreParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| using ForGenex = cmGeneratorExpressionDAGChecker::ForGenex; | |
| if (!eval->HeadTarget || !dagChecker || | |
| !dagChecker->EvaluatingLinkLibraries(nullptr, | |
| ForGenex::LINK_LIBRARY)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<LINK_LIBRARY:...> 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( | |
| eval, content->GetOriginalExpression(), | |
| "$<LINK_LIBRARY:...> 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(eval, content->GetOriginalExpression(), | |
| cmStrCat("The feature name '", feature, | |
| "' contains invalid characters.")); | |
| return std::string(); | |
| } | |
| auto const LL_BEGIN = cmStrCat("<LINK_LIBRARY:", feature, '>'); | |
| auto const LL_END = cmStrCat("</LINK_LIBRARY:", feature, '>'); | |
| // filter out $<LINK_LIBRARY:..> tags with same feature | |
| // and raise an error for any different feature | |
| cm::erase_if(list, [&](std::string const& item) -> bool { | |
| return item == LL_BEGIN || item == LL_END; | |
| }); | |
| auto it = | |
| std::find_if(list.cbegin() + 1, list.cend(), | |
| [&feature](std::string const& item) -> bool { | |
| return cmHasPrefix(item, "<LINK_LIBRARY:"_s) && | |
| item.substr(14, item.find('>', 14) - 14) != feature; | |
| }); | |
| if (it != list.cend()) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<LINK_LIBRARY:...> with different features cannot be nested."); | |
| return std::string(); | |
| } | |
| // $<LINK_GROUP:...> must not appear as part of $<LINK_LIBRARY:...> | |
| it = std::find_if(list.cbegin() + 1, list.cend(), | |
| [](std::string const& item) -> bool { | |
| return cmHasPrefix(item, "<LINK_GROUP:"_s); | |
| }); | |
| if (it != list.cend()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<LINK_GROUP:...> cannot be nested inside a " | |
| "$<LINK_LIBRARY:...> 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| using ForGenex = cmGeneratorExpressionDAGChecker::ForGenex; | |
| if (!eval->HeadTarget || !dagChecker || | |
| !dagChecker->EvaluatingLinkLibraries(nullptr, ForGenex::LINK_GROUP)) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<LINK_GROUP:...> 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( | |
| eval, content->GetOriginalExpression(), | |
| "$<LINK_GROUP:...> expects a feature name as first argument."); | |
| return std::string(); | |
| } | |
| // $<LINK_GROUP:..> cannot be nested | |
| if (std::find_if(list.cbegin(), list.cend(), | |
| [](std::string const& item) -> bool { | |
| return cmHasPrefix(item, "<LINK_GROUP"_s); | |
| }) != list.cend()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<LINK_GROUP:...> 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(eval, content->GetOriginalExpression(), | |
| cmStrCat("The feature name '", feature, | |
| "' contains invalid characters.")); | |
| return std::string(); | |
| } | |
| auto const LG_BEGIN = cmStrCat( | |
| "<LINK_GROUP:", feature, ':', | |
| cmJoin(cmRange<decltype(list.cbegin())>(list.cbegin() + 1, list.cend()), | |
| "|"_s), | |
| '>'); | |
| auto const LG_END = cmStrCat("</LINK_GROUP:", feature, '>'); | |
| 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!eval->HeadTarget || !dagChecker || | |
| !dagChecker->EvaluatingLinkOptionsExpression()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<HOST_LINK:...> may only be used with binary targets " | |
| "to specify link options."); | |
| return std::string(); | |
| } | |
| return eval->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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (!eval->HeadTarget || !dagChecker || | |
| !dagChecker->EvaluatingLinkOptionsExpression()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<DEVICE_LINK:...> may only be used with binary targets " | |
| "to specify link options."); | |
| return std::string(); | |
| } | |
| if (eval->HeadTarget->IsDeviceLink()) { | |
| cmList list{ parameters.begin(), parameters.end() }; | |
| auto const DL_BEGIN = "<DEVICE_LINK>"_s; | |
| auto const DL_END = "</DEVICE_LINK>"_s; | |
| cm::erase_if(list, [&](std::string const& item) { | |
| return item == DL_BEGIN || item == DL_END; | |
| }); | |
| list.insert(list.begin(), static_cast<std::string>(DL_BEGIN)); | |
| list.push_back(static_cast<std::string>(DL_END)); | |
| return list.to_string(); | |
| } | |
| return std::string(); | |
| } | |
| } deviceLinkNode; | |
| static const struct GlobalPropertyNode : public cmGeneratorExpressionNode | |
| { | |
| GlobalPropertyNode() {} // NOLINT(modernize-use-equals-default) | |
| // This node handles errors on parameter count itself. | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagCheckerParent*/) const override | |
| { | |
| auto const& propertyName = parameters[0]; | |
| if (propertyName.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<GLOBAL_PROPERTY:property> expression requires a " | |
| "non-empty property name."); | |
| return std::string{}; | |
| } | |
| if (!IsValidPropertyName(eval, content, propertyName)) { | |
| return std::string{}; | |
| } | |
| cmake* cm = eval->Context.LG->GetCMakeInstance(); | |
| return cm->GetState()->GetGlobalProperty(propertyName); | |
| } | |
| } globalPropertyNode; | |
| static const struct DirectoryPropertyNode : public cmGeneratorExpressionNode | |
| { | |
| DirectoryPropertyNode() {} // NOLINT(modernize-use-equals-default) | |
| // This node handles errors on parameter count itself. | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagCheckerParent*/) const override | |
| { | |
| auto const& directoryName = parameters[0]; | |
| auto const& propertyName = parameters[1]; | |
| if (directoryName.empty()) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<DIRECTORY_PROPERTY:directory,property> expression requires a " | |
| "non-empty directory name."); | |
| return std::string{}; | |
| } | |
| if (propertyName.empty()) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<DIRECTORY_PROPERTY:directory,property> expression requires a " | |
| "non-empty property name."); | |
| return std::string{}; | |
| } | |
| if (!IsValidPropertyName(eval, content, propertyName)) { | |
| return std::string{}; | |
| } | |
| cmValue value; | |
| cmGetDirectoryPropertyResult status = cmGetDirectoryProperty( | |
| directoryName, propertyName, *eval->Context.LG->GetMakefile(), value); | |
| if (status == cmGetDirectoryPropertyResult::DirectoryNotFound) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("Directory \"", directoryName, "\" was not found.")); | |
| return std::string{}; | |
| } | |
| return value; | |
| } | |
| } directoryPropertyNode; | |
| namespace { | |
| bool GetFileSet(std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorTarget const*& target, | |
| cmGeneratorFileSet const*& fileSet) | |
| { | |
| auto const& fileSetName = parameters[0]; | |
| auto targetName = parameters[1]; | |
| fileSet = nullptr; | |
| auto const TARGET = "TARGET:"_s; | |
| if (cmHasPrefix(targetName, TARGET)) { | |
| targetName = targetName.substr(TARGET.length()); | |
| if (targetName.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("No value provided for the ", TARGET, " option.")); | |
| return false; | |
| } | |
| cmLocalGenerator const* lg = eval->CurrentTarget | |
| ? eval->CurrentTarget->GetLocalGenerator() | |
| : eval->Context.LG; | |
| target = lg->FindGeneratorTargetToUse(targetName); | |
| if (!target) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Non-existent target: ", targetName)); | |
| return false; | |
| } | |
| fileSet = target->GetFileSet(fileSetName); | |
| } else { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Invalid option. ", TARGET, " expected.")); | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |
| static const struct FileSetExistsNode : public cmGeneratorExpressionNode | |
| { | |
| FileSetExistsNode() {} // NOLINT(modernize-use-equals-default) | |
| // This node handles errors on parameter count itself. | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagCheckerParent*/) const override | |
| { | |
| if (parameters[0].empty()) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<FILE_SET_EXISTS:fileset,TARGET:tgt> expression requires a " | |
| "non-empty FILE_SET name."); | |
| return std::string{}; | |
| } | |
| cmGeneratorTarget const* target = nullptr; | |
| cmGeneratorFileSet const* fileSet = nullptr; | |
| if (!GetFileSet(parameters, eval, content, target, fileSet)) { | |
| return std::string{}; | |
| } | |
| return fileSet ? "1" : "0"; | |
| } | |
| } fileSetExistsNode; | |
| static const struct FileSetPropertyNode : public cmGeneratorExpressionNode | |
| { | |
| FileSetPropertyNode() {} // NOLINT(modernize-use-equals-default) | |
| // This node handles errors on parameter count itself. | |
| int NumExpectedParameters() const override { return 3; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const override | |
| { | |
| std::string const& fileSetName = parameters.front(); | |
| std::string const& propertyName = parameters.back(); | |
| if (fileSetName.empty() && propertyName.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<FILE_SET_PROPERTY:fileset,TARGET:tgt,prop> expression " | |
| "requires a non-empty FILE_SET name and property name."); | |
| return std::string{}; | |
| } | |
| if (fileSetName.empty()) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<FILE_SET_PROPERTY:fileset,TARGET:tgt,prop> expression requires a " | |
| "non-empty FILE_SET name."); | |
| return std::string{}; | |
| } | |
| if (propertyName.empty()) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<FILE_SET_PROPERTY:fileset,TARGET:tgt,prop> expression requires a " | |
| "non-empty property name."); | |
| return std::string{}; | |
| } | |
| if (!IsValidPropertyName(eval, content, propertyName)) { | |
| return std::string{}; | |
| } | |
| cmGeneratorTarget const* target = nullptr; | |
| cmGeneratorFileSet const* fileSet = nullptr; | |
| if (!GetFileSet(parameters, eval, content, target, fileSet)) { | |
| return std::string{}; | |
| } | |
| if (!fileSet) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("FILE_SET \"", fileSetName, "\" is not known from CMake.")); | |
| return std::string{}; | |
| } | |
| auto result = fileSet->GetProperty(propertyName); | |
| if (propertyName == "BASE_DIRS"_s || propertyName == "SOURCES"_s || | |
| propertyName == "INTERFACE_SOURCES"_s || | |
| propertyName == "INCLUDE_DIRECTORIES"_s || | |
| propertyName == "INTERFACE_INCLUDE_DIRECTORIES"_s || | |
| propertyName == "COMPILE_OPTIONS"_s || | |
| propertyName == "INTERFACE_COMPILE_OPTIONS"_s || | |
| propertyName == "COMPILE_DEFINITIONS"_s || | |
| propertyName == "INTERFACE_COMPILE_DEFINITIONS"_s) { | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| target, propertyName, content, | |
| dagCheckerParent, eval->Context, eval->Backtrace, | |
| }; | |
| switch (dagChecker.Check()) { | |
| case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: | |
| dagChecker.ReportError(eval, content->GetOriginalExpression()); | |
| return std::string{}; | |
| case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: | |
| // No error. We just skip cyclic references. | |
| return std::string{}; | |
| case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: | |
| case cmGeneratorExpressionDAGChecker::DAG: | |
| break; | |
| } | |
| return cmGeneratorExpression::StripEmptyListElements( | |
| this->EvaluateDependentExpression(result, eval, target, &dagChecker, | |
| target)); | |
| } | |
| return result; | |
| } | |
| } fileSetPropertyNode; | |
| namespace { | |
| bool GetSourceFile( | |
| cmRange<std::vector<std::string>::const_iterator> parameters, | |
| cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* content, | |
| cmSourceFile*& sourceFile) | |
| { | |
| auto sourceName = *parameters.begin(); | |
| auto* makefile = eval->Context.LG->GetMakefile(); | |
| sourceFile = nullptr; | |
| if (parameters.size() == 2) { | |
| auto const& option = *parameters.advance(1).begin(); | |
| auto const DIRECTORY = "DIRECTORY:"_s; | |
| auto const TARGET_DIRECTORY = "TARGET_DIRECTORY:"_s; | |
| if (cmHasPrefix(option, DIRECTORY)) { | |
| auto dir = option.substr(DIRECTORY.length()); | |
| if (dir.empty()) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("No value provided for the ", DIRECTORY, " option.")); | |
| return false; | |
| } | |
| dir = cmSystemTools::CollapseFullPath( | |
| dir, makefile->GetCurrentSourceDirectory()); | |
| makefile = makefile->GetGlobalGenerator()->FindMakefile(dir); | |
| if (!makefile) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("Directory \"", dir, "\" is not known from CMake.")); | |
| return false; | |
| } | |
| } else if (cmHasPrefix(option, TARGET_DIRECTORY)) { | |
| auto targetName = option.substr(TARGET_DIRECTORY.length()); | |
| if (targetName.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("No value provided for the ", TARGET_DIRECTORY, | |
| " option.")); | |
| return false; | |
| } | |
| auto* target = makefile->FindTargetToUse(targetName); | |
| if (!target) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Non-existent target: ", targetName)); | |
| return false; | |
| } | |
| makefile = makefile->GetGlobalGenerator()->FindMakefile( | |
| target->GetProperty("BINARY_DIR")); | |
| } else { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Invalid option. ", DIRECTORY, " or ", | |
| TARGET_DIRECTORY, " expected.")); | |
| return false; | |
| } | |
| sourceName = cmSystemTools::CollapseFullPath( | |
| sourceName, | |
| eval->Context.LG->GetMakefile()->GetCurrentSourceDirectory()); | |
| } | |
| sourceFile = makefile->GetSource(sourceName); | |
| return true; | |
| } | |
| } | |
| static const struct SourceExistsNode : public cmGeneratorExpressionNode | |
| { | |
| SourceExistsNode() {} // NOLINT(modernize-use-equals-default) | |
| // This node handles errors on parameter count itself. | |
| int NumExpectedParameters() const override { return OneOrMoreParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagCheckerParent*/) const override | |
| { | |
| if (parameters.size() > 2) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<SOURCE_EXISTS:...> expression requires at most two " | |
| "parameters."); | |
| return std::string{}; | |
| } | |
| if (parameters[0].empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<SOURCE_EXISTS:src> expression requires a " | |
| "non-empty source name."); | |
| return std::string{}; | |
| } | |
| cmSourceFile* sourceFile = nullptr; | |
| if (!GetSourceFile(cmMakeRange(parameters), eval, content, sourceFile)) { | |
| return std::string{}; | |
| } | |
| return sourceFile ? "1" : "0"; | |
| } | |
| } sourceExistsNode; | |
| static const struct SourcePropertyNode : public cmGeneratorExpressionNode | |
| { | |
| SourcePropertyNode() {} // NOLINT(modernize-use-equals-default) | |
| // This node handles errors on parameter count itself. | |
| int NumExpectedParameters() const override { return TwoOrMoreParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const override | |
| { | |
| if (parameters.size() > 3) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<SOURCE_PROPERTY:...> expression requires at most three " | |
| "parameters."); | |
| return std::string{}; | |
| } | |
| std::string sourceName = parameters.front(); | |
| std::string const& propertyName = parameters.back(); | |
| if (sourceName.empty() && propertyName.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<SOURCE_PROPERTY:src,prop> expression requires a " | |
| "non-empty source name and property name."); | |
| return std::string{}; | |
| } | |
| if (sourceName.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<SOURCE_PROPERTY:src,prop> expression requires a " | |
| "non-empty source name."); | |
| return std::string{}; | |
| } | |
| if (propertyName.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<SOURCE_PROPERTY:src,prop> expression requires a " | |
| "non-empty property name."); | |
| return std::string{}; | |
| } | |
| if (!IsValidPropertyName(eval, content, propertyName)) { | |
| return std::string{}; | |
| } | |
| cmSourceFile* sourceFile = nullptr; | |
| if (!GetSourceFile(cmMakeRange(parameters).retreat(1), eval, content, | |
| sourceFile)) { | |
| return std::string{}; | |
| } | |
| if (!sourceFile) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("Source file \"", sourceName, "\" is not known from CMake.")); | |
| return std::string{}; | |
| } | |
| if (propertyName == "OBJECT_NAME"_s) { | |
| return eval->Context.LG->GetCustomObjectFileName(*sourceFile); | |
| } | |
| std::string result = sourceFile->GetPropertyForUser(propertyName); | |
| if (propertyName == "INCLUDE_DIRECTORIES"_s || | |
| propertyName == "COMPILE_DEFINITIONS"_s || | |
| propertyName == "COMPILE_OPTIONS"_s || | |
| propertyName == "COMPILE_FLAGS"_s || | |
| propertyName == "OBJECT_OUTPUTS"_s || | |
| propertyName == "VS_DEPLOYMENT_CONTENT"_s || | |
| propertyName == "VS_SETTINGS"_s) { | |
| if (eval->HeadTarget) { | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| eval->HeadTarget, propertyName, content, | |
| dagCheckerParent, eval->Context, eval->Backtrace, | |
| }; | |
| switch (dagChecker.Check()) { | |
| case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: | |
| case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: { | |
| dagChecker.ReportError(eval, content->GetOriginalExpression()); | |
| return std::string{}; | |
| } | |
| case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: | |
| case cmGeneratorExpressionDAGChecker::DAG: | |
| break; | |
| } | |
| return cmGeneratorExpression::StripEmptyListElements( | |
| this->EvaluateDependentExpression(result, eval, eval->HeadTarget, | |
| &dagChecker, eval->CurrentTarget)); | |
| } | |
| return cmGeneratorExpression::StripEmptyListElements( | |
| this->EvaluateDependentExpression(result, eval, eval->HeadTarget, | |
| dagCheckerParent, | |
| eval->CurrentTarget)); | |
| } | |
| return result; | |
| } | |
| } sourcePropertyNode; | |
| static const struct RulePropertyNode : public cmGeneratorExpressionNode | |
| { | |
| RulePropertyNode() {} // NOLINT(modernize-use-equals-default) | |
| // This node handles errors on parameter count itself. | |
| int NumExpectedParameters() const override { return 2; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const override | |
| { | |
| std::string ruleName = parameters.front(); | |
| std::string const& propertyName = parameters.back(); | |
| if (ruleName.empty() && propertyName.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<RULE_PROPERTY:rule,prop> expression requires a " | |
| "non-empty rule name and property name."); | |
| return std::string{}; | |
| } | |
| if (ruleName.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<RULE_PROPERTY:rule,prop> expression requires a " | |
| "non-empty rule name."); | |
| return std::string{}; | |
| } | |
| if (propertyName.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "$<RULE_PROPERTY:src,prop> expression requires a " | |
| "non-empty property name."); | |
| return std::string{}; | |
| } | |
| if (!IsValidPropertyName(eval, content, propertyName)) { | |
| return std::string{}; | |
| } | |
| cmValue propertyValue; | |
| cmGeneratorRule* genRule = nullptr; | |
| genRule = eval->Context.LG->FindGeneratorRuleToUse(ruleName); | |
| if (!genRule) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Rule \"", ruleName, "\" is not known to CMake.")); | |
| return std::string{}; | |
| } | |
| // retrieve associated target | |
| cmGeneratorTarget* target = eval->Context.LG->FindGeneratorTargetToUse( | |
| genRule->GetTarget()->GetName()); | |
| if (!target) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Internal error for rule \"", | |
| genRule->GetRule()->GetName(), "\".")); | |
| return std::string{}; | |
| } | |
| propertyValue = genRule->GetProperty(propertyName); | |
| static cmsys::RegularExpression commandIndex("^COMMAND_[0-9]+$"); | |
| if (propertyValue && | |
| (propertyName == "OUTPUT"_s || propertyName == "COMMAND"_s || | |
| commandIndex.find(propertyName) || propertyName == "DEPENDS"_s || | |
| propertyName == "DEPFILE"_s || | |
| propertyName == "INCLUDE_DIRECTORIES"_s || | |
| propertyName == "COMPILE_OPTIONS"_s || | |
| propertyName == "COMPILE_DEFINITIONS"_s || | |
| propertyName == "WORKING_DIRECTORY"_s || | |
| propertyName == "COMMENT"_s)) { | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| target, propertyName, content, | |
| dagCheckerParent, eval->Context, eval->Backtrace, | |
| }; | |
| switch (dagChecker.Check()) { | |
| case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Self reference on rule \"", | |
| genRule->GetRule()->GetName(), "\".")); | |
| return std::string{}; | |
| case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: | |
| // No error. We just skip cyclic references. | |
| return std::string{}; | |
| case cmGeneratorExpressionDAGChecker::ALREADY_SEEN: | |
| CM_FALLTHROUGH; | |
| case cmGeneratorExpressionDAGChecker::DAG: | |
| break; | |
| } | |
| return cmGeneratorExpression::StripEmptyListElements( | |
| this->EvaluateDependentExpression(propertyValue, eval, target, | |
| &dagChecker, target)); | |
| } | |
| return propertyValue; | |
| } | |
| } rulePropertyNode; | |
| static std::string getLinkedTargetsContent( | |
| cmGeneratorTarget const* target, std::string const& prop, | |
| cm::GenEx::Evaluation* eval, cmGeneratorExpressionDAGChecker* dagChecker, | |
| cmGeneratorTarget::UseTo usage) | |
| { | |
| std::string result; | |
| if (cmLinkImplementationLibraries const* impl = | |
| target->GetLinkImplementationLibraries( | |
| eval->Context.Config, cmGeneratorTarget::UseTo::Compile)) { | |
| for (cmLinkItem const& lib : impl->Libraries) { | |
| if (lib.Target) { | |
| // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our | |
| // caller's property and hand-evaluate it as if it were compiled. | |
| // Create a context as cmCompiledGeneratorExpression::Evaluate does. | |
| cm::GenEx::Context libContext(eval->Context); | |
| // FIXME: Why have we long used the target's local generator | |
| // instead of that of the evaluation context? | |
| libContext.LG = target->GetLocalGenerator(); | |
| cm::GenEx::Evaluation libEval( | |
| std::move(libContext), eval->Quiet, target, target, | |
| eval->EvaluateForBuildsystem, lib.Backtrace); | |
| std::string libResult = lib.Target->EvaluateInterfaceProperty( | |
| prop, &libEval, 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 char const* GetErrorText(std::string const& targetName, | |
| std::string const& propertyName) | |
| { | |
| if (targetName.empty() && propertyName.empty()) { | |
| return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " | |
| "target name and property name."; | |
| } | |
| if (targetName.empty()) { | |
| return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty " | |
| "target name."; | |
| } | |
| if (!cmGeneratorExpression::IsValidTargetName(targetName)) { | |
| if (!IsValidPropertyName(propertyName)) { | |
| return "Target name and property name not supported."; | |
| } | |
| return "Target name not supported."; | |
| } | |
| return nullptr; | |
| } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagCheckerParent) const override | |
| { | |
| cmGeneratorTarget const* target = nullptr; | |
| std::string targetName; | |
| std::string propertyName; | |
| if (parameters.size() == 2) { | |
| targetName = parameters[0]; | |
| propertyName = parameters[1]; | |
| if (char const* e = GetErrorText(targetName, propertyName)) { | |
| reportError(eval, content->GetOriginalExpression(), e); | |
| return std::string(); | |
| } | |
| if (propertyName == "ALIASED_TARGET"_s) { | |
| if (eval->Context.LG->GetMakefile()->IsAlias(targetName)) { | |
| if (cmGeneratorTarget* tgt = | |
| eval->Context.LG->FindGeneratorTargetToUse(targetName)) { | |
| return tgt->GetName(); | |
| } | |
| } | |
| return std::string(); | |
| } | |
| if (propertyName == "ALIAS_GLOBAL"_s) { | |
| if (eval->Context.LG->GetMakefile()->IsAlias(targetName)) { | |
| return eval->Context.LG->GetGlobalGenerator()->IsAlias(targetName) | |
| ? "TRUE" | |
| : "FALSE"; | |
| } | |
| return std::string(); | |
| } | |
| cmLocalGenerator const* lg = eval->CurrentTarget | |
| ? eval->CurrentTarget->GetLocalGenerator() | |
| : eval->Context.LG; | |
| target = lg->FindGeneratorTargetToUse(targetName); | |
| if (!target) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Target \"", targetName, "\" not found.")); | |
| return std::string(); | |
| } | |
| eval->AllTargets.insert(target); | |
| } else if (parameters.size() == 1) { | |
| target = eval->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. | |
| eval->SeenTargetProperties.insert(propertyName); | |
| eval->HadHeadSensitiveCondition = true; | |
| if (!target) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<TARGET_PROPERTY:prop> may only be used with binary targets. " | |
| "It may not be used with add_custom_command or add_custom_target. " | |
| " " | |
| " " | |
| "Specify the target to read a property from using the " | |
| "$<TARGET_PROPERTY:tgt,prop> signature instead."); | |
| return std::string(); | |
| } | |
| } else { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<TARGET_PROPERTY:...> expression requires one or two parameters"); | |
| return std::string(); | |
| } | |
| if (propertyName == "SOURCES") { | |
| eval->SourceSensitiveTargets.insert(target); | |
| } | |
| if (propertyName.empty()) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<TARGET_PROPERTY:...> expression requires a non-empty property " | |
| "name."); | |
| return std::string(); | |
| } | |
| if (!IsValidPropertyName(eval, content, propertyName)) { | |
| return std::string(); | |
| } | |
| assert(target); | |
| if (propertyName == "LINKER_LANGUAGE") { | |
| if (target->LinkLanguagePropagatesToDependents() && dagCheckerParent && | |
| (dagCheckerParent->EvaluatingLinkLibraries() || | |
| dagCheckerParent->EvaluatingSources())) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "LINKER_LANGUAGE target property can not be used while evaluating " | |
| "link libraries for a static library"); | |
| return std::string(); | |
| } | |
| return target->GetLinkerLanguage(eval->Context.Config); | |
| } | |
| bool const evaluatingLinkLibraries = | |
| dagCheckerParent && dagCheckerParent->EvaluatingLinkLibraries(); | |
| std::string interfacePropertyName; | |
| bool isInterfaceProperty = false; | |
| cmGeneratorTarget::UseTo usage = cmGeneratorTarget::UseTo::Compile; | |
| if (cm::optional<cmGeneratorTarget::TransitiveProperty> transitiveProp = | |
| target->IsTransitiveProperty(propertyName, eval->Context, | |
| dagCheckerParent)) { | |
| interfacePropertyName = std::string(transitiveProp->InterfaceName); | |
| isInterfaceProperty = transitiveProp->InterfaceName == propertyName; | |
| usage = transitiveProp->Usage; | |
| } | |
| if (dagCheckerParent) { | |
| // This $<TARGET_PROPERTY:...> 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() && | |
| interfacePropertyName != "INTERFACE_LINK_LIBRARIES"_s) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<TARGET_PROPERTY:...> expression in link libraries " | |
| "evaluation depends on target property which is transitive " | |
| "over the link libraries, creating a recursion."); | |
| return std::string(); | |
| } | |
| } else { | |
| assert(dagCheckerParent->EvaluatingTransitiveProperty()); | |
| } | |
| } | |
| if (isInterfaceProperty) { | |
| return cmGeneratorExpression::StripEmptyListElements( | |
| target->EvaluateInterfaceProperty(propertyName, eval, dagCheckerParent, | |
| usage)); | |
| } | |
| cmGeneratorExpressionDAGChecker dagChecker{ | |
| target, propertyName, content, | |
| dagCheckerParent, eval->Context, eval->Backtrace, | |
| }; | |
| switch (dagChecker.Check()) { | |
| case cmGeneratorExpressionDAGChecker::SELF_REFERENCE: | |
| dagChecker.ReportError(eval, 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, | |
| eval->Context.Config)) { | |
| eval->HadContextSensitiveCondition = true; | |
| return target->GetLinkInterfaceDependentBoolProperty( | |
| propertyName, eval->Context.Config) | |
| ? "1" | |
| : "0"; | |
| } | |
| if (target->IsLinkInterfaceDependentStringProperty( | |
| propertyName, eval->Context.Config)) { | |
| eval->HadContextSensitiveCondition = true; | |
| char const* propContent = | |
| target->GetLinkInterfaceDependentStringProperty( | |
| propertyName, eval->Context.Config); | |
| return propContent ? propContent : ""; | |
| } | |
| } | |
| if (!evaluatingLinkLibraries && !target->IsImported()) { | |
| if (target->IsLinkInterfaceDependentNumberMinProperty( | |
| propertyName, eval->Context.Config)) { | |
| eval->HadContextSensitiveCondition = true; | |
| char const* propContent = | |
| target->GetLinkInterfaceDependentNumberMinProperty( | |
| propertyName, eval->Context.Config); | |
| return propContent ? propContent : ""; | |
| } | |
| if (target->IsLinkInterfaceDependentNumberMaxProperty( | |
| propertyName, eval->Context.Config)) { | |
| eval->HadContextSensitiveCondition = true; | |
| char const* propContent = | |
| target->GetLinkInterfaceDependentNumberMaxProperty( | |
| propertyName, eval->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, eval, target, &dagChecker, | |
| target)); | |
| std::string linkedTargetsContent = getLinkedTargetsContent( | |
| target, interfacePropertyName, eval, &dagChecker, usage); | |
| if (!linkedTargetsContent.empty()) { | |
| if (result.empty()) { | |
| result = std::move(linkedTargetsContent); | |
| } else { | |
| result = | |
| cmStrCat(std::move(result), ';', std::move(linkedTargetsContent)); | |
| } | |
| } | |
| } | |
| return result; | |
| } | |
| } targetPropertyNode; | |
| static const struct targetIntermediateDirNode | |
| : public cmGeneratorExpressionNode | |
| { | |
| targetIntermediateDirNode() {} // NOLINT(modernize-use-equals-default) | |
| static char const* GetErrorText(std::string const& targetName) | |
| { | |
| if (targetName.empty()) { | |
| return "$<TARGET_INTERMEDIATE_DIR:tgt> expression requires a non-empty " | |
| "target name."; | |
| } | |
| if (!cmGeneratorExpression::IsValidTargetName(targetName)) { | |
| return "Target name not supported."; | |
| } | |
| return nullptr; | |
| } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| cmGeneratorTarget const* target = nullptr; | |
| std::string targetName; | |
| if (parameters.size() == 1) { | |
| targetName = parameters[0]; | |
| if (char const* e = GetErrorText(targetName)) { | |
| reportError(eval, content->GetOriginalExpression(), e); | |
| return std::string(); | |
| } | |
| cmLocalGenerator const* lg = eval->CurrentTarget | |
| ? eval->CurrentTarget->GetLocalGenerator() | |
| : eval->Context.LG; | |
| target = lg->FindGeneratorTargetToUse(targetName); | |
| if (!target) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Target \"", targetName, "\" not found.")); | |
| return std::string(); | |
| } | |
| eval->AllTargets.insert(target); | |
| } else { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<TARGET_INTERMEDIATE_DIR:...> expression requires one parameter"); | |
| return std::string(); | |
| } | |
| assert(target); | |
| if (!HasKnownObjectFileLocation(eval, content, "TARGET_INTERMEDIATE_DIR", | |
| target)) { | |
| return std::string(); | |
| } | |
| return cmSystemTools::CollapseFullPath( | |
| target->GetObjectDirectory(eval->Context.Config)); | |
| } | |
| } targetIntermediateDirNode; | |
| 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( | |
| std::vector<std::string> const& parameters, | |
| cm::GenEx::Evaluation* /*eval*/, | |
| GeneratorExpressionContent const* /*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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| std::string const& tgtName = parameters.front(); | |
| cmGeneratorTarget* gt = | |
| eval->Context.LG->FindGeneratorTargetToUse(tgtName); | |
| if (!gt) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Objects of target \"", tgtName, | |
| "\" referenced but no such target exists.")); | |
| return std::string(); | |
| } | |
| cm::TargetType type = gt->GetType(); | |
| if (type != cm::TargetType::EXECUTABLE && | |
| type != cm::TargetType::STATIC_LIBRARY && | |
| type != cm::TargetType::SHARED_LIBRARY && | |
| type != cm::TargetType::MODULE_LIBRARY && | |
| type != cm::TargetType::OBJECT_LIBRARY) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("Objects of target \"", tgtName, | |
| "\" referenced but is not one of the allowed target types " | |
| "(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT).")); | |
| return std::string(); | |
| } | |
| cmGlobalGenerator const* gg = eval->Context.LG->GetGlobalGenerator(); | |
| if (!HasKnownObjectFileLocation(eval, content, "TARGET_OBJECTS", gt)) { | |
| return std::string(); | |
| } | |
| std::vector<std::string> sourceFilePaths; | |
| for (auto const& arg : cmMakeRange(parameters).advance(1)) { | |
| if (cmHasLiteralPrefix(arg, "SOURCE_FILES:")) { | |
| cm::string_view listView{ arg.c_str() + cmStrLen("SOURCE_FILES:") }; | |
| std::size_t semicolon; | |
| std::size_t start = 0; | |
| do { | |
| semicolon = listView.find(';', start); | |
| sourceFilePaths.push_back( | |
| std::string{ listView.substr(start, semicolon - start) }); | |
| start = semicolon + 1; | |
| } while (semicolon != cm::string_view::npos); | |
| } else { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Unrecognized argument:\n ", arg)); | |
| return std::string(); | |
| } | |
| } | |
| if (gt->IsImported() && !sourceFilePaths.empty()) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("Cannot use SOURCE_FILES argument on imported target \"", | |
| tgtName, '"')); | |
| return std::string(); | |
| } | |
| std::set<cmSourceFile const*> sourceFiles; | |
| for (auto const& sf : gt->GetSourceFiles(eval->Context.Config)) { | |
| sourceFiles.insert(sf.Value); | |
| } | |
| std::set<cmSourceFile const*> filteredSourceFiles; | |
| for (auto const& path : sourceFilePaths) { | |
| if (!cmSystemTools::FileIsFullPath(path)) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("Source file:\n ", path, "\nis not an absolute path")); | |
| return std::string(); | |
| } | |
| auto const* sf = gt->Makefile->GetSource(path); | |
| if (!sf || !sourceFiles.count(sf)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Source file:\n ", path, | |
| "\ndoes not exist for target \"", tgtName, '"')); | |
| return std::string(); | |
| } | |
| filteredSourceFiles.insert(sf); | |
| } | |
| cmList objects; | |
| if (gt->IsImported()) { | |
| cmValue loc = nullptr; | |
| cmValue imp = nullptr; | |
| std::string suffix; | |
| if (gt->Target->GetMappedConfig(eval->Context.Config, loc, imp, | |
| suffix)) { | |
| objects.assign(*loc); | |
| } | |
| eval->HadContextSensitiveCondition = true; | |
| } else { | |
| auto const filter = | |
| [&filteredSourceFiles](cmSourceFile const& sf) -> bool { | |
| return filteredSourceFiles.empty() || filteredSourceFiles.count(&sf); | |
| }; | |
| gt->GetTargetObjectNames(eval->Context.Config, filter, objects); | |
| std::string obj_dir; | |
| if (eval->EvaluateForBuildsystem && !gg->SupportsCrossConfigs()) { | |
| // Use object file directory with buildsystem placeholder. | |
| obj_dir = gt->ObjectDirectory; | |
| eval->HadContextSensitiveCondition = gt->HasContextDependentSources(); | |
| } else { | |
| // Use object file directory with per-config location. | |
| obj_dir = gt->GetObjectDirectory(eval->Context.Config); | |
| eval->HadContextSensitiveCondition = true; | |
| } | |
| for (auto& o : objects) { | |
| o = cmStrCat(obj_dir, std::move(o)); | |
| } | |
| } | |
| // Create the cmSourceFile instances in the referencing directory. | |
| cmMakefile* mf = eval->Context.LG->GetMakefile(); | |
| for (std::string const& o : objects) { | |
| mf->AddTargetObject(tgtName, o); | |
| } | |
| return objects.to_string(); | |
| } | |
| int NumExpectedParameters() const override { return OneOrMoreParameters; } | |
| } targetObjectsNode; | |
| struct TargetRuntimeDllsBaseNode : public cmGeneratorExpressionNode | |
| { | |
| std::vector<std::string> CollectDlls( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) const | |
| { | |
| std::string const& tgtName = parameters.front(); | |
| cmGeneratorTarget* gt = | |
| eval->Context.LG->FindGeneratorTargetToUse(tgtName); | |
| if (!gt) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("Objects of target \"", tgtName, | |
| "\" referenced but no such target exists.")); | |
| return std::vector<std::string>(); | |
| } | |
| cm::TargetType type = gt->GetType(); | |
| if (type != cm::TargetType::EXECUTABLE && | |
| type != cm::TargetType::SHARED_LIBRARY && | |
| type != cm::TargetType::MODULE_LIBRARY) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("Objects of target \"", tgtName, | |
| "\" referenced but is not one of the allowed target types " | |
| "(EXECUTABLE, SHARED, MODULE).")); | |
| return std::vector<std::string>(); | |
| } | |
| if (auto* cli = gt->GetLinkInformation(eval->Context.Config)) { | |
| std::vector<std::string> dllPaths; | |
| auto const& dlls = cli->GetRuntimeDLLs(); | |
| for (auto const& dll : dlls) { | |
| if (auto loc = dll->MaybeGetLocation(eval->Context.Config)) { | |
| dllPaths.emplace_back(*loc); | |
| } | |
| } | |
| return dllPaths; | |
| } | |
| return std::vector<std::string>(); | |
| } | |
| }; | |
| static const struct TargetRuntimeDllsNode : public TargetRuntimeDllsBaseNode | |
| { | |
| TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default) | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| std::vector<std::string> dlls = CollectDlls(parameters, eval, content); | |
| return cmList::to_string(dlls); | |
| } | |
| } targetRuntimeDllsNode; | |
| static const struct TargetRuntimeDllDirsNode : public TargetRuntimeDllsBaseNode | |
| { | |
| TargetRuntimeDllDirsNode() {} // NOLINT(modernize-use-equals-default) | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| std::vector<std::string> dlls = CollectDlls(parameters, eval, content); | |
| std::vector<std::string> dllDirs; | |
| for (std::string const& 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( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| cmGeneratorTarget const* target = eval->HeadTarget; | |
| if (!target) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<COMPILE_FEATURE> may only be used with binary targets. It may " | |
| "not be used with add_custom_command or add_custom_target."); | |
| return std::string(); | |
| } | |
| eval->HadHeadSensitiveCondition = true; | |
| using LangMap = std::map<std::string, cmList>; | |
| static LangMap availableFeatures; | |
| LangMap testedFeatures; | |
| cmStandardLevelResolver standardResolver(eval->Context.LG->GetMakefile()); | |
| for (std::string const& p : parameters) { | |
| std::string error; | |
| std::string lang; | |
| if (!standardResolver.CompileFeatureKnown( | |
| eval->HeadTarget->Target->GetName(), p, lang, &error)) { | |
| reportError(eval, 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(eval, content->GetOriginalExpression(), error); | |
| return std::string(); | |
| } | |
| availableFeatures[lang].assign(featuresKnown); | |
| } | |
| } | |
| bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries(); | |
| for (auto const& lit : testedFeatures) { | |
| std::vector<std::string> const& langAvailable = | |
| availableFeatures[lit.first]; | |
| cmValue standardDefault = eval->Context.LG->GetMakefile()->GetDefinition( | |
| cmStrCat("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, eval->Context.Config, it)) { | |
| if (evalLL) { | |
| cmValue l = | |
| target->GetLanguageStandard(lit.first, eval->Context.Config); | |
| if (!l) { | |
| l = standardDefault; | |
| } | |
| assert(l); | |
| eval->MaxLanguageStandard[target][lit.first] = *l; | |
| } else { | |
| return "0"; | |
| } | |
| } | |
| } | |
| } | |
| return "1"; | |
| } | |
| } compileFeaturesNode; | |
| static char const* 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, | |
| char const* policy) | |
| { | |
| #define RETURN_POLICY(POLICY) \ | |
| if (strcmp(policy, #POLICY) == 0) { \ | |
| return tgt->GetPolicyStatus##POLICY(); \ | |
| } | |
| CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY) | |
| #undef RETURN_POLICY | |
| CM_UNREACHABLE; | |
| return cmPolicies::WARN; | |
| } | |
| static cmPolicies::PolicyID policyForString(char const* 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 | |
| CM_UNREACHABLE; | |
| return cmPolicies::CMPCOUNT; | |
| } | |
| static const struct TargetPolicyNode : public cmGeneratorExpressionNode | |
| { | |
| TargetPolicyNode() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| if (!eval->HeadTarget) { | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<TARGET_POLICY:prop> may only be used with binary targets. It " | |
| "may not be used with add_custom_command or add_custom_target."); | |
| return std::string(); | |
| } | |
| eval->HadContextSensitiveCondition = true; | |
| eval->HadHeadSensitiveCondition = true; | |
| for (size_t i = 1; i < cm::size(targetPolicyWhitelist); ++i) { | |
| char const* policy = targetPolicyWhitelist[i]; | |
| if (parameters.front() == policy) { | |
| cmLocalGenerator* lg = eval->HeadTarget->GetLocalGenerator(); | |
| switch (statusForTarget(eval->HeadTarget, policy)) { | |
| case cmPolicies::WARN: | |
| lg->IssuePolicyWarning(policyForString(policy)); | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| return "0"; | |
| case cmPolicies::NEW: | |
| return "1"; | |
| } | |
| } | |
| } | |
| reportError( | |
| eval, content->GetOriginalExpression(), | |
| "$<TARGET_POLICY:prop> may only be used with a limited number of " | |
| "policies. Currently it may be used with the following policies:\n" | |
| #define STRINGIFY_HELPER(X) #X | |
| #define STRINGIFY(X) STRINGIFY_HELPER(X) | |
| #define TARGET_POLICY_LIST_ITEM(POLICY) " * " STRINGIFY(POLICY) "\n" | |
| CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_LIST_ITEM) | |
| #undef TARGET_POLICY_LIST_ITEM | |
| ); | |
| return std::string(); | |
| } | |
| } targetPolicyNode; | |
| static const struct InstallPrefixNode : public cmGeneratorExpressionNode | |
| { | |
| InstallPrefixNode() {} // NOLINT(modernize-use-equals-default) | |
| bool GeneratesContent() const override { return true; } | |
| int NumExpectedParameters() const override { return 0; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& /*parameters*/, | |
| cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| reportError(eval, 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 <typename ArtifactT, typename ComponentT> | |
| struct TargetFilesystemArtifactDependency | |
| { | |
| static void AddDependency(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval) | |
| { | |
| eval->DependTargets.insert(target); | |
| eval->AllTargets.insert(target); | |
| } | |
| }; | |
| struct TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| static void AddDependency(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval) | |
| { | |
| eval->AllTargets.insert(target); | |
| cmLocalGenerator const* const lg = eval->Context.LG; | |
| switch (target->GetPolicyStatusCMP0112()) { | |
| case cmPolicies::WARN: | |
| if (lg->GetMakefile()->PolicyOptionalWarningEnabled( | |
| "CMAKE_POLICY_WARNING_CMP0112")) { | |
| lg->IssuePolicyWarning( | |
| cmPolicies::CMP0112, {}, | |
| cmStrCat("Dependency being added to target:\n \""_s, | |
| target->GetName(), "\"\n"_s), | |
| eval->Backtrace); | |
| } | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| eval->DependTargets.insert(target); | |
| break; | |
| case cmPolicies::NEW: | |
| break; | |
| } | |
| } | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactNameTag> | |
| : TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFilesystemArtifactDependency<ArtifactT, ArtifactDirTag> | |
| : TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactDependency<ArtifactBundleDirTag, | |
| ArtifactPathTag> | |
| : TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactDependency<ArtifactBundleDirNameTag, | |
| ArtifactPathTag> | |
| : TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactDependency<ArtifactBundleContentDirTag, | |
| ArtifactPathTag> | |
| : TargetFilesystemArtifactDependencyCMP0112 | |
| { | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFilesystemArtifactResultCreator | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content); | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| // The target soname file (.so.1). | |
| if (target->IsDLLPlatform()) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_SONAME_FILE is not allowed " | |
| "for DLL target platforms."); | |
| return std::string(); | |
| } | |
| if (target->GetType() != cm::TargetType::SHARED_LIBRARY) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_SONAME_FILE is allowed only for " | |
| "SHARED libraries."); | |
| return std::string(); | |
| } | |
| if (target->IsArchivedAIXSharedLibrary()) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_SONAME_FILE is not allowed for " | |
| "AIX_SHARED_LIBRARY_ARCHIVE libraries."); | |
| return std::string(); | |
| } | |
| std::string result = | |
| cmStrCat(target->GetDirectory(eval->Context.Config), '/', | |
| target->GetSOName(eval->Context.Config)); | |
| return result; | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactSonameImportTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| // The target soname file (.so.1). | |
| if (target->IsDLLPlatform()) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_SONAME_IMPORT_FILE is not allowed " | |
| "for DLL target platforms."); | |
| return std::string(); | |
| } | |
| if (target->GetType() != cm::TargetType::SHARED_LIBRARY) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_SONAME_IMPORT_FILE is allowed only for " | |
| "SHARED libraries."); | |
| return std::string(); | |
| } | |
| if (target->IsArchivedAIXSharedLibrary()) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_SONAME_IMPORT_FILE is not allowed for " | |
| "AIX_SHARED_LIBRARY_ARCHIVE libraries."); | |
| return std::string(); | |
| } | |
| if (target->HasImportLibrary(eval->Context.Config)) { | |
| return cmStrCat( | |
| target->GetDirectory(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact), | |
| '/', | |
| target->GetSOName(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact)); | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| if (target->IsImported()) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE not allowed for IMPORTED targets."); | |
| return std::string(); | |
| } | |
| std::string language = target->GetLinkerLanguage(eval->Context.Config); | |
| std::string pdbSupportVar = | |
| cmStrCat("CMAKE_", language, "_LINKER_SUPPORTS_PDB"); | |
| if (!eval->Context.LG->GetMakefile()->IsOn(pdbSupportVar)) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE is not supported by the target linker."); | |
| return std::string(); | |
| } | |
| cm::TargetType targetType = target->GetType(); | |
| if (targetType != cm::TargetType::SHARED_LIBRARY && | |
| targetType != cm::TargetType::MODULE_LIBRARY && | |
| targetType != cm::TargetType::EXECUTABLE) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE is allowed only for " | |
| "targets with linker created artifacts."); | |
| return std::string(); | |
| } | |
| std::string result = | |
| cmStrCat(target->GetPDBDirectory(eval->Context.Config), '/', | |
| target->GetPDBName(eval->Context.Config)); | |
| return result; | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| // The file used to link to the target (.so, .lib, .a) or import file | |
| // (.lib, .tbd). | |
| if (!target->IsLinkable()) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_FILE is allowed only for libraries and " | |
| "executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| cmStateEnums::ArtifactType artifact = | |
| target->HasImportLibrary(eval->Context.Config) | |
| ? cmStateEnums::ImportLibraryArtifact | |
| : cmStateEnums::RuntimeBinaryArtifact; | |
| return target->GetFullPath(eval->Context.Config, artifact); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactLinkerLibraryTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| // The file used to link to the target (.dylib, .so, .a). | |
| if (!target->IsLinkable() || | |
| target->GetType() == cm::TargetType::EXECUTABLE) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_LIBRARY_FILE is allowed only for libraries " | |
| "with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| if (!target->IsDLLPlatform() || | |
| target->GetType() == cm::TargetType::STATIC_LIBRARY) { | |
| return target->GetFullPath(eval->Context.Config, | |
| cmStateEnums::RuntimeBinaryArtifact); | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactLinkerImportTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| // The file used to link to the target (.lib, .tbd). | |
| if (!target->IsLinkable()) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_IMPORT_FILE is allowed only for libraries and " | |
| "executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| if (target->HasImportLibrary(eval->Context.Config)) { | |
| return target->GetFullPath(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact); | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| if (target->IsImported()) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_BUNDLE_DIR not allowed for IMPORTED targets."); | |
| return std::string(); | |
| } | |
| if (!target->IsBundleOnApple()) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_BUNDLE_DIR is allowed only for Bundle targets."); | |
| return std::string(); | |
| } | |
| std::string outpath = target->GetDirectory(eval->Context.Config) + '/'; | |
| return target->BuildBundleDirectory(outpath, eval->Context.Config, | |
| cmGeneratorTarget::BundleDirLevel); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirNameTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| if (target->IsImported()) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_BUNDLE_DIR_NAME not allowed for IMPORTED targets."); | |
| return std::string(); | |
| } | |
| if (!target->IsBundleOnApple()) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_BUNDLE_DIR_NAME is allowed only for Bundle targets."); | |
| return std::string(); | |
| } | |
| auto level = cmGeneratorTarget::BundleDirLevel; | |
| auto config = eval->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<ArtifactBundleContentDirTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| if (target->IsImported()) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_BUNDLE_CONTENT_DIR not allowed for IMPORTED targets."); | |
| return std::string(); | |
| } | |
| if (!target->IsBundleOnApple()) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_BUNDLE_CONTENT_DIR is allowed only for Bundle targets."); | |
| return std::string(); | |
| } | |
| std::string outpath = target->GetDirectory(eval->Context.Config) + '/'; | |
| return target->BuildBundleDirectory(outpath, eval->Context.Config, | |
| cmGeneratorTarget::ContentLevel); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactNameTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* /*unused*/) | |
| { | |
| return target->GetFullPath(eval->Context.Config, | |
| cmStateEnums::RuntimeBinaryArtifact, true); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultCreator<ArtifactImportTag> | |
| { | |
| static std::string Create(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* /*unused*/) | |
| { | |
| if (target->HasImportLibrary(eval->Context.Config)) { | |
| return target->GetFullPath(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact, true); | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFilesystemArtifactResultGetter | |
| { | |
| static std::string Get(std::string const& result); | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultGetter<ArtifactNameTag> | |
| { | |
| static std::string Get(std::string const& result) | |
| { | |
| return cmSystemTools::GetFilenameName(result); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultGetter<ArtifactDirTag> | |
| { | |
| static std::string Get(std::string const& result) | |
| { | |
| return cmSystemTools::GetFilenamePath(result); | |
| } | |
| }; | |
| template <> | |
| struct TargetFilesystemArtifactResultGetter<ArtifactPathTag> | |
| { | |
| static std::string Get(std::string const& result) { return result; } | |
| }; | |
| struct TargetArtifactBase : public cmGeneratorExpressionNode | |
| { | |
| TargetArtifactBase() {} // NOLINT(modernize-use-equals-default) | |
| protected: | |
| cmGeneratorTarget* GetTarget( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const | |
| { | |
| // Lookup the referenced target. | |
| std::string const& name = parameters.front(); | |
| if (!cmGeneratorExpression::IsValidTargetName(name)) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "Expression syntax not recognized."); | |
| return nullptr; | |
| } | |
| cmGeneratorTarget* target = | |
| eval->Context.LG->FindGeneratorTargetToUse(name); | |
| if (!target) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat("No target \"", name, '"')); | |
| return nullptr; | |
| } | |
| if (target->GetType() >= cm::TargetType::OBJECT_LIBRARY && | |
| target->GetType() != cm::TargetType::UNKNOWN_LIBRARY) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| cmStrCat("Target \"", name, "\" is not an executable or library.")); | |
| return nullptr; | |
| } | |
| if (dagChecker && | |
| (dagChecker->EvaluatingLinkLibraries(target) || | |
| (dagChecker->EvaluatingSources() && | |
| target == dagChecker->TopTarget()))) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "Expressions which require the linker language may not " | |
| "be used while evaluating link libraries"); | |
| return nullptr; | |
| } | |
| return target; | |
| } | |
| }; | |
| template <typename ArtifactT, typename ComponentT> | |
| struct TargetFilesystemArtifact : public TargetArtifactBase | |
| { | |
| TargetFilesystemArtifact() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| cmGeneratorTarget* target = | |
| this->GetTarget(parameters, eval, content, dagChecker); | |
| if (!target) { | |
| return std::string(); | |
| } | |
| // Not a dependent target if we are querying for ArtifactDirTag, | |
| // ArtifactNameTag, ArtifactBundleDirTag, ArtifactBundleDirNameTag, | |
| // and ArtifactBundleContentDirTag | |
| TargetFilesystemArtifactDependency<ArtifactT, ComponentT>::AddDependency( | |
| target, eval); | |
| std::string result = | |
| TargetFilesystemArtifactResultCreator<ArtifactT>::Create(target, eval, | |
| content); | |
| if (eval->HadError) { | |
| return std::string(); | |
| } | |
| return TargetFilesystemArtifactResultGetter<ComponentT>::Get(result); | |
| } | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFilesystemArtifactNodeGroup | |
| { | |
| TargetFilesystemArtifactNodeGroup() // NOLINT(modernize-use-equals-default) | |
| { | |
| } | |
| TargetFilesystemArtifact<ArtifactT, ArtifactPathTag> File; | |
| TargetFilesystemArtifact<ArtifactT, ArtifactNameTag> FileName; | |
| TargetFilesystemArtifact<ArtifactT, ArtifactDirTag> FileDir; | |
| }; | |
| static TargetFilesystemArtifactNodeGroup<ArtifactNameTag> const | |
| targetNodeGroup; | |
| static TargetFilesystemArtifactNodeGroup<ArtifactImportTag> const | |
| targetImportNodeGroup; | |
| static TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag> const | |
| targetLinkerNodeGroup; | |
| static TargetFilesystemArtifactNodeGroup<ArtifactLinkerLibraryTag> const | |
| targetLinkerLibraryNodeGroup; | |
| static TargetFilesystemArtifactNodeGroup<ArtifactLinkerImportTag> const | |
| targetLinkerImportNodeGroup; | |
| static TargetFilesystemArtifactNodeGroup<ArtifactSonameTag> const | |
| targetSoNameNodeGroup; | |
| static TargetFilesystemArtifactNodeGroup<ArtifactSonameImportTag> const | |
| targetSoNameImportNodeGroup; | |
| static TargetFilesystemArtifactNodeGroup<ArtifactPdbTag> const | |
| targetPdbNodeGroup; | |
| static TargetFilesystemArtifact<ArtifactBundleDirTag, ArtifactPathTag> const | |
| targetBundleDirNode; | |
| static TargetFilesystemArtifact<ArtifactBundleDirNameTag, | |
| ArtifactNameTag> const targetBundleDirNameNode; | |
| static TargetFilesystemArtifact<ArtifactBundleContentDirTag, | |
| ArtifactPathTag> const | |
| targetBundleContentDirNode; | |
| // | |
| // To retrieve base name for various artifacts | |
| // | |
| enum class Postfix | |
| { | |
| Unspecified, | |
| Exclude, | |
| Include | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetOutputNameArtifactResultGetter | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| Postfix postfix); | |
| }; | |
| template <> | |
| struct TargetOutputNameArtifactResultGetter<ArtifactNameTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* /*unused*/, | |
| Postfix postfix) | |
| { | |
| auto output = target->GetOutputName(eval->Context.Config, | |
| cmStateEnums::RuntimeBinaryArtifact); | |
| return postfix != Postfix::Exclude | |
| ? cmStrCat(output, target->GetFilePostfix(eval->Context.Config)) | |
| : output; | |
| } | |
| }; | |
| template <> | |
| struct TargetOutputNameArtifactResultGetter<ArtifactImportTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* /*unused*/, | |
| Postfix postfix) | |
| { | |
| if (target->HasImportLibrary(eval->Context.Config)) { | |
| auto output = target->GetOutputName(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact); | |
| return postfix != Postfix::Exclude | |
| ? cmStrCat(output, target->GetFilePostfix(eval->Context.Config)) | |
| : output; | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| Postfix postfix) | |
| { | |
| // The library file used to link to the target (.so, .lib, .a) or import | |
| // file (.lin, .tbd). | |
| if (!target->IsLinkable()) { | |
| ::reportError(eval, 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(eval->Context.Config) | |
| ? cmStateEnums::ImportLibraryArtifact | |
| : cmStateEnums::RuntimeBinaryArtifact; | |
| auto output = target->GetOutputName(eval->Context.Config, artifact); | |
| return postfix != Postfix::Exclude | |
| ? cmStrCat(output, target->GetFilePostfix(eval->Context.Config)) | |
| : output; | |
| } | |
| }; | |
| template <> | |
| struct TargetOutputNameArtifactResultGetter<ArtifactLinkerLibraryTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| Postfix postfix) | |
| { | |
| // The library file used to link to the target (.so, .lib, .a). | |
| if (!target->IsLinkable() || | |
| target->GetType() == cm::TargetType::EXECUTABLE) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_LIBRARY_FILE_BASE_NAME is allowed only for " | |
| "libraries with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| if (!target->IsDLLPlatform() || | |
| target->GetType() == cm::TargetType::STATIC_LIBRARY) { | |
| auto output = target->GetOutputName(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact); | |
| return postfix != Postfix::Exclude | |
| ? cmStrCat(output, target->GetFilePostfix(eval->Context.Config)) | |
| : output; | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetOutputNameArtifactResultGetter<ArtifactLinkerImportTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| Postfix postfix) | |
| { | |
| // The import file used to link to the target (.lib, .tbd). | |
| if (!target->IsLinkable()) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_IMPORT_FILE_BASE_NAME is allowed only for " | |
| "libraries and executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| if (target->HasImportLibrary(eval->Context.Config)) { | |
| auto output = target->GetOutputName(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact); | |
| return postfix != Postfix::Exclude | |
| ? cmStrCat(output, target->GetFilePostfix(eval->Context.Config)) | |
| : output; | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| Postfix postfix) | |
| { | |
| if (target->IsImported()) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets."); | |
| return std::string(); | |
| } | |
| cmLocalGenerator const* const lg = eval->Context.LG; | |
| std::string language = target->GetLinkerLanguage(eval->Context.Config); | |
| std::string pdbSupportVar = | |
| cmStrCat("CMAKE_", language, "_LINKER_SUPPORTS_PDB"); | |
| if (!lg->GetMakefile()->IsOn(pdbSupportVar)) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE_BASE_NAME is not supported by the target linker."); | |
| return std::string(); | |
| } | |
| cm::TargetType targetType = target->GetType(); | |
| if (targetType != cm::TargetType::SHARED_LIBRARY && | |
| targetType != cm::TargetType::MODULE_LIBRARY && | |
| targetType != cm::TargetType::EXECUTABLE) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_PDB_FILE_BASE_NAME is allowed only for " | |
| "targets with linker created artifacts."); | |
| return std::string(); | |
| } | |
| auto output = target->GetPDBOutputName(eval->Context.Config); | |
| if (target->GetPolicyStatusCMP0202() == cmPolicies::NEW) { | |
| return postfix != Postfix::Exclude | |
| ? cmStrCat(output, target->GetFilePostfix(eval->Context.Config)) | |
| : output; | |
| } | |
| if (target->GetPolicyStatusCMP0202() == cmPolicies::WARN && | |
| postfix != Postfix::Unspecified) { | |
| lg->IssuePolicyWarning( | |
| cmPolicies::CMP0202, {}, | |
| "\"POSTFIX\" option is recognized only when the policy is set to NEW." | |
| " Since the policy is not set, the OLD behavior will be used."_s, | |
| eval->Backtrace); | |
| } | |
| return output; | |
| } | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFileBaseNameArtifact : public TargetArtifactBase | |
| { | |
| TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return OneOrMoreParameters; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| if (parameters.size() > 2) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "Unexpected parameters, require one or two parameters."); | |
| return std::string{}; | |
| } | |
| cmGeneratorTarget* target = | |
| this->GetTarget(parameters, eval, content, dagChecker); | |
| if (!target) { | |
| return std::string(); | |
| } | |
| Postfix postfix = Postfix::Unspecified; | |
| if (parameters.size() == 2) { | |
| if (parameters[1] == "POSTFIX:INCLUDE") { | |
| postfix = Postfix::Include; | |
| } else if (parameters[1] == "POSTFIX:EXCLUDE") { | |
| postfix = Postfix::Exclude; | |
| } else { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "Wrong second parameter: \"POSTFIX:INCLUDE\" or " | |
| "\"POSTFIX:EXCLUDE\" is expected"); | |
| } | |
| } | |
| std::string result = TargetOutputNameArtifactResultGetter<ArtifactT>::Get( | |
| target, eval, content, postfix); | |
| if (eval->HadError) { | |
| return std::string(); | |
| } | |
| return result; | |
| } | |
| }; | |
| static TargetFileBaseNameArtifact<ArtifactNameTag> const | |
| targetFileBaseNameNode; | |
| static TargetFileBaseNameArtifact<ArtifactImportTag> const | |
| targetImportFileBaseNameNode; | |
| static TargetFileBaseNameArtifact<ArtifactLinkerTag> const | |
| targetLinkerFileBaseNameNode; | |
| static TargetFileBaseNameArtifact<ArtifactLinkerLibraryTag> const | |
| targetLinkerLibraryFileBaseNameNode; | |
| static TargetFileBaseNameArtifact<ArtifactLinkerImportTag> const | |
| targetLinkerImportFileBaseNameNode; | |
| static TargetFileBaseNameArtifact<ArtifactPdbTag> const | |
| targetPdbFileBaseNameNode; | |
| class ArtifactFilePrefixTag; | |
| class ArtifactImportFilePrefixTag; | |
| class ArtifactLinkerFilePrefixTag; | |
| class ArtifactLinkerLibraryFilePrefixTag; | |
| class ArtifactLinkerImportFilePrefixTag; | |
| class ArtifactFileSuffixTag; | |
| class ArtifactImportFileSuffixTag; | |
| class ArtifactLinkerFileSuffixTag; | |
| class ArtifactLinkerLibraryFileSuffixTag; | |
| class ArtifactLinkerImportFileSuffixTag; | |
| template <typename ArtifactT> | |
| struct TargetFileArtifactResultGetter | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content); | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const*) | |
| { | |
| return target->GetFilePrefix(eval->Context.Config); | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactImportFilePrefixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const*) | |
| { | |
| if (target->HasImportLibrary(eval->Context.Config)) { | |
| return target->GetFilePrefix(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact); | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| if (!target->IsLinkable()) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_FILE_PREFIX is allowed only for libraries and " | |
| "executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| cmStateEnums::ArtifactType artifact = | |
| target->HasImportLibrary(eval->Context.Config) | |
| ? cmStateEnums::ImportLibraryArtifact | |
| : cmStateEnums::RuntimeBinaryArtifact; | |
| return target->GetFilePrefix(eval->Context.Config, artifact); | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactLinkerLibraryFilePrefixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| if (!target->IsLinkable() || | |
| target->GetType() == cm::TargetType::EXECUTABLE) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_LIBRARY_FILE_PREFIX is allowed only for libraries " | |
| "with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| if (!target->IsDLLPlatform() || | |
| target->GetType() == cm::TargetType::STATIC_LIBRARY) { | |
| return target->GetFilePrefix(eval->Context.Config, | |
| cmStateEnums::RuntimeBinaryArtifact); | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactLinkerImportFilePrefixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| if (!target->IsLinkable()) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_IMPORT_FILE_PREFIX is allowed only for libraries and " | |
| "executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| if (target->HasImportLibrary(eval->Context.Config)) { | |
| return target->GetFilePrefix(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact); | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const*) | |
| { | |
| return target->GetFileSuffix(eval->Context.Config); | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactImportFileSuffixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const*) | |
| { | |
| if (target->HasImportLibrary(eval->Context.Config)) { | |
| return target->GetFileSuffix(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact); | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| if (!target->IsLinkable()) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_FILE_SUFFIX is allowed only for libraries and " | |
| "executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| cmStateEnums::ArtifactType artifact = | |
| target->HasImportLibrary(eval->Context.Config) | |
| ? cmStateEnums::ImportLibraryArtifact | |
| : cmStateEnums::RuntimeBinaryArtifact; | |
| return target->GetFileSuffix(eval->Context.Config, artifact); | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactLinkerLibraryFileSuffixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| if (!target->IsLinkable() || | |
| target->GetType() == cm::TargetType::STATIC_LIBRARY) { | |
| ::reportError(eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_LIBRARY_FILE_SUFFIX is allowed only for " | |
| "libraries with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| if (!target->IsDLLPlatform() || | |
| target->GetType() == cm::TargetType::STATIC_LIBRARY) { | |
| return target->GetFileSuffix(eval->Context.Config, | |
| cmStateEnums::RuntimeBinaryArtifact); | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <> | |
| struct TargetFileArtifactResultGetter<ArtifactLinkerImportFileSuffixTag> | |
| { | |
| static std::string Get(cmGeneratorTarget* target, | |
| cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content) | |
| { | |
| if (!target->IsLinkable()) { | |
| ::reportError( | |
| eval, content->GetOriginalExpression(), | |
| "TARGET_LINKER_IMPORT_FILE_SUFFIX is allowed only for libraries and " | |
| "executables with ENABLE_EXPORTS."); | |
| return std::string(); | |
| } | |
| if (target->HasImportLibrary(eval->Context.Config)) { | |
| return target->GetFileSuffix(eval->Context.Config, | |
| cmStateEnums::ImportLibraryArtifact); | |
| } | |
| return std::string{}; | |
| } | |
| }; | |
| template <typename ArtifactT> | |
| struct TargetFileArtifact : public TargetArtifactBase | |
| { | |
| TargetFileArtifact() {} // NOLINT(modernize-use-equals-default) | |
| int NumExpectedParameters() const override { return 1; } | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* dagChecker) const override | |
| { | |
| cmGeneratorTarget* target = | |
| this->GetTarget(parameters, eval, content, dagChecker); | |
| if (!target) { | |
| return std::string(); | |
| } | |
| std::string result = | |
| TargetFileArtifactResultGetter<ArtifactT>::Get(target, eval, content); | |
| if (eval->HadError) { | |
| return std::string(); | |
| } | |
| return result; | |
| } | |
| }; | |
| static TargetFileArtifact<ArtifactFilePrefixTag> const targetFilePrefixNode; | |
| static TargetFileArtifact<ArtifactImportFilePrefixTag> const | |
| targetImportFilePrefixNode; | |
| static TargetFileArtifact<ArtifactLinkerFilePrefixTag> const | |
| targetLinkerFilePrefixNode; | |
| static TargetFileArtifact<ArtifactLinkerLibraryFilePrefixTag> const | |
| targetLinkerLibraryFilePrefixNode; | |
| static TargetFileArtifact<ArtifactLinkerImportFilePrefixTag> const | |
| targetLinkerImportFilePrefixNode; | |
| static TargetFileArtifact<ArtifactFileSuffixTag> const targetFileSuffixNode; | |
| static TargetFileArtifact<ArtifactImportFileSuffixTag> const | |
| targetImportFileSuffixNode; | |
| static TargetFileArtifact<ArtifactLinkerFileSuffixTag> const | |
| targetLinkerFileSuffixNode; | |
| static TargetFileArtifact<ArtifactLinkerLibraryFileSuffixTag> const | |
| targetLinkerLibraryFileSuffixNode; | |
| static TargetFileArtifact<ArtifactLinkerImportFileSuffixTag> const | |
| targetLinkerImportFileSuffixNode; | |
| static const struct ShellPathNode : public cmGeneratorExpressionNode | |
| { | |
| ShellPathNode() {} // NOLINT(modernize-use-equals-default) | |
| std::string Evaluate( | |
| std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval, | |
| GeneratorExpressionContent const* content, | |
| cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override | |
| { | |
| cmList list_in{ parameters.front() }; | |
| if (list_in.empty()) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| "\"\" is not an absolute path."); | |
| return std::string(); | |
| } | |
| cmStateSnapshot snapshot = eval->Context.LG->GetStateSnapshot(); | |
| cmOutputConverter converter(snapshot); | |
| char const* separator = snapshot.GetState()->UseWindowsShell() ? ";" : ":"; | |
| std::vector<std::string> list_out; | |
| list_out.reserve(list_in.size()); | |
| for (auto const& in : list_in) { | |
| if (!cmSystemTools::FileIsFullPath(in)) { | |
| reportError(eval, content->GetOriginalExpression(), | |
| cmStrCat('"', in, "\" is not an absolute path.")); | |
| return std::string(); | |
| } | |
| list_out.emplace_back(converter.ConvertDirectorySeparatorsForShell(in)); | |
| } | |
| return cmJoin(list_out, separator); | |
| } | |
| } shellPathNode; | |
| cmGeneratorExpressionNode const* cmGeneratorExpressionNode::GetNode( | |
| std::string const& identifier) | |
| { | |
| static std::map<std::string, cmGeneratorExpressionNode const*> const nodeMap{ | |
| { "0", &zeroNode }, | |
| { "1", &oneNode }, | |
| { "AND", &andNode }, | |
| { "OR", &orNode }, | |
| { "NOT", ¬Node }, | |
| { "ARGS", &argsNode }, | |
| { "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 }, | |
| { "SYCL_COMPILER_ID", &syclCompilerIdNode }, | |
| { "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 }, | |
| { "SYCL_COMPILER_VERSION", &syclCompilerVersionNode }, | |
| { "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 }, | |
| { "SYCL_COMPILER_FRONTEND_VARIANT", &syclCompilerFrontendVariantNode }, | |
| { "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 }, | |
| { "STRLESS", &strLessNode }, | |
| { "STRLESS_EQUAL", &strLessEqualNode }, | |
| { "STRGREATER", &strGreaterNode }, | |
| { "STRGREATER_EQUAL", &strGreaterEqualNode }, | |
| { "STRING", &stringNode }, | |
| { "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 }, | |
| { "_0", &boundOperandNode0 }, | |
| { "_1", &boundOperandNode1 }, | |
| { "IF", &ifNode }, | |
| { "ANGLE-R", &angle_rNode }, | |
| { "COMMA", &commaNode }, | |
| { "SEMICOLON", &semicolonNode }, | |
| { "QUOTE", "eNode }, | |
| { "GLOBAL_PROPERTY", &globalPropertyNode }, | |
| { "DIRECTORY_PROPERTY", &directoryPropertyNode }, | |
| { "RULE_PROPERTY", &rulePropertyNode }, | |
| { "SOURCE_EXISTS", &sourceExistsNode }, | |
| { "SOURCE_PROPERTY", &sourcePropertyNode }, | |
| { "FILE_SET_EXISTS", &fileSetExistsNode }, | |
| { "FILE_SET_PROPERTY", &fileSetPropertyNode }, | |
| { "TARGET_PROPERTY", &targetPropertyNode }, | |
| { "TARGET_INTERMEDIATE_DIR", &targetIntermediateDirNode }, | |
| { "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 }, | |
| { "C_COMPILER_LINKER_ID", &cCompilerLinkerIdNode }, | |
| { "CXX_COMPILER_LINKER_ID", &cxxCompilerLinkerIdNode }, | |
| { "OBJC_COMPILER_LINKER_ID", &objcCompilerLinkerIdNode }, | |
| { "OBJCXX_COMPILER_LINKER_ID", &objcxxCompilerLinkerIdNode }, | |
| { "CUDA_COMPILER_LINKER_ID", &cudaCompilerLinkerIdNode }, | |
| { "Fortran_COMPILER_LINKER_ID", &fortranCompilerLinkerIdNode }, | |
| { "HIP_COMPILER_LINKER_ID", &hipCompilerLinkerIdNode }, | |
| { "SYCL_COMPILER_LINKER_ID", &syclCompilerLinkerIdNode }, | |
| { "C_COMPILER_LINKER_FRONTEND_VARIANT", | |
| &cCompilerLinkerFrontendVariantNode }, | |
| { "CXX_COMPILER_LINKER_FRONTEND_VARIANT", | |
| &cxxCompilerLinkerFrontendVariantNode }, | |
| { "CUDA_COMPILER_LINKER_FRONTEND_VARIANT", | |
| &cudaCompilerLinkerFrontendVariantNode }, | |
| { "OBJC_COMPILER_LINKER_FRONTEND_VARIANT", | |
| &objcCompilerLinkerFrontendVariantNode }, | |
| { "OBJCXX_COMPILER_LINKER_FRONTEND_VARIANT", | |
| &objcxxCompilerLinkerFrontendVariantNode }, | |
| { "Fortran_COMPILER_LINKER_FRONTEND_VARIANT", | |
| &fortranCompilerLinkerFrontendVariantNode }, | |
| { "HIP_COMPILER_LINKER_FRONTEND_VARIANT", | |
| &hipCompilerLinkerFrontendVariantNode }, | |
| { "SYCL_COMPILER_LINKER_FRONTEND_VARIANT", | |
| &syclCompilerLinkerFrontendVariantNode }, | |
| { "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(cm::GenEx::Evaluation* eval, std::string const& expr, | |
| std::string const& result) | |
| { | |
| eval->HadError = true; | |
| if (eval->Quiet) { | |
| return; | |
| } | |
| eval->Context.LG->GetCMakeInstance()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Error evaluating generator expression:\n" | |
| " ", | |
| expr, '\n', result), | |
| eval->Backtrace); | |
| } |