Files
cmake/Source/cmGeneratorExpressionNode.cxx
T
Mickaël Germain 7192a2b798 cmake_path: An empty path is not a prefix of any path
cmake_path(IS_PREFIX) and $<PATH:IS_PREFIX> treated an empty path as a
prefix of every path, including another empty path, following
std::filesystem::path.  A prefix that is empty because a variable was
set to an empty value, or because a generator expression argument
expanded to nothing, therefore satisfied a check meant to reject it.

Return false for an empty prefix, in cmCMakePath::IsPrefix so that every
caller shares one implementation and both the plain and NORMALIZE forms
are covered.  Normalizing an empty path leaves it empty, so no separate
handling of NORMALIZE is needed.  Unlike the component comparison, which
follows std::filesystem::path deliberately, IsPrefix has no counterpart
in the standard: it borrows path iteration but the predicate itself is
defined by CMake, so an empty prefix is a gap to fill rather than a
standard answer to override.

Add policy CMP0223 and restore the old result behind it at the two
released surfaces.  The other callers of IsPrefix, source_group() and
the Makefile generator's source classification, take the new behavior
ungated: source_group() rejects an empty TREE argument before reaching
it, and the generator passes the source and binary directories.

The if(PATH_IS_PREFIX) operator, new in this same release, follows the
policy too rather than simply taking the new behavior, so that it agrees
with cmake_path(IS_PREFIX) in every policy state and the parity
assertions in its test hold unconditionally.

Fixes: #28077
2026-09-17 10:05:27 -04:00

6536 lines
238 KiB
C++

