Files
CMake/Source/cmGeneratorExpressionNode.cxx
Craig Scott 1b764b6816 cmLinkItem: Remove redundant cmLinkImplItem class
The cmLinkImplItem class used to contain some additional members
to support the CMP0027 policy. Since the CMP0027 policy and those
associated members were removed, the cmLinkImplItem class has been
a plain wrapper around cmLinkItem with no additional members.
It is no longer needed and cmLinkItem can be used directly wherever
cmLinkImplItem was used previously.
2025-10-07 10:44:28 -04:00

4886 lines
178 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 <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 "cmComputeLinkInformation.h"
#include "cmGenExContext.h"
#include "cmGenExEvaluation.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorExpressionEvaluator.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLinkItem.h"
#include "cmList.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmOutputConverter.h"
#include "cmPolicies.h"
#include "cmRange.h"
#include "cmStandardLevelResolver.h"
#include "cmState.h"
#include "cmStateSnapshot.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmValue.h"
#include "cmake.h"
std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
std::string const& prop, 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;
}
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;
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 parameters.front() == parameters[1] ? "1" : "0";
}
} strEqualNode;
static const struct EqualNode : public cmGeneratorExpressionNode
{
EqualNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 2; }
std::string Evaluate(
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(),
"$<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;
switch (eval->Context.LG->GetPolicyStatus(cmPolicies::CMP0085)) {
case cmPolicies::WARN:
if (parameters.front().empty()) {
check = true;
checkValues.assign(parameters[1], cmList::EmptyElements::Yes);
}
CM_FALLTHROUGH;
case cmPolicies::OLD:
values.assign(parameters[1]);
if (check && values != checkValues) {
std::ostringstream e;
e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085)
<< "\nSearch Item:\n \"" << parameters.front()
<< "\"\nList:\n \"" << parameters[1] << "\"\n";
eval->Context.LG->GetCMakeInstance()->IssueMessage(
MessageType ::AUTHOR_WARNING, e.str(), 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, 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;
};
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)) {
if (normalize) {
return ToString(cmCMakePath{ args[0] }.Normal().IsPrefix(
cmCMakePath{ args[1] }.Normal()));
}
return ToString(
cmCMakePath{ args[0] }.IsPrefix(cmCMakePath{ args[1] }));
}
return std::string{};
} },
{ "CMAKE_PATH"_s,
[](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 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 };
}
bool GetNumericArgument(std::string const& arg, cmList::index_type& value)
{
try {
std::size_t pos;
if (sizeof(cmList::index_type) == sizeof(long)) {
value = std::stol(arg, &pos);
} else {
value = std::stoll(arg, &pos);
}
if (pos != arg.length()) {
// this is not a number
return false;
}
} catch (std::invalid_argument const&) {
return false;
}
return true;
}
bool GetNumericArguments(
cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* cnt,
Arguments args, std::vector<cmList::index_type>& 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) {
cmList::index_type 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;
}
}
static const struct ListNode : public cmGeneratorExpressionNode
{
ListNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return TwoOrMoreParameters; }
bool AcceptsArbitraryContentParameter() const override { return true; }
std::string Evaluate(
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&)>>
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 {
if (CheckListParameters(ev, cnt, "FILTER"_s, args, 3)) {
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{};
}
try {
return GetList(args.front())
.filter(args[2],
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 \"",
args[2], "\"."));
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()) {
struct ActionDescriptor
{
ActionDescriptor(std::string name)
: Name(std::move(name))
{
}
ActionDescriptor(std::string name,
cmList::TransformAction action, int arity)
: Name(std::move(name))
, Action(action)
, Arity(arity)
{
}
operator std::string const&() const { return this->Name; }
std::string Name;
cmList::TransformAction Action;
int Arity = 0;
};
static std::set<
ActionDescriptor,
std::function<bool(std::string const&, std::string const&)>>
descriptors{
{ { "APPEND", cmList::TransformAction::APPEND, 1 },
{ "PREPEND", cmList::TransformAction::PREPEND, 1 },
{ "TOUPPER", cmList::TransformAction::TOUPPER, 0 },
{ "TOLOWER", cmList::TransformAction::TOLOWER, 0 },
{ "STRIP", cmList::TransformAction::STRIP, 0 },
{ "REPLACE", cmList::TransformAction::REPLACE, 2 } },
[](std::string const& x, std::string const& y) {
return x < y;
}
};
auto descriptor = descriptors.find(args.advance(1).front());
if (descriptor == descriptors.end()) {
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat(" sub-command TRANSFORM, ",
args.front(), " invalid action."));
return std::string{};
}
// Action arguments
args.advance(1);
if (args.size() < descriptor->Arity) {
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat("sub-command TRANSFORM, action ",
descriptor->Name, " 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::string const REGEX{ "REGEX" };
std::string const AT{ "AT" };
std::string const FOR{ "FOR" };
std::unique_ptr<cmList::TransformSelector> selector;
try {
// handle optional arguments
while (!args.empty()) {
if ((args.front() == REGEX || args.front() == AT ||
args.front() == FOR) &&
selector) {
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat("sub-command TRANSFORM, selector "
"already specified (",
selector->GetTag(), ")."));
return std::string{};
}
// REGEX selector
if (args.front() == REGEX) {
if (args.advance(1).empty()) {
reportError(
ev, cnt->GetOriginalExpression(),
"sub-command TRANSFORM, selector REGEX expects "
"'regular expression' argument.");
return std::string{};
}
selector = cmList::TransformSelector::New<
cmList::TransformSelector::REGEX>(args.front());
args.advance(1);
continue;
}
// AT selector
if (args.front() == AT) {
args.advance(1);
// get all specified indexes
std::vector<cmList::index_type> indexes;
while (!args.empty()) {
cmList indexList{ args.front() };
for (auto const& index : indexList) {
cmList::index_type value;
if (!GetNumericArgument(index, value)) {
// this is not a number, stop processing
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("sub-command TRANSFORM, selector AT: '",
index, "': unexpected argument."));
return std::string{};
}
indexes.push_back(value);
}
args.advance(1);
}
if (indexes.empty()) {
reportError(ev, cnt->GetOriginalExpression(),
"sub-command TRANSFORM, selector AT "
"expects at least one "
"numeric value.");
return std::string{};
}
selector = cmList::TransformSelector::New<
cmList::TransformSelector::AT>(std::move(indexes));
continue;
}
// FOR selector
if (args.front() == FOR) {
if (args.advance(1).size() < 2) {
reportError(ev, cnt->GetOriginalExpression(),
"sub-command TRANSFORM, selector FOR "
"expects, at least,"
" two arguments.");
return std::string{};
}
cmList::index_type start = 0;
cmList::index_type stop = 0;
cmList::index_type step = 1;
bool valid = false;
if (GetNumericArgument(args.front(), start) &&
GetNumericArgument(args.advance(1).front(), stop)) {
valid = true;
}
if (!valid) {
reportError(
ev, cnt->GetOriginalExpression(),
"sub-command TRANSFORM, selector FOR expects, "
"at least, two numeric values.");
return std::string{};
}
// try to read a third numeric value for step
if (!args.advance(1).empty()) {
if (!GetNumericArgument(args.front(), step)) {
// this is not a number
step = -1;
}
args.advance(1);
}
if (step <= 0) {
reportError(
ev, cnt->GetOriginalExpression(),
"sub-command TRANSFORM, selector FOR expects "
"positive numeric value for <step>.");
return std::string{};
}
selector = cmList::TransformSelector::New<
cmList::TransformSelector::FOR>({ start, stop, step });
continue;
}
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat("sub-command TRANSFORM, '",
cmJoin(args, ", "),
"': unexpected argument(s)."));
return std::string{};
}
if (!selector) {
selector = cmList::TransformSelector::New();
}
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);
auto const COMPARE = "COMPARE:"_s;
auto const CASE = "CASE:"_s;
auto const ORDER = "ORDER:"_s;
using SortConfig = cmList::SortConfiguration;
SortConfig sortConfig;
for (auto const& arg : args) {
if (cmHasPrefix(arg, COMPARE)) {
if (sortConfig.Compare !=
SortConfig::CompareMethod::DEFAULT) {
reportError(ev, cnt->GetOriginalExpression(),
"sub-command SORT, COMPARE option has been "
"specified multiple times.");
return std::string{};
}
auto option =
cm::string_view{ arg.c_str() + COMPARE.length() };
if (option == "STRING"_s) {
sortConfig.Compare = SortConfig::CompareMethod::STRING;
continue;
}
if (option == "FILE_BASENAME"_s) {
sortConfig.Compare =
SortConfig::CompareMethod::FILE_BASENAME;
continue;
}
if (option == "NATURAL"_s) {
sortConfig.Compare = SortConfig::CompareMethod::NATURAL;
continue;
}
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat(
"sub-command SORT, an invalid COMPARE option has been "
"specified: \"",
option, "\"."));
return std::string{};
}
if (cmHasPrefix(arg, CASE)) {
if (sortConfig.Case !=
SortConfig::CaseSensitivity::DEFAULT) {
reportError(ev, cnt->GetOriginalExpression(),
"sub-command SORT, CASE option has been "
"specified multiple times.");
return std::string{};
}
auto option = cm::string_view{ arg.c_str() + CASE.length() };
if (option == "SENSITIVE"_s) {
sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE;
continue;
}
if (option == "INSENSITIVE"_s) {
sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE;
continue;
}
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat(
"sub-command SORT, an invalid CASE option has been "
"specified: \"",
option, "\"."));
return std::string{};
}
if (cmHasPrefix(arg, ORDER)) {
if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) {
reportError(ev, cnt->GetOriginalExpression(),
"sub-command SORT, ORDER option has been "
"specified multiple times.");
return std::string{};
}
auto option =
cm::string_view{ arg.c_str() + ORDER.length() };
if (option == "ASCENDING"_s) {
sortConfig.Order = SortConfig::OrderMode::ASCENDING;
continue;
}
if (option == "DESCENDING"_s) {
sortConfig.Order = SortConfig::OrderMode::DESCENDING;
continue;
}
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat(
"sub-command SORT, an invalid ORDER option has been "
"specified: \"",
option, "\"."));
return std::string{};
}
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("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("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(
"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;
}
// 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, or the consuming target's
// configuration, otherwise.
//
// If CMPxxxx is OLD, we evaluate first in the context of the consuming
// target, then, if the consumed target is imported, we evaluate based on
// the mapped configurations (this logic is... problematic; see comment
// below), then finally based on the selected configuration of the imported
// target.
bool const targetIsImported =
(eval->CurrentTarget && eval->CurrentTarget->IsImported());
bool const oldPolicy = [&] {
if (!targetIsImported) {
// For non-imported targets, there is no behavior difference between
// the OLD and NEW policy.
return false;
}
cmTarget const* const t = eval->CurrentTarget->Target;
if (t->GetOrigin() == cmTarget::Origin::Cps) {
// Generator expressions appearing on targets imported from CPS should
// always be evaluated according to the selected configuration of the
// imported target, i.e. the NEW policy.
return false;
}
switch (eval->HeadTarget->GetPolicyStatusCMP0199()) {
case cmPolicies::WARN:
if (eval->Context.LG->GetMakefile()->PolicyOptionalWarningEnabled(
"CMAKE_POLICY_WARNING_CMP0199")) {
std::string const err =
cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0199),
"\nEvaluation of $<CONFIG> for imported target \"",
eval->CurrentTarget->GetName(), "\", used by \"",
eval->HeadTarget->GetName(),
"\", may match multiple configurations.\n");
eval->Context.LG->GetCMakeInstance()->IssueMessage(
MessageType ::AUTHOR_WARNING, err, eval->Backtrace);
}
CM_FALLTHROUGH;
case cmPolicies::OLD:
return true;
case cmPolicies::NEW:
return false;
}
// Should be unreachable
assert(false);
return false;
}();
if (!targetIsImported || oldPolicy) {
// 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)) {
if (oldPolicy) {
// If the target has a MAP_IMPORTED_CONFIG_<CONFIG> property for the
// consumer's <CONFIG>, 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";
}
}
// This imported target has an appropriate location for this (possibly
// mapped) config.
if (!suffix.empty()) {
// Use the (possibly mapped) configuration of the imported location
// that was selected.
for (auto const& param : parameters) {
if (cmStrCat('_', cmSystemTools::UpperCase(param)) == suffix) {
return "1";
}
}
}
}
}
return "0";
}
} configurationTestNode;
static const struct JoinNode : public cmGeneratorExpressionNode
{
JoinNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 2; }
bool AcceptsArbitraryContentParameter() const override { return true; }
std::string Evaluate(
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("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;
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 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();
}
cmStateEnums::TargetType type = gt->GetType();
if (type != cmStateEnums::EXECUTABLE &&
type != cmStateEnums::STATIC_LIBRARY &&
type != cmStateEnums::SHARED_LIBRARY &&
type != cmStateEnums::MODULE_LIBRARY &&
type != cmStateEnums::OBJECT_LIBRARY) {
std::ostringstream e;
e << "Objects of target \"" << tgtName
<< "\" referenced but is not one of the allowed target types "
<< "(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT).";
reportError(eval, content->GetOriginalExpression(), e.str());
return std::string();
}
cmGlobalGenerator const* gg = eval->Context.LG->GetGlobalGenerator();
{
std::string reason;
if (!eval->EvaluateForBuildsystem &&
!gt->Target->HasKnownObjectFileLocation(&reason)) {
std::ostringstream e;
e << "The evaluation of the TARGET_OBJECTS generator expression "
"is only suitable for consumption by CMake (limited"
<< reason
<< "). "
"It is not suitable for writing out elsewhere.";
reportError(eval, content->GetOriginalExpression(), e.str());
return std::string();
}
}
cmList objects;
if (gt->IsImported()) {
cmValue loc = nullptr;
cmValue imp = nullptr;
std::string suffix;
if (gt->Target->GetMappedConfig(eval->Context.Config, loc, imp,
suffix)) {
objects.assign(*loc);
}
eval->HadContextSensitiveCondition = true;
} else {
gt->GetTargetObjectNames(eval->Context.Config, 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();
}
} 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>();
}
cmStateEnums::TargetType type = gt->GetType();
if (type != cmStateEnums::EXECUTABLE &&
type != cmStateEnums::SHARED_LIBRARY &&
type != cmStateEnums::MODULE_LIBRARY) {
std::ostringstream e;
e << "Objects of target \"" << tgtName
<< "\" referenced but is not one of the allowed target types "
<< "(EXECUTABLE, SHARED, MODULE).";
reportError(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(
"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
assert(false && "Unreachable code. Not a valid policy");
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
assert(false && "Unreachable code. Not a valid policy");
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->IssueMessage(
MessageType::AUTHOR_WARNING,
cmPolicies::GetPolicyWarning(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* lg = eval->Context.LG;
switch (target->GetPolicyStatusCMP0112()) {
case cmPolicies::WARN:
if (lg->GetMakefile()->PolicyOptionalWarningEnabled(
"CMAKE_POLICY_WARNING_CMP0112")) {
std::string err =
cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0112),
"\nDependency being added to target:\n \"",
target->GetName(), "\"\n");
lg->GetCMakeInstance()->IssueMessage(MessageType ::AUTHOR_WARNING,
err, 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() != cmStateEnums::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() != cmStateEnums::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 = "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();
}
cmStateEnums::TargetType targetType = target->GetType();
if (targetType != cmStateEnums::SHARED_LIBRARY &&
targetType != cmStateEnums::MODULE_LIBRARY &&
targetType != cmStateEnums::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() == cmStateEnums::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() == cmStateEnums::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(),
"No target \"" + name + "\"");
return nullptr;
}
if (target->GetType() >= cmStateEnums::OBJECT_LIBRARY &&
target->GetType() != cmStateEnums::UNKNOWN_LIBRARY) {
::reportError(eval, content->GetOriginalExpression(),
"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() == cmStateEnums::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() == cmStateEnums::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();
}
std::string language = target->GetLinkerLanguage(eval->Context.Config);
std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB";
if (!eval->Context.LG->GetMakefile()->IsOn(pdbSupportVar)) {
::reportError(
eval, content->GetOriginalExpression(),
"TARGET_PDB_FILE_BASE_NAME is not supported by the target linker.");
return std::string();
}
cmStateEnums::TargetType targetType = target->GetType();
if (targetType != cmStateEnums::SHARED_LIBRARY &&
targetType != cmStateEnums::MODULE_LIBRARY &&
targetType != cmStateEnums::EXECUTABLE) {
::reportError(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) {
eval->Context.LG->GetCMakeInstance()->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0202), '\n',
"\"POSTFIX\" option is recognized only when the policy is "
"set to NEW. Since the policy is not set, the OLD behavior "
"will be used."),
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() == cmStateEnums::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() == cmStateEnums::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() == cmStateEnums::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() == cmStateEnums::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(),
"\"" + 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 },
{ "EQUAL", &equalNode },
{ "IN_LIST", &inListNode },
{ "FILTER", &filterNode },
{ "REMOVE_DUPLICATES", &removeDuplicatesNode },
{ "LIST", &listNode },
{ "LOWER_CASE", &lowerCaseNode },
{ "UPPER_CASE", &upperCaseNode },
{ "PATH", &pathNode },
{ "PATH_EQUAL", &pathEqualNode },
{ "MAKE_C_IDENTIFIER", &makeCIdentifierNode },
{ "BOOL", &boolNode },
{ "IF", &ifNode },
{ "ANGLE-R", &angle_rNode },
{ "COMMA", &commaNode },
{ "SEMICOLON", &semicolonNode },
{ "QUOTE", &quoteNode },
{ "TARGET_PROPERTY", &targetPropertyNode },
{ "TARGET_NAME", &targetNameNode },
{ "TARGET_OBJECTS", &targetObjectsNode },
{ "TARGET_POLICY", &targetPolicyNode },
{ "TARGET_EXISTS", &targetExistsNode },
{ "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode },
{ "TARGET_GENEX_EVAL", &targetGenexEvalNode },
{ "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode },
{ "TARGET_RUNTIME_DLL_DIRS", &targetRuntimeDllDirsNode },
{ "GENEX_EVAL", &genexEvalNode },
{ "BUILD_INTERFACE", &buildInterfaceNode },
{ "INSTALL_INTERFACE", &installInterfaceNode },
{ "BUILD_LOCAL_INTERFACE", &buildLocalInterfaceNode },
{ "INSTALL_PREFIX", &installPrefixNode },
{ "JOIN", &joinNode },
{ "COMPILE_ONLY", &compileOnlyNode },
{ "LINK_ONLY", &linkOnlyNode },
{ "COMPILE_LANG_AND_ID", &languageAndIdNode },
{ "COMPILE_LANGUAGE", &languageNode },
{ "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
{ "LINK_LANGUAGE", &linkLanguageNode },
{ "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);
}