/* 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 <sstream>
#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 "cmGenExContext.h"
#include "cmGenExEvaluation.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorExpressionEvaluator.h"
#include "cmGeneratorFileSet.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 "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 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)) {
std::ostringstream e;
e << "The evaluation of the " << genex
<< " generator expression "
"is only suitable for consumption by CMake (limited"
<< reason
<< "). "
"It is not suitable for writing out elsewhere.";
reportError(eval, content->GetOriginalExpression(), e.str());
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) {
std::ostringstream e;
e << "Parameters to $<" << this->op;
e << "> must resolve to either '0' or '1'.";
reportError(eval, content->GetOriginalExpression(), e.str());
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 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) {
std::ostringstream e;
e << "$<TARGET_GENEX_EVAL:tgt, ...> target \"" << targetName
<< "\" not found.";
reportError(eval, content->GetOriginalExpression(), e.str());
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) {
std::ostringstream e;
e << "$<" << this->CompilerLanguage
<< "_COMPILER_ID> may only be used with binary targets. It may "
"not be used with add_custom_command or add_custom_target.";
reportError(eval, content->GetOriginalExpression(), e.str());
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");
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) {
std::ostringstream e;
e << "$<" << this->CompilerLanguage
<< "_COMPILER_VERSION> may only be used with binary targets. It "
"may not be used with add_custom_command or add_custom_target.";
reportError(eval, content->GetOriginalExpression(), e.str());
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");
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) {
std::ostringstream e;
e << "$<" << this->CompilerLanguage
<< "_COMPILER_FRONTEND_VARIANT> may only be used with binary targets. "
" It may not be used with add_custom_command or add_custom_target.";
reportError(eval, content->GetOriginalExpression(), e.str());
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");
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
std::ostringstream e;
/* clang-format off */
e << "Warning evaluating generator expression:\n"
<< " " << content->GetOriginalExpression() << "\n"
<< "The config name of \"" << param << "\" is invalid";
/* clang-format on */
eval->Context.LG->GetCMakeInstance()->IssueMessage(
MessageType::WARNING, e.str(), 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, 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, and compile "
"options. 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 "
"and link depends.");
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, and "
"link "
"depends.");
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");
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");
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;
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
{
static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
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 (!propertyNameValidator.find(propertyName)) {
reportError(eval, content->GetOriginalExpression(),
"Property name not supported.");
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
{
static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
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 (!propertyNameValidator.find(propertyName)) {
reportError(eval, content->GetOriginalExpression(),
"Property name not supported.");
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 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)
{
static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
if (targetName.empty() && propertyName.empty()) {
return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty "
"target name and property name.";
}
if (targetName.empty()) {
return "$<TARGET_PROPERTY:tgt,prop> expression requires a non-empty "
"target name.";
}
if (!cmGeneratorExpression::IsValidTargetName(targetName)) {
if (!propertyNameValidator.find(propertyName)) {
return "Target name and property name not supported.";
}
return "Target name not supported.";
}
return nullptr;
}
std::string Evaluate(
std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* content,
cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
{
static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
cmGeneratorTarget const* target = nullptr;
std::string targetName;
std::string propertyName;
if (parameters.size() == 2) {
targetName = parameters[0];
propertyName = parameters[1];
if (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) {
std::ostringstream e;
e << "Target \"" << targetName << "\" not found.";
reportError(eval, content->GetOriginalExpression(), e.str());
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 (!propertyNameValidator.find(propertyName)) {
::reportError(eval, content->GetOriginalExpression(),
"Property name not supported.");
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()) {
result += (result.empty() ? "" : ";") + linkedTargetsContent;
}
}
return result;
}
} targetPropertyNode;
static const struct targetIntermediateDirNode
: public cmGeneratorExpressionNode
{
targetIntermediateDirNode() {} // NOLINT(modernize-use-equals-default)
static char const* GetErrorText(std::string const& targetName)
{
static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
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) {
std::ostringstream e;
e << "Target \"" << targetName << "\" not found.";
reportError(eval, content->GetOriginalExpression(), e.str());
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) {
std::ostringstream e;
e << "Objects of target \"" << tgtName
<< "\" referenced but no such target exists.";
reportError(eval, content->GetOriginalExpression(), e.str());
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) {
std::ostringstream e;
e << "Objects of target \"" << tgtName
<< "\" referenced but is not one of the allowed target types "
<< "(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT).";
reportError(eval, content->GetOriginalExpression(), e.str());
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, 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) {
std::ostringstream e;
e << "Objects of target \"" << tgtName
<< "\" referenced but no such target exists.";
reportError(eval, content->GetOriginalExpression(), e.str());
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) {
std::ostringstream e;
e << "Objects of target \"" << tgtName
<< "\" referenced but is not one of the allowed target types "
<< "(EXECUTABLE, SHARED, MODULE).";
reportError(eval, content->GetOriginalExpression(), e.str());
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", &notNode },
{ "C_COMPILER_ID", &cCompilerIdNode },
{ "CXX_COMPILER_ID", &cxxCompilerIdNode },
{ "OBJC_COMPILER_ID", &objcCompilerIdNode },
{ "OBJCXX_COMPILER_ID", &objcxxCompilerIdNode },
{ "CUDA_COMPILER_ID", &cudaCompilerIdNode },
{ "Fortran_COMPILER_ID", &fortranCompilerIdNode },
{ "HIP_COMPILER_ID", &hipCompilerIdNode },
{ "VERSION_GREATER", &versionGreaterNode },
{ "VERSION_GREATER_EQUAL", &versionGreaterEqNode },
{ "VERSION_LESS", &versionLessNode },
{ "VERSION_LESS_EQUAL", &versionLessEqNode },
{ "VERSION_EQUAL", &versionEqualNode },
{ "C_COMPILER_VERSION", &cCompilerVersionNode },
{ "CXX_COMPILER_VERSION", &cxxCompilerVersionNode },
{ "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode },
{ "OBJC_COMPILER_VERSION", &objcCompilerVersionNode },
{ "OBJCXX_COMPILER_VERSION", &objcxxCompilerVersionNode },
{ "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode },
{ "HIP_COMPILER_VERSION", &hipCompilerVersionNode },
{ "C_COMPILER_FRONTEND_VARIANT", &cCompilerFrontendVariantNode },
{ "CXX_COMPILER_FRONTEND_VARIANT", &cxxCompilerFrontendVariantNode },
{ "CUDA_COMPILER_FRONTEND_VARIANT", &cudaCompilerFrontendVariantNode },
{ "OBJC_COMPILER_FRONTEND_VARIANT", &objcCompilerFrontendVariantNode },
{ "OBJCXX_COMPILER_FRONTEND_VARIANT", &objcxxCompilerFrontendVariantNode },
{ "Fortran_COMPILER_FRONTEND_VARIANT",
&fortranCompilerFrontendVariantNode },
{ "HIP_COMPILER_FRONTEND_VARIANT", &hipCompilerFrontendVariantNode },
{ "PLATFORM_ID", &platformIdNode },
{ "COMPILE_FEATURES", &compileFeaturesNode },
{ "CONFIGURATION", &configurationNode },
{ "CONFIG", &configurationTestNode },
{ "TARGET_FILE", &targetNodeGroup.File },
{ "TARGET_IMPORT_FILE", &targetImportNodeGroup.File },
{ "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File },
{ "TARGET_LINKER_LIBRARY_FILE", &targetLinkerLibraryNodeGroup.File },
{ "TARGET_LINKER_IMPORT_FILE", &targetLinkerImportNodeGroup.File },
{ "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File },
{ "TARGET_SONAME_IMPORT_FILE", &targetSoNameImportNodeGroup.File },
{ "TARGET_PDB_FILE", &targetPdbNodeGroup.File },
{ "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode },
{ "TARGET_IMPORT_FILE_BASE_NAME", &targetImportFileBaseNameNode },
{ "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode },
{ "TARGET_LINKER_LIBRARY_FILE_BASE_NAME",
&targetLinkerLibraryFileBaseNameNode },
{ "TARGET_LINKER_IMPORT_FILE_BASE_NAME",
&targetLinkerImportFileBaseNameNode },
{ "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode },
{ "TARGET_FILE_PREFIX", &targetFilePrefixNode },
{ "TARGET_IMPORT_FILE_PREFIX", &targetImportFilePrefixNode },
{ "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode },
{ "TARGET_LINKER_LIBRARY_FILE_PREFIX",
&targetLinkerLibraryFilePrefixNode },
{ "TARGET_LINKER_IMPORT_FILE_PREFIX", &targetLinkerImportFilePrefixNode },
{ "TARGET_FILE_SUFFIX", &targetFileSuffixNode },
{ "TARGET_IMPORT_FILE_SUFFIX", &targetImportFileSuffixNode },
{ "TARGET_LINKER_FILE_SUFFIX", &targetLinkerFileSuffixNode },
{ "TARGET_LINKER_LIBRARY_FILE_SUFFIX",
&targetLinkerLibraryFileSuffixNode },
{ "TARGET_LINKER_IMPORT_FILE_SUFFIX", &targetLinkerImportFileSuffixNode },
{ "TARGET_FILE_NAME", &targetNodeGroup.FileName },
{ "TARGET_IMPORT_FILE_NAME", &targetImportNodeGroup.FileName },
{ "TARGET_LINKER_FILE_NAME", &targetLinkerNodeGroup.FileName },
{ "TARGET_LINKER_LIBRARY_FILE_NAME",
&targetLinkerLibraryNodeGroup.FileName },
{ "TARGET_LINKER_IMPORT_FILE_NAME",
&targetLinkerImportNodeGroup.FileName },
{ "TARGET_SONAME_FILE_NAME", &targetSoNameNodeGroup.FileName },
{ "TARGET_SONAME_IMPORT_FILE_NAME",
&targetSoNameImportNodeGroup.FileName },
{ "TARGET_PDB_FILE_NAME", &targetPdbNodeGroup.FileName },
{ "TARGET_FILE_DIR", &targetNodeGroup.FileDir },
{ "TARGET_IMPORT_FILE_DIR", &targetImportNodeGroup.FileDir },
{ "TARGET_LINKER_FILE_DIR", &targetLinkerNodeGroup.FileDir },
{ "TARGET_LINKER_LIBRARY_FILE_DIR",
&targetLinkerLibraryNodeGroup.FileDir },
{ "TARGET_LINKER_IMPORT_FILE_DIR", &targetLinkerImportNodeGroup.FileDir },
{ "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.FileDir },
{ "TARGET_SONAME_IMPORT_FILE_DIR", &targetSoNameImportNodeGroup.FileDir },
{ "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir },
{ "TARGET_BUNDLE_DIR", &targetBundleDirNode },
{ "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode },
{ "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode },
{ "STREQUAL", &strEqualNode },
{ "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", &quoteNode },
{ "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 },
{ "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 },
{ "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;
}
std::ostringstream e;
/* clang-format off */
e << "Error evaluating generator expression:\n"
<< " " << expr << "\n"
<< result;
/* clang-format on */
eval->Context.LG->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
e.str(), eval->Backtrace);
}