You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4565 lines
165 KiB
4565 lines
165 KiB
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmGeneratorExpressionNode.h"
|
|
|
|
#include <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 "cmGeneratorExpression.h"
|
|
#include "cmGeneratorExpressionContext.h"
|
|
#include "cmGeneratorExpressionDAGChecker.h"
|
|
#include "cmGeneratorExpressionEvaluator.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmLinkItem.h"
|
|
#include "cmList.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMessageType.h"
|
|
#include "cmOutputConverter.h"
|
|
#include "cmPolicies.h"
|
|
#include "cmRange.h"
|
|
#include "cmStandardLevelResolver.h"
|
|
#include "cmState.h"
|
|
#include "cmStateSnapshot.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmTarget.h"
|
|
#include "cmValue.h"
|
|
#include "cmake.h"
|
|
|
|
std::string cmGeneratorExpressionNode::EvaluateDependentExpression(
|
|
std::string const& prop, cmLocalGenerator* lg,
|
|
cmGeneratorExpressionContext* context, cmGeneratorTarget const* headTarget,
|
|
cmGeneratorExpressionDAGChecker* dagChecker,
|
|
cmGeneratorTarget const* currentTarget)
|
|
{
|
|
cmGeneratorExpression ge(*lg->GetCMakeInstance(), context->Backtrace);
|
|
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(prop);
|
|
cge->SetEvaluateForBuildsystem(context->EvaluateForBuildsystem);
|
|
cge->SetQuiet(context->Quiet);
|
|
std::string result =
|
|
cge->Evaluate(lg, context->Config, headTarget, dagChecker, currentTarget,
|
|
context->Language);
|
|
if (cge->GetHadContextSensitiveCondition()) {
|
|
context->HadContextSensitiveCondition = true;
|
|
}
|
|
if (cge->GetHadHeadSensitiveCondition()) {
|
|
context->HadHeadSensitiveCondition = true;
|
|
}
|
|
if (cge->GetHadLinkLanguageSensitiveCondition()) {
|
|
context->HadLinkLanguageSensitiveCondition = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static const struct ZeroNode : public cmGeneratorExpressionNode
|
|
{
|
|
ZeroNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
bool GeneratesContent() const override { return false; }
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& /*parameters*/,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return std::string();
|
|
}
|
|
} zeroNode;
|
|
|
|
static const struct OneNode : public cmGeneratorExpressionNode
|
|
{
|
|
OneNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return parameters.front();
|
|
}
|
|
} oneNode;
|
|
|
|
static const struct OneNode buildInterfaceNode;
|
|
|
|
static const struct ZeroNode installInterfaceNode;
|
|
|
|
static const struct OneNode buildLocalInterfaceNode;
|
|
|
|
struct BooleanOpNode : public cmGeneratorExpressionNode
|
|
{
|
|
BooleanOpNode(const char* op_, const char* successVal_,
|
|
const char* failureVal_)
|
|
: op(op_)
|
|
, successVal(successVal_)
|
|
, failureVal(failureVal_)
|
|
{
|
|
}
|
|
|
|
int NumExpectedParameters() const override { return OneOrMoreParameters; }
|
|
|
|
std::string Evaluate(const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker*) const override
|
|
{
|
|
for (std::string const& param : parameters) {
|
|
if (param == this->failureVal) {
|
|
return this->failureVal;
|
|
}
|
|
if (param != this->successVal) {
|
|
std::ostringstream e;
|
|
e << "Parameters to $<" << this->op;
|
|
e << "> must resolve to either '0' or '1'.";
|
|
reportError(context, content->GetOriginalExpression(), e.str());
|
|
return std::string();
|
|
}
|
|
}
|
|
return this->successVal;
|
|
}
|
|
|
|
const char *const op, *const successVal, *const failureVal;
|
|
};
|
|
|
|
static const BooleanOpNode andNode("AND", "1", "0"), orNode("OR", "0", "1");
|
|
|
|
static const struct NotNode : public cmGeneratorExpressionNode
|
|
{
|
|
NotNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
if (parameters.front() != "0" && parameters.front() != "1") {
|
|
reportError(
|
|
context, 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(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return !cmIsOff(parameters.front()) ? "1" : "0";
|
|
}
|
|
} boolNode;
|
|
|
|
static const struct IfNode : public cmGeneratorExpressionNode
|
|
{
|
|
IfNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 3; }
|
|
|
|
std::string Evaluate(const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker*) const override
|
|
{
|
|
if (parameters[0] != "1" && parameters[0] != "0") {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"First parameter to $<IF> must resolve to exactly one '0' "
|
|
"or '1' value.");
|
|
return std::string();
|
|
}
|
|
return parameters[0] == "1" ? parameters[1] : parameters[2];
|
|
}
|
|
} ifNode;
|
|
|
|
static const struct StrEqualNode : public cmGeneratorExpressionNode
|
|
{
|
|
StrEqualNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 2; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return parameters.front() == parameters[1] ? "1" : "0";
|
|
}
|
|
} strEqualNode;
|
|
|
|
static const struct EqualNode : public cmGeneratorExpressionNode
|
|
{
|
|
EqualNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 2; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
long numbers[2];
|
|
for (int i = 0; i < 2; ++i) {
|
|
if (!ParameterToLong(parameters[i].c_str(), &numbers[i])) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<EQUAL> parameter " + parameters[i] +
|
|
" is not a valid integer.");
|
|
return {};
|
|
}
|
|
}
|
|
return numbers[0] == numbers[1] ? "1" : "0";
|
|
}
|
|
|
|
static bool ParameterToLong(const char* param, long* outResult)
|
|
{
|
|
const char isNegative = param[0] == '-';
|
|
|
|
int base = 0;
|
|
if (cmHasLiteralPrefix(param, "0b") || cmHasLiteralPrefix(param, "0B")) {
|
|
base = 2;
|
|
param += 2;
|
|
} else if (cmHasLiteralPrefix(param, "-0b") ||
|
|
cmHasLiteralPrefix(param, "-0B") ||
|
|
cmHasLiteralPrefix(param, "+0b") ||
|
|
cmHasLiteralPrefix(param, "+0B")) {
|
|
base = 2;
|
|
param += 3;
|
|
}
|
|
|
|
char* pEnd;
|
|
long result = strtol(param, &pEnd, base);
|
|
if (pEnd == param || *pEnd != '\0' || errno == ERANGE) {
|
|
return false;
|
|
}
|
|
if (isNegative && result > 0) {
|
|
result *= -1;
|
|
}
|
|
*outResult = result;
|
|
return true;
|
|
}
|
|
} equalNode;
|
|
|
|
static const struct InListNode : public cmGeneratorExpressionNode
|
|
{
|
|
InListNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 2; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
cmList values;
|
|
cmList checkValues;
|
|
bool check = false;
|
|
switch (context->LG->GetPolicyStatus(cmPolicies::CMP0085)) {
|
|
case cmPolicies::WARN:
|
|
if (parameters.front().empty()) {
|
|
check = true;
|
|
checkValues.assign(parameters[1], cmList::EmptyElements::Yes);
|
|
}
|
|
CM_FALLTHROUGH;
|
|
case cmPolicies::OLD:
|
|
values.assign(parameters[1]);
|
|
if (check && values != checkValues) {
|
|
std::ostringstream e;
|
|
e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0085)
|
|
<< "\nSearch Item:\n \"" << parameters.front()
|
|
<< "\"\nList:\n \"" << parameters[1] << "\"\n";
|
|
context->LG->GetCMakeInstance()->IssueMessage(
|
|
MessageType ::AUTHOR_WARNING, e.str(), context->Backtrace);
|
|
return "0";
|
|
}
|
|
if (values.empty()) {
|
|
return "0";
|
|
}
|
|
break;
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
case cmPolicies::REQUIRED_ALWAYS:
|
|
case cmPolicies::NEW:
|
|
values.assign(parameters[1], cmList::EmptyElements::Yes);
|
|
break;
|
|
}
|
|
|
|
return values.find(parameters.front()) != cmList::npos ? "1" : "0";
|
|
}
|
|
} inListNode;
|
|
|
|
static const struct FilterNode : public cmGeneratorExpressionNode
|
|
{
|
|
FilterNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 3; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
if (parameters.size() != 3) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<FILTER:...> expression requires three parameters");
|
|
return {};
|
|
}
|
|
|
|
if (parameters[1] != "INCLUDE" && parameters[1] != "EXCLUDE") {
|
|
reportError(
|
|
context, 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(context, 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(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
if (parameters.size() != 1) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<REMOVE_DUPLICATES:...> expression requires one parameter");
|
|
}
|
|
|
|
return cmList{ parameters.front() }.remove_duplicates().to_string();
|
|
}
|
|
|
|
} removeDuplicatesNode;
|
|
|
|
static const struct TargetExistsNode : public cmGeneratorExpressionNode
|
|
{
|
|
TargetExistsNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 1; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
if (parameters.size() != 1) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<TARGET_EXISTS:...> expression requires one parameter");
|
|
return std::string();
|
|
}
|
|
|
|
std::string const& targetName = parameters.front();
|
|
if (targetName.empty() ||
|
|
!cmGeneratorExpression::IsValidTargetName(targetName)) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<TARGET_EXISTS:tgt> expression requires a non-empty "
|
|
"valid target name.");
|
|
return std::string();
|
|
}
|
|
|
|
return context->LG->GetMakefile()->FindTargetToUse(targetName) ? "1" : "0";
|
|
}
|
|
} targetExistsNode;
|
|
|
|
static const struct TargetNameIfExistsNode : public cmGeneratorExpressionNode
|
|
{
|
|
TargetNameIfExistsNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 1; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
if (parameters.size() != 1) {
|
|
reportError(context, 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(context, content->GetOriginalExpression(),
|
|
"$<TARGET_NAME_IF_EXISTS:tgt> expression requires a "
|
|
"non-empty valid target name.");
|
|
return std::string();
|
|
}
|
|
|
|
return context->LG->GetMakefile()->FindTargetToUse(targetName)
|
|
? targetName
|
|
: std::string();
|
|
}
|
|
} targetNameIfExistsNode;
|
|
|
|
struct GenexEvaluator : public cmGeneratorExpressionNode
|
|
{
|
|
GenexEvaluator() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
protected:
|
|
std::string EvaluateExpression(
|
|
const std::string& genexOperator, const std::string& expression,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagCheckerParent) const
|
|
{
|
|
if (context->HeadTarget) {
|
|
cmGeneratorExpressionDAGChecker dagChecker(
|
|
context->Backtrace, context->HeadTarget,
|
|
genexOperator + ":" + expression, content, dagCheckerParent);
|
|
switch (dagChecker.Check()) {
|
|
case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
|
|
case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE: {
|
|
dagChecker.ReportError(context, content->GetOriginalExpression());
|
|
return std::string();
|
|
}
|
|
case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
|
|
case cmGeneratorExpressionDAGChecker::DAG:
|
|
break;
|
|
}
|
|
|
|
return this->EvaluateDependentExpression(
|
|
expression, context->LG, context, context->HeadTarget, &dagChecker,
|
|
context->CurrentTarget);
|
|
}
|
|
|
|
return this->EvaluateDependentExpression(
|
|
expression, context->LG, context, context->HeadTarget, dagCheckerParent,
|
|
context->CurrentTarget);
|
|
}
|
|
};
|
|
|
|
static const struct TargetGenexEvalNode : public GenexEvaluator
|
|
{
|
|
TargetGenexEvalNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 2; }
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
|
|
{
|
|
const std::string& targetName = parameters.front();
|
|
if (targetName.empty() ||
|
|
!cmGeneratorExpression::IsValidTargetName(targetName)) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<TARGET_GENEX_EVAL:tgt, ...> expression requires a "
|
|
"non-empty valid target name.");
|
|
return std::string();
|
|
}
|
|
|
|
const auto* target = context->LG->FindGeneratorTargetToUse(targetName);
|
|
if (!target) {
|
|
std::ostringstream e;
|
|
e << "$<TARGET_GENEX_EVAL:tgt, ...> target \"" << targetName
|
|
<< "\" not found.";
|
|
reportError(context, content->GetOriginalExpression(), e.str());
|
|
return std::string();
|
|
}
|
|
|
|
const std::string& expression = parameters[1];
|
|
if (expression.empty()) {
|
|
return expression;
|
|
}
|
|
|
|
cmGeneratorExpressionContext targetContext(
|
|
context->LG, context->Config, context->Quiet, target, target,
|
|
context->EvaluateForBuildsystem, context->Backtrace, context->Language);
|
|
|
|
return this->EvaluateExpression("TARGET_GENEX_EVAL", expression,
|
|
&targetContext, content, dagCheckerParent);
|
|
}
|
|
} targetGenexEvalNode;
|
|
|
|
static const struct GenexEvalNode : public GenexEvaluator
|
|
{
|
|
GenexEvalNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 1; }
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
|
|
{
|
|
const std::string& expression = parameters[0];
|
|
if (expression.empty()) {
|
|
return expression;
|
|
}
|
|
|
|
return this->EvaluateExpression("GENEX_EVAL", expression, context, content,
|
|
dagCheckerParent);
|
|
}
|
|
} genexEvalNode;
|
|
|
|
static const struct LowerCaseNode : public cmGeneratorExpressionNode
|
|
{
|
|
LowerCaseNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return cmSystemTools::LowerCase(parameters.front());
|
|
}
|
|
} lowerCaseNode;
|
|
|
|
static const struct UpperCaseNode : public cmGeneratorExpressionNode
|
|
{
|
|
UpperCaseNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return cmSystemTools::UpperCase(parameters.front());
|
|
}
|
|
} upperCaseNode;
|
|
|
|
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(const Container& container)
|
|
: Base(container.begin(), container.end())
|
|
{
|
|
}
|
|
|
|
const_reference operator[](size_type pos) const
|
|
{
|
|
return *(this->begin() + pos);
|
|
}
|
|
|
|
const_reference front() const { return *this->begin(); }
|
|
const_reference back() const { return *std::prev(this->end()); }
|
|
|
|
Range& advance(difference_type amount) &
|
|
{
|
|
Base::advance(amount);
|
|
return *this;
|
|
}
|
|
Range advance(difference_type amount) &&
|
|
{
|
|
Base::advance(amount);
|
|
return std::move(*this);
|
|
}
|
|
};
|
|
|
|
using Arguments = Range<std::vector<std::string>>;
|
|
|
|
bool CheckGenExParameters(cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
cm::string_view genex, cm::string_view option,
|
|
std::size_t count, int required = 1,
|
|
bool exactly = true)
|
|
{
|
|
if (static_cast<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(std::to_string(required), " parameters");
|
|
}
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("$<", genex, ':', option, "> expression requires ",
|
|
(exactly ? "exactly" : "at least"), ' ', nbParameters,
|
|
'.'));
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
bool CheckPathParametersEx(cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
cm::string_view option, std::size_t count,
|
|
int required = 1, bool exactly = true)
|
|
{
|
|
return CheckGenExParameters(ctx, cnt, "PATH"_s, option, count, required,
|
|
exactly);
|
|
}
|
|
bool CheckPathParameters(cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
cm::string_view option, const Arguments& args,
|
|
int required = 1)
|
|
{
|
|
return CheckPathParametersEx(ctx, cnt, option, args.size(), required);
|
|
};
|
|
|
|
std::string ToString(bool isTrue)
|
|
{
|
|
return isTrue ? "1" : "0";
|
|
};
|
|
}
|
|
|
|
static const struct PathNode : public cmGeneratorExpressionNode
|
|
{
|
|
PathNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return TwoOrMoreParameters; }
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* 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(cmGeneratorExpressionContext*,
|
|
const GeneratorExpressionContent*,
|
|
Arguments&)>>
|
|
pathCommands{
|
|
{ "GET_ROOT_NAME"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParameters(ctx, cnt, "GET_ROOT_NAME"_s, args) &&
|
|
!args.front().empty()) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.GetRootName().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "GET_ROOT_DIRECTORY"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParameters(ctx, cnt, "GET_ROOT_DIRECTORY"_s, args) &&
|
|
!args.front().empty()) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.GetRootDirectory().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "GET_ROOT_PATH"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParameters(ctx, cnt, "GET_ROOT_PATH"_s, args) &&
|
|
!args.front().empty()) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.GetRootPath().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "GET_FILENAME"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParameters(ctx, cnt, "GET_FILENAME"_s, args) &&
|
|
!args.front().empty()) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.GetFileName().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "GET_EXTENSION"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
bool lastOnly = args.front() == "LAST_ONLY"_s;
|
|
if (lastOnly) {
|
|
args.advance(1);
|
|
}
|
|
if (CheckPathParametersEx(ctx, cnt,
|
|
lastOnly ? "GET_EXTENSION,LAST_ONLY"_s
|
|
: "GET_EXTENSION"_s,
|
|
args.size())) {
|
|
if (args.front().empty()) {
|
|
return std::string{};
|
|
}
|
|
if (lastOnly) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.GetExtension().String();
|
|
});
|
|
}
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.GetWideExtension().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "GET_STEM"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
bool lastOnly = args.front() == "LAST_ONLY"_s;
|
|
if (lastOnly) {
|
|
args.advance(1);
|
|
}
|
|
if (CheckPathParametersEx(
|
|
ctx, cnt, lastOnly ? "GET_STEM,LAST_ONLY"_s : "GET_STEM"_s,
|
|
args.size())) {
|
|
if (args.front().empty()) {
|
|
return std::string{};
|
|
}
|
|
if (lastOnly) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.GetStem().String();
|
|
});
|
|
}
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.GetNarrowStem().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "GET_RELATIVE_PART"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParameters(ctx, cnt, "GET_RELATIVE_PART"_s, args) &&
|
|
!args.front().empty()) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.GetRelativePath().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "GET_PARENT_PATH"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParameters(ctx, cnt, "GET_PARENT_PATH"_s, args)) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.GetParentPath().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "HAS_ROOT_NAME"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
return CheckPathParameters(ctx, cnt, "HAS_ROOT_NAME"_s, args)
|
|
? ToString(cmCMakePath{ args.front() }.HasRootName())
|
|
: std::string{ "0" };
|
|
} },
|
|
{ "HAS_ROOT_DIRECTORY"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
return CheckPathParameters(ctx, cnt, "HAS_ROOT_DIRECTORY"_s, args)
|
|
? ToString(cmCMakePath{ args.front() }.HasRootDirectory())
|
|
: std::string{ "0" };
|
|
} },
|
|
{ "HAS_ROOT_PATH"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
return CheckPathParameters(ctx, cnt, "HAS_ROOT_PATH"_s, args)
|
|
? ToString(cmCMakePath{ args.front() }.HasRootPath())
|
|
: std::string{ "0" };
|
|
} },
|
|
{ "HAS_FILENAME"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
return CheckPathParameters(ctx, cnt, "HAS_FILENAME"_s, args)
|
|
? ToString(cmCMakePath{ args.front() }.HasFileName())
|
|
: std::string{ "0" };
|
|
} },
|
|
{ "HAS_EXTENSION"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
return CheckPathParameters(ctx, cnt, "HAS_EXTENSION"_s, args) &&
|
|
!args.front().empty()
|
|
? ToString(cmCMakePath{ args.front() }.HasExtension())
|
|
: std::string{ "0" };
|
|
} },
|
|
{ "HAS_STEM"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
return CheckPathParameters(ctx, cnt, "HAS_STEM"_s, args)
|
|
? ToString(cmCMakePath{ args.front() }.HasStem())
|
|
: std::string{ "0" };
|
|
} },
|
|
{ "HAS_RELATIVE_PART"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
return CheckPathParameters(ctx, cnt, "HAS_RELATIVE_PART"_s, args)
|
|
? ToString(cmCMakePath{ args.front() }.HasRelativePath())
|
|
: std::string{ "0" };
|
|
} },
|
|
{ "HAS_PARENT_PATH"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
return CheckPathParameters(ctx, cnt, "HAS_PARENT_PATH"_s, args)
|
|
? ToString(cmCMakePath{ args.front() }.HasParentPath())
|
|
: std::string{ "0" };
|
|
} },
|
|
{ "IS_ABSOLUTE"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
return CheckPathParameters(ctx, cnt, "IS_ABSOLUTE"_s, args)
|
|
? ToString(cmCMakePath{ args.front() }.IsAbsolute())
|
|
: std::string{ "0" };
|
|
} },
|
|
{ "IS_RELATIVE"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
return CheckPathParameters(ctx, cnt, "IS_RELATIVE"_s, args)
|
|
? ToString(cmCMakePath{ args.front() }.IsRelative())
|
|
: std::string{ "0" };
|
|
} },
|
|
{ "IS_PREFIX"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
bool normalize = args.front() == "NORMALIZE"_s;
|
|
if (normalize) {
|
|
args.advance(1);
|
|
}
|
|
if (CheckPathParametersEx(ctx, cnt,
|
|
normalize ? "IS_PREFIX,NORMALIZE"_s
|
|
: "IS_PREFIX"_s,
|
|
args.size(), 2)) {
|
|
if (normalize) {
|
|
return ToString(cmCMakePath{ args[0] }.Normal().IsPrefix(
|
|
cmCMakePath{ args[1] }.Normal()));
|
|
}
|
|
return ToString(
|
|
cmCMakePath{ args[0] }.IsPrefix(cmCMakePath{ args[1] }));
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "CMAKE_PATH"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
bool normalize = args.front() == "NORMALIZE"_s;
|
|
if (normalize) {
|
|
args.advance(1);
|
|
}
|
|
if (CheckPathParametersEx(ctx, cnt,
|
|
normalize ? "CMAKE_PATH,NORMALIZE"_s
|
|
: "CMAKE_PATH"_s,
|
|
args.size(), 1)) {
|
|
return processList(
|
|
args.front(), [normalize](std::string& value) {
|
|
auto path = cmCMakePath{ value, cmCMakePath::auto_format };
|
|
value = normalize ? path.Normal().GenericString()
|
|
: path.GenericString();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "APPEND"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParametersEx(ctx, cnt, "APPEND"_s, args.size(), 1,
|
|
false)) {
|
|
auto const& list = args.front();
|
|
args.advance(1);
|
|
|
|
return processList(list, [&args](std::string& value) {
|
|
cmCMakePath path{ value };
|
|
for (const auto& p : args) {
|
|
path /= p;
|
|
}
|
|
value = path.String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "REMOVE_FILENAME"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParameters(ctx, cnt, "REMOVE_FILENAME"_s, args) &&
|
|
!args.front().empty()) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.RemoveFileName().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "REPLACE_FILENAME"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParameters(ctx, cnt, "REPLACE_FILENAME"_s, args, 2)) {
|
|
return processList(args.front(), [&args](std::string& value) {
|
|
value = cmCMakePath{ value }
|
|
.ReplaceFileName(cmCMakePath{ args[1] })
|
|
.String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "REMOVE_EXTENSION"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
bool lastOnly = args.front() == "LAST_ONLY"_s;
|
|
if (lastOnly) {
|
|
args.advance(1);
|
|
}
|
|
if (CheckPathParametersEx(ctx, cnt,
|
|
lastOnly ? "REMOVE_EXTENSION,LAST_ONLY"_s
|
|
: "REMOVE_EXTENSION"_s,
|
|
args.size())) {
|
|
if (args.front().empty()) {
|
|
return std::string{};
|
|
}
|
|
if (lastOnly) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.RemoveExtension().String();
|
|
});
|
|
}
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.RemoveWideExtension().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "REPLACE_EXTENSION"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
bool lastOnly = args.front() == "LAST_ONLY"_s;
|
|
if (lastOnly) {
|
|
args.advance(1);
|
|
}
|
|
if (CheckPathParametersEx(ctx, cnt,
|
|
lastOnly
|
|
? "REPLACE_EXTENSION,LAST_ONLY"_s
|
|
: "REPLACE_EXTENSION"_s,
|
|
args.size(), 2)) {
|
|
if (lastOnly) {
|
|
return processList(args.front(), [&args](std::string& value) {
|
|
value = cmCMakePath{ value }
|
|
.ReplaceExtension(cmCMakePath{ args[1] })
|
|
.String();
|
|
});
|
|
}
|
|
return processList(args.front(), [&args](std::string& value) {
|
|
value = cmCMakePath{ value }
|
|
.ReplaceWideExtension(cmCMakePath{ args[1] })
|
|
.String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "NORMAL_PATH"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParameters(ctx, cnt, "NORMAL_PATH"_s, args) &&
|
|
!args.front().empty()) {
|
|
return processList(args.front(), [](std::string& value) {
|
|
value = cmCMakePath{ value }.Normal().String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "RELATIVE_PATH"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckPathParameters(ctx, cnt, "RELATIVE_PATH"_s, args, 2)) {
|
|
return processList(args.front(), [&args](std::string& value) {
|
|
value = cmCMakePath{ value }.Relative(args[1]).String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "ABSOLUTE_PATH"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
bool normalize = args.front() == "NORMALIZE"_s;
|
|
if (normalize) {
|
|
args.advance(1);
|
|
}
|
|
if (CheckPathParametersEx(ctx, cnt,
|
|
normalize ? "ABSOLUTE_PATH,NORMALIZE"_s
|
|
: "ABSOLUTE_PATH"_s,
|
|
args.size(), 2)) {
|
|
return processList(
|
|
args.front(), [&args, normalize](std::string& value) {
|
|
auto path = cmCMakePath{ value }.Absolute(args[1]);
|
|
value = normalize ? path.Normal().String() : path.String();
|
|
});
|
|
}
|
|
return std::string{};
|
|
} }
|
|
};
|
|
|
|
if (cm::contains(pathCommands, parameters.front())) {
|
|
auto args = Arguments{ parameters }.advance(1);
|
|
return pathCommands[parameters.front()](context, content, args);
|
|
}
|
|
|
|
reportError(context, content->GetOriginalExpression(),
|
|
cmStrCat(parameters.front(), ": invalid option."));
|
|
return std::string{};
|
|
}
|
|
} pathNode;
|
|
|
|
static const struct PathEqualNode : public cmGeneratorExpressionNode
|
|
{
|
|
PathEqualNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 2; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return cmCMakePath{ parameters[0] } == cmCMakePath{ parameters[1] } ? "1"
|
|
: "0";
|
|
}
|
|
} pathEqualNode;
|
|
|
|
namespace {
|
|
inline bool CheckListParametersEx(cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
cm::string_view option, std::size_t count,
|
|
int required = 1, bool exactly = true)
|
|
{
|
|
return CheckGenExParameters(ctx, cnt, "LIST"_s, option, count, required,
|
|
exactly);
|
|
}
|
|
inline bool CheckListParameters(cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
cm::string_view option, const Arguments& args,
|
|
int required = 1)
|
|
{
|
|
return CheckListParametersEx(ctx, cnt, option, args.size(), required);
|
|
};
|
|
|
|
inline cmList GetList(std::string const& list)
|
|
{
|
|
return list.empty() ? cmList{} : cmList{ list, cmList::EmptyElements::Yes };
|
|
}
|
|
|
|
bool GetNumericArgument(const std::string& arg, cmList::index_type& value)
|
|
{
|
|
try {
|
|
std::size_t pos;
|
|
|
|
if (sizeof(cmList::index_type) == sizeof(long)) {
|
|
value = std::stol(arg, &pos);
|
|
} else {
|
|
value = std::stoll(arg, &pos);
|
|
}
|
|
|
|
if (pos != arg.length()) {
|
|
// this is not a number
|
|
return false;
|
|
}
|
|
} catch (const std::invalid_argument&) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GetNumericArguments(
|
|
cmGeneratorExpressionContext* ctx, const GeneratorExpressionContent* cnt,
|
|
Arguments const& args, std::vector<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(ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("index: \"", value, "\" is not a valid index"));
|
|
return false;
|
|
}
|
|
indexes.push_back(index);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
static const struct ListNode : public cmGeneratorExpressionNode
|
|
{
|
|
ListNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return TwoOrMoreParameters; }
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
static std::unordered_map<
|
|
cm::string_view,
|
|
std::function<std::string(cmGeneratorExpressionContext*,
|
|
const GeneratorExpressionContent*,
|
|
Arguments&)>>
|
|
listCommands{
|
|
{ "LENGTH"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParameters(ctx, cnt, "LENGTH"_s, args)) {
|
|
return std::to_string(GetList(args.front()).size());
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "GET"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParametersEx(ctx, cnt, "GET"_s, args.size(), 2,
|
|
false)) {
|
|
auto list = GetList(args.front());
|
|
if (list.empty()) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
"given empty list");
|
|
return std::string{};
|
|
}
|
|
|
|
std::vector<cmList::index_type> indexes;
|
|
if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes,
|
|
cmList::ExpandElements::Yes)) {
|
|
return std::string{};
|
|
}
|
|
try {
|
|
return list.get_items(indexes.begin(), indexes.end())
|
|
.to_string();
|
|
} catch (std::out_of_range& e) {
|
|
reportError(ctx, cnt->GetOriginalExpression(), e.what());
|
|
return std::string{};
|
|
}
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "JOIN"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParameters(ctx, cnt, "JOIN"_s, args, 2)) {
|
|
return GetList(args.front()).join(args[1]);
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "SUBLIST"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParameters(ctx, cnt, "SUBLIST"_s, args, 3)) {
|
|
auto list = GetList(args.front());
|
|
if (!list.empty()) {
|
|
std::vector<cmList::index_type> indexes;
|
|
if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes)) {
|
|
return std::string{};
|
|
}
|
|
if (indexes[0] < 0) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("begin index: ", indexes[0],
|
|
" is out of range 0 - ",
|
|
list.size() - 1));
|
|
return std::string{};
|
|
}
|
|
if (indexes[1] < -1) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("length: ", indexes[1],
|
|
" should be -1 or greater"));
|
|
return std::string{};
|
|
}
|
|
try {
|
|
return list
|
|
.sublist(static_cast<cmList::size_type>(indexes[0]),
|
|
static_cast<cmList::size_type>(indexes[1]))
|
|
.to_string();
|
|
} catch (std::out_of_range& e) {
|
|
reportError(ctx, cnt->GetOriginalExpression(), e.what());
|
|
return std::string{};
|
|
}
|
|
}
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "FIND"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParameters(ctx, cnt, "FIND"_s, args, 2)) {
|
|
auto list = GetList(args.front());
|
|
auto index = list.find(args[1]);
|
|
return index == cmList::npos ? "-1" : std::to_string(index);
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "APPEND"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParametersEx(ctx, cnt, "APPEND"_s, args.size(), 2,
|
|
false)) {
|
|
auto list = args.front();
|
|
args.advance(1);
|
|
return cmList::append(list, args.begin(), args.end());
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "PREPEND"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParametersEx(ctx, cnt, "PREPEND"_s, args.size(), 2,
|
|
false)) {
|
|
auto list = args.front();
|
|
args.advance(1);
|
|
return cmList::prepend(list, args.begin(), args.end());
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "INSERT"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParametersEx(ctx, cnt, "INSERT"_s, args.size(), 3,
|
|
false)) {
|
|
cmList::index_type index;
|
|
if (!GetNumericArgument(args[1], index)) {
|
|
reportError(
|
|
ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("index: \"", args[1], "\" is not a valid index"));
|
|
return std::string{};
|
|
}
|
|
try {
|
|
auto list = GetList(args.front());
|
|
args.advance(2);
|
|
list.insert_items(index, args.begin(), args.end());
|
|
return list.to_string();
|
|
} catch (std::out_of_range& e) {
|
|
reportError(ctx, cnt->GetOriginalExpression(), e.what());
|
|
return std::string{};
|
|
}
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "POP_BACK"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParameters(ctx, cnt, "POP_BACK"_s, args)) {
|
|
auto list = GetList(args.front());
|
|
if (!list.empty()) {
|
|
list.pop_back();
|
|
return list.to_string();
|
|
}
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "POP_FRONT"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParameters(ctx, cnt, "POP_FRONT"_s, args)) {
|
|
auto list = GetList(args.front());
|
|
if (!list.empty()) {
|
|
list.pop_front();
|
|
return list.to_string();
|
|
}
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "REMOVE_DUPLICATES"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParameters(ctx, cnt, "REMOVE_DUPLICATES"_s, args)) {
|
|
return GetList(args.front()).remove_duplicates().to_string();
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "REMOVE_ITEM"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParametersEx(ctx, cnt, "REMOVE_ITEM"_s, args.size(),
|
|
2, false)) {
|
|
auto list = GetList(args.front());
|
|
args.advance(1);
|
|
cmList items{ args.begin(), args.end(),
|
|
cmList::ExpandElements::Yes };
|
|
return list.remove_items(items.begin(), items.end()).to_string();
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "REMOVE_AT"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParametersEx(ctx, cnt, "REMOVE_AT"_s, args.size(), 2,
|
|
false)) {
|
|
auto list = GetList(args.front());
|
|
std::vector<cmList::index_type> indexes;
|
|
if (!GetNumericArguments(ctx, cnt, args.advance(1), indexes,
|
|
cmList::ExpandElements::Yes)) {
|
|
return std::string{};
|
|
}
|
|
try {
|
|
return list.remove_items(indexes.begin(), indexes.end())
|
|
.to_string();
|
|
} catch (std::out_of_range& e) {
|
|
reportError(ctx, cnt->GetOriginalExpression(), e.what());
|
|
return std::string{};
|
|
}
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "FILTER"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParameters(ctx, cnt, "FILTER"_s, args, 3)) {
|
|
auto const& op = args[1];
|
|
if (op != "INCLUDE"_s && op != "EXCLUDE"_s) {
|
|
reportError(
|
|
ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("sub-command FILTER does not recognize operator \"",
|
|
op, "\". It must be either INCLUDE or EXCLUDE."));
|
|
return std::string{};
|
|
}
|
|
try {
|
|
return GetList(args.front())
|
|
.filter(args[2],
|
|
op == "INCLUDE"_s ? cmList::FilterMode::INCLUDE
|
|
: cmList::FilterMode::EXCLUDE)
|
|
.to_string();
|
|
} catch (std::invalid_argument&) {
|
|
reportError(
|
|
ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("sub-command FILTER, failed to compile regex \"",
|
|
args[2], "\"."));
|
|
return std::string{};
|
|
}
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "TRANSFORM"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParametersEx(ctx, cnt, "TRANSFORM"_s, args.size(), 2,
|
|
false)) {
|
|
auto list = GetList(args.front());
|
|
if (!list.empty()) {
|
|
struct ActionDescriptor
|
|
{
|
|
ActionDescriptor(std::string name)
|
|
: Name(std::move(name))
|
|
{
|
|
}
|
|
ActionDescriptor(std::string name,
|
|
cmList::TransformAction action, int arity)
|
|
: Name(std::move(name))
|
|
, Action(action)
|
|
, Arity(arity)
|
|
{
|
|
}
|
|
|
|
operator const std::string&() const { return this->Name; }
|
|
|
|
std::string Name;
|
|
cmList::TransformAction Action;
|
|
int Arity = 0;
|
|
};
|
|
|
|
static std::set<
|
|
ActionDescriptor,
|
|
std::function<bool(const std::string&, const std::string&)>>
|
|
descriptors{
|
|
{ { "APPEND", cmList::TransformAction::APPEND, 1 },
|
|
{ "PREPEND", cmList::TransformAction::PREPEND, 1 },
|
|
{ "TOUPPER", cmList::TransformAction::TOUPPER, 0 },
|
|
{ "TOLOWER", cmList::TransformAction::TOLOWER, 0 },
|
|
{ "STRIP", cmList::TransformAction::STRIP, 0 },
|
|
{ "REPLACE", cmList::TransformAction::REPLACE, 2 } },
|
|
[](const std::string& x, const std::string& y) {
|
|
return x < y;
|
|
}
|
|
};
|
|
|
|
auto descriptor = descriptors.find(args.advance(1).front());
|
|
if (descriptor == descriptors.end()) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat(" sub-command TRANSFORM, ",
|
|
args.front(), " invalid action."));
|
|
return std::string{};
|
|
}
|
|
|
|
// Action arguments
|
|
args.advance(1);
|
|
if (args.size() < descriptor->Arity) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("sub-command TRANSFORM, action ",
|
|
descriptor->Name, " expects ",
|
|
descriptor->Arity, " argument(s)."));
|
|
return std::string{};
|
|
}
|
|
std::vector<std::string> arguments;
|
|
if (descriptor->Arity > 0) {
|
|
arguments = std::vector<std::string>(
|
|
args.begin(), args.begin() + descriptor->Arity);
|
|
args.advance(descriptor->Arity);
|
|
}
|
|
|
|
const std::string REGEX{ "REGEX" };
|
|
const std::string AT{ "AT" };
|
|
const std::string FOR{ "FOR" };
|
|
std::unique_ptr<cmList::TransformSelector> selector;
|
|
|
|
try {
|
|
// handle optional arguments
|
|
while (!args.empty()) {
|
|
if ((args.front() == REGEX || args.front() == AT ||
|
|
args.front() == FOR) &&
|
|
selector) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("sub-command TRANSFORM, selector "
|
|
"already specified (",
|
|
selector->GetTag(), ")."));
|
|
|
|
return std::string{};
|
|
}
|
|
|
|
// REGEX selector
|
|
if (args.front() == REGEX) {
|
|
if (args.advance(1).empty()) {
|
|
reportError(
|
|
ctx, cnt->GetOriginalExpression(),
|
|
"sub-command TRANSFORM, selector REGEX expects "
|
|
"'regular expression' argument.");
|
|
return std::string{};
|
|
}
|
|
|
|
selector = cmList::TransformSelector::New<
|
|
cmList::TransformSelector::REGEX>(args.front());
|
|
|
|
args.advance(1);
|
|
continue;
|
|
}
|
|
|
|
// AT selector
|
|
if (args.front() == AT) {
|
|
args.advance(1);
|
|
// get all specified indexes
|
|
std::vector<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(
|
|
ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("sub-command TRANSFORM, selector AT: '",
|
|
index, "': unexpected argument."));
|
|
return std::string{};
|
|
}
|
|
indexes.push_back(value);
|
|
}
|
|
args.advance(1);
|
|
}
|
|
|
|
if (indexes.empty()) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
"sub-command TRANSFORM, selector AT "
|
|
"expects at least one "
|
|
"numeric value.");
|
|
return std::string{};
|
|
}
|
|
|
|
selector = cmList::TransformSelector::New<
|
|
cmList::TransformSelector::AT>(std::move(indexes));
|
|
|
|
continue;
|
|
}
|
|
|
|
// FOR selector
|
|
if (args.front() == FOR) {
|
|
if (args.advance(1).size() < 2) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
"sub-command TRANSFORM, selector FOR "
|
|
"expects, at least,"
|
|
" two arguments.");
|
|
return std::string{};
|
|
}
|
|
|
|
cmList::index_type start = 0;
|
|
cmList::index_type stop = 0;
|
|
cmList::index_type step = 1;
|
|
bool valid = false;
|
|
|
|
if (GetNumericArgument(args.front(), start) &&
|
|
GetNumericArgument(args.advance(1).front(), stop)) {
|
|
valid = true;
|
|
}
|
|
|
|
if (!valid) {
|
|
reportError(
|
|
ctx, cnt->GetOriginalExpression(),
|
|
"sub-command TRANSFORM, selector FOR expects, "
|
|
"at least, two numeric values.");
|
|
return std::string{};
|
|
}
|
|
// try to read a third numeric value for step
|
|
if (!args.advance(1).empty()) {
|
|
if (!GetNumericArgument(args.front(), step)) {
|
|
// this is not a number
|
|
step = -1;
|
|
}
|
|
args.advance(1);
|
|
}
|
|
|
|
if (step <= 0) {
|
|
reportError(
|
|
ctx, cnt->GetOriginalExpression(),
|
|
"sub-command TRANSFORM, selector FOR expects "
|
|
"positive numeric value for <step>.");
|
|
return std::string{};
|
|
}
|
|
|
|
selector = cmList::TransformSelector::New<
|
|
cmList::TransformSelector::FOR>({ start, stop, step });
|
|
continue;
|
|
}
|
|
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("sub-command TRANSFORM, '",
|
|
cmJoin(args, ", "),
|
|
"': unexpected argument(s)."));
|
|
return std::string{};
|
|
}
|
|
|
|
return list
|
|
.transform(descriptor->Action, arguments,
|
|
std::move(selector))
|
|
.to_string();
|
|
} catch (cmList::transform_error& e) {
|
|
reportError(ctx, cnt->GetOriginalExpression(), e.what());
|
|
return std::string{};
|
|
}
|
|
}
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "REVERSE"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParameters(ctx, cnt, "REVERSE"_s, args)) {
|
|
return GetList(args.front()).reverse().to_string();
|
|
}
|
|
return std::string{};
|
|
} },
|
|
{ "SORT"_s,
|
|
[](cmGeneratorExpressionContext* ctx,
|
|
const GeneratorExpressionContent* cnt,
|
|
Arguments& args) -> std::string {
|
|
if (CheckListParametersEx(ctx, cnt, "SORT"_s, args.size(), 1,
|
|
false)) {
|
|
auto list = GetList(args.front());
|
|
args.advance(1);
|
|
const auto COMPARE = "COMPARE:"_s;
|
|
const auto CASE = "CASE:"_s;
|
|
const auto ORDER = "ORDER:"_s;
|
|
using SortConfig = cmList::SortConfiguration;
|
|
SortConfig sortConfig;
|
|
for (auto const& arg : args) {
|
|
if (cmHasPrefix(arg, COMPARE)) {
|
|
if (sortConfig.Compare !=
|
|
SortConfig::CompareMethod::DEFAULT) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
"sub-command SORT, COMPARE option has been "
|
|
"specified multiple times.");
|
|
return std::string{};
|
|
}
|
|
auto option =
|
|
cm::string_view{ arg.c_str() + COMPARE.length() };
|
|
if (option == "STRING"_s) {
|
|
sortConfig.Compare = SortConfig::CompareMethod::STRING;
|
|
continue;
|
|
}
|
|
if (option == "FILE_BASENAME"_s) {
|
|
sortConfig.Compare =
|
|
SortConfig::CompareMethod::FILE_BASENAME;
|
|
continue;
|
|
}
|
|
if (option == "NATURAL"_s) {
|
|
sortConfig.Compare = SortConfig::CompareMethod::NATURAL;
|
|
continue;
|
|
}
|
|
reportError(
|
|
ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat(
|
|
"sub-command SORT, an invalid COMPARE option has been "
|
|
"specified: \"",
|
|
option, "\"."));
|
|
return std::string{};
|
|
}
|
|
if (cmHasPrefix(arg, CASE)) {
|
|
if (sortConfig.Case !=
|
|
SortConfig::CaseSensitivity::DEFAULT) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
"sub-command SORT, CASE option has been "
|
|
"specified multiple times.");
|
|
return std::string{};
|
|
}
|
|
auto option = cm::string_view{ arg.c_str() + CASE.length() };
|
|
if (option == "SENSITIVE"_s) {
|
|
sortConfig.Case = SortConfig::CaseSensitivity::SENSITIVE;
|
|
continue;
|
|
}
|
|
if (option == "INSENSITIVE"_s) {
|
|
sortConfig.Case = SortConfig::CaseSensitivity::INSENSITIVE;
|
|
continue;
|
|
}
|
|
reportError(
|
|
ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat(
|
|
"sub-command SORT, an invalid CASE option has been "
|
|
"specified: \"",
|
|
option, "\"."));
|
|
return std::string{};
|
|
}
|
|
if (cmHasPrefix(arg, ORDER)) {
|
|
if (sortConfig.Order != SortConfig::OrderMode::DEFAULT) {
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
"sub-command SORT, ORDER option has been "
|
|
"specified multiple times.");
|
|
return std::string{};
|
|
}
|
|
auto option =
|
|
cm::string_view{ arg.c_str() + ORDER.length() };
|
|
if (option == "ASCENDING"_s) {
|
|
sortConfig.Order = SortConfig::OrderMode::ASCENDING;
|
|
continue;
|
|
}
|
|
if (option == "DESCENDING"_s) {
|
|
sortConfig.Order = SortConfig::OrderMode::DESCENDING;
|
|
continue;
|
|
}
|
|
reportError(
|
|
ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat(
|
|
"sub-command SORT, an invalid ORDER option has been "
|
|
"specified: \"",
|
|
option, "\"."));
|
|
return std::string{};
|
|
}
|
|
reportError(ctx, cnt->GetOriginalExpression(),
|
|
cmStrCat("sub-command SORT, option \"", arg,
|
|
"\" is invalid."));
|
|
return std::string{};
|
|
}
|
|
|
|
return list.sort(sortConfig).to_string();
|
|
}
|
|
return std::string{};
|
|
} }
|
|
};
|
|
|
|
if (cm::contains(listCommands, parameters.front())) {
|
|
auto args = Arguments{ parameters }.advance(1);
|
|
return listCommands[parameters.front()](context, content, args);
|
|
}
|
|
|
|
reportError(context, content->GetOriginalExpression(),
|
|
cmStrCat(parameters.front(), ": invalid option."));
|
|
return std::string{};
|
|
}
|
|
} listNode;
|
|
|
|
static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode
|
|
{
|
|
MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*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(
|
|
const std::vector<std::string>& /*parameters*/,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return { C };
|
|
}
|
|
};
|
|
static const CharacterNode<'>'> angle_rNode;
|
|
static const CharacterNode<','> commaNode;
|
|
static const CharacterNode<';'> semicolonNode;
|
|
|
|
struct CompilerIdNode : public cmGeneratorExpressionNode
|
|
{
|
|
CompilerIdNode(const char* compilerLang)
|
|
: CompilerLanguage(compilerLang)
|
|
{
|
|
}
|
|
|
|
int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
if (!context->HeadTarget) {
|
|
std::ostringstream e;
|
|
e << "$<" << this->CompilerLanguage
|
|
<< "_COMPILER_ID> may only be used with binary targets. It may "
|
|
"not be used with add_custom_command or add_custom_target.";
|
|
reportError(context, content->GetOriginalExpression(), e.str());
|
|
return {};
|
|
}
|
|
return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
|
|
this->CompilerLanguage);
|
|
}
|
|
|
|
std::string EvaluateWithLanguage(const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*unused*/,
|
|
const std::string& lang) const
|
|
{
|
|
std::string const& compilerId =
|
|
context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
|
|
"_COMPILER_ID");
|
|
if (parameters.empty()) {
|
|
return compilerId;
|
|
}
|
|
if (compilerId.empty()) {
|
|
return parameters.front().empty() ? "1" : "0";
|
|
}
|
|
static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$");
|
|
|
|
for (auto const& param : parameters) {
|
|
|
|
if (!compilerIdValidator.find(param)) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"Expression syntax not recognized.");
|
|
return std::string();
|
|
}
|
|
|
|
if (strcmp(param.c_str(), compilerId.c_str()) == 0) {
|
|
return "1";
|
|
}
|
|
|
|
if (cmsysString_strcasecmp(param.c_str(), compilerId.c_str()) == 0) {
|
|
switch (context->LG->GetPolicyStatus(cmPolicies::CMP0044)) {
|
|
case cmPolicies::WARN: {
|
|
context->LG->GetCMakeInstance()->IssueMessage(
|
|
MessageType::AUTHOR_WARNING,
|
|
cmPolicies::GetPolicyWarning(cmPolicies::CMP0044),
|
|
context->Backtrace);
|
|
CM_FALLTHROUGH;
|
|
}
|
|
case cmPolicies::OLD:
|
|
return "1";
|
|
case cmPolicies::NEW:
|
|
case cmPolicies::REQUIRED_ALWAYS:
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return "0";
|
|
}
|
|
|
|
const char* const CompilerLanguage;
|
|
};
|
|
|
|
static const CompilerIdNode cCompilerIdNode("C"), cxxCompilerIdNode("CXX"),
|
|
cudaCompilerIdNode("CUDA"), objcCompilerIdNode("OBJC"),
|
|
objcxxCompilerIdNode("OBJCXX"), fortranCompilerIdNode("Fortran"),
|
|
hipCompilerIdNode("HIP"), ispcCompilerIdNode("ISPC");
|
|
|
|
struct CompilerVersionNode : public cmGeneratorExpressionNode
|
|
{
|
|
CompilerVersionNode(const char* compilerLang)
|
|
: CompilerLanguage(compilerLang)
|
|
{
|
|
}
|
|
|
|
int NumExpectedParameters() const override { return OneOrZeroParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
if (!context->HeadTarget) {
|
|
std::ostringstream e;
|
|
e << "$<" << this->CompilerLanguage
|
|
<< "_COMPILER_VERSION> may only be used with binary targets. It "
|
|
"may not be used with add_custom_command or add_custom_target.";
|
|
reportError(context, content->GetOriginalExpression(), e.str());
|
|
return {};
|
|
}
|
|
return this->EvaluateWithLanguage(parameters, context, content, dagChecker,
|
|
this->CompilerLanguage);
|
|
}
|
|
|
|
std::string EvaluateWithLanguage(const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*unused*/,
|
|
const std::string& lang) const
|
|
{
|
|
std::string const& compilerVersion =
|
|
context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
|
|
"_COMPILER_VERSION");
|
|
if (parameters.empty()) {
|
|
return compilerVersion;
|
|
}
|
|
|
|
static cmsys::RegularExpression compilerIdValidator("^[0-9\\.]*$");
|
|
if (!compilerIdValidator.find(parameters.front())) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"Expression syntax not recognized.");
|
|
return {};
|
|
}
|
|
if (compilerVersion.empty()) {
|
|
return parameters.front().empty() ? "1" : "0";
|
|
}
|
|
|
|
return cmSystemTools::VersionCompare(cmSystemTools::OP_EQUAL,
|
|
parameters.front(), compilerVersion)
|
|
? "1"
|
|
: "0";
|
|
}
|
|
|
|
const char* const CompilerLanguage;
|
|
};
|
|
|
|
static const CompilerVersionNode cCompilerVersionNode("C"),
|
|
cxxCompilerVersionNode("CXX"), cudaCompilerVersionNode("CUDA"),
|
|
objcCompilerVersionNode("OBJC"), objcxxCompilerVersionNode("OBJCXX"),
|
|
fortranCompilerVersionNode("Fortran"), ispcCompilerVersionNode("ISPC"),
|
|
hipCompilerVersionNode("HIP");
|
|
|
|
struct PlatformIdNode : public cmGeneratorExpressionNode
|
|
{
|
|
PlatformIdNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
std::string const& platformId =
|
|
context->LG->GetMakefile()->GetSafeDefinition("CMAKE_SYSTEM_NAME");
|
|
if (parameters.empty()) {
|
|
return platformId;
|
|
}
|
|
|
|
if (platformId.empty()) {
|
|
return parameters.front().empty() ? "1" : "0";
|
|
}
|
|
|
|
for (auto const& param : parameters) {
|
|
if (param == platformId) {
|
|
return "1";
|
|
}
|
|
}
|
|
return "0";
|
|
}
|
|
};
|
|
static struct PlatformIdNode platformIdNode;
|
|
|
|
template <cmSystemTools::CompareOp Op>
|
|
struct VersionNode : public cmGeneratorExpressionNode
|
|
{
|
|
VersionNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 2; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return cmSystemTools::VersionCompare(Op, parameters.front(), parameters[1])
|
|
? "1"
|
|
: "0";
|
|
}
|
|
};
|
|
|
|
static const VersionNode<cmSystemTools::OP_GREATER> versionGreaterNode;
|
|
static const VersionNode<cmSystemTools::OP_GREATER_EQUAL> versionGreaterEqNode;
|
|
static const VersionNode<cmSystemTools::OP_LESS> versionLessNode;
|
|
static const VersionNode<cmSystemTools::OP_LESS_EQUAL> versionLessEqNode;
|
|
static const VersionNode<cmSystemTools::OP_EQUAL> versionEqualNode;
|
|
|
|
static const struct CompileOnlyNode : public cmGeneratorExpressionNode
|
|
{
|
|
CompileOnlyNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
if (!dagChecker) {
|
|
reportError(context, 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(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
if (!dagChecker) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<LINK_ONLY:...> may only be used for linking");
|
|
return std::string();
|
|
}
|
|
if (!dagChecker->GetTransitivePropertiesOnlyCMP0131()) {
|
|
return parameters.front();
|
|
}
|
|
return std::string();
|
|
}
|
|
} linkOnlyNode;
|
|
|
|
static const struct ConfigurationNode : public cmGeneratorExpressionNode
|
|
{
|
|
ConfigurationNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 0; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& /*parameters*/,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
context->HadContextSensitiveCondition = true;
|
|
return context->Config;
|
|
}
|
|
} configurationNode;
|
|
|
|
static const struct ConfigurationTestNode : public cmGeneratorExpressionNode
|
|
{
|
|
ConfigurationTestNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
if (parameters.empty()) {
|
|
return configurationNode.Evaluate(parameters, context, content, nullptr);
|
|
}
|
|
static cmsys::RegularExpression configValidator("^[A-Za-z0-9_]*$");
|
|
if (!configValidator.find(parameters.front())) {
|
|
}
|
|
|
|
context->HadContextSensitiveCondition = true;
|
|
bool firstParam = true;
|
|
for (auto const& param : parameters) {
|
|
if (!configValidator.find(param)) {
|
|
if (firstParam) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"Expression syntax not recognized.");
|
|
return std::string();
|
|
}
|
|
// for backwards compat invalid config names are only errors as
|
|
// the first parameter
|
|
std::ostringstream e;
|
|
/* clang-format off */
|
|
e << "Warning evaluating generator expression:\n"
|
|
<< " " << content->GetOriginalExpression() << "\n"
|
|
<< "The config name of \"" << param << "\" is invalid";
|
|
/* clang-format on */
|
|
context->LG->GetCMakeInstance()->IssueMessage(
|
|
MessageType::WARNING, e.str(), context->Backtrace);
|
|
}
|
|
|
|
firstParam = false;
|
|
if (context->Config.empty()) {
|
|
if (param.empty()) {
|
|
return "1";
|
|
}
|
|
} else if (cmsysString_strcasecmp(param.c_str(),
|
|
context->Config.c_str()) == 0) {
|
|
return "1";
|
|
}
|
|
}
|
|
|
|
if (context->CurrentTarget && context->CurrentTarget->IsImported()) {
|
|
cmValue loc = nullptr;
|
|
cmValue imp = nullptr;
|
|
std::string suffix;
|
|
if (context->CurrentTarget->Target->GetMappedConfig(context->Config, loc,
|
|
imp, suffix)) {
|
|
// This imported target has an appropriate location
|
|
// for this (possibly mapped) config.
|
|
// Check if there is a proper config mapping for the tested config.
|
|
cmList mappedConfigs;
|
|
std::string mapProp = cmStrCat(
|
|
"MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(context->Config));
|
|
if (cmValue mapValue = context->CurrentTarget->GetProperty(mapProp)) {
|
|
mappedConfigs.assign(cmSystemTools::UpperCase(*mapValue));
|
|
|
|
for (auto const& param : parameters) {
|
|
if (cm::contains(mappedConfigs, cmSystemTools::UpperCase(param))) {
|
|
return "1";
|
|
}
|
|
}
|
|
} else if (!suffix.empty()) {
|
|
// There is no explicit mapping for the tested config, so use
|
|
// the configuration of the imported location that was selected.
|
|
for (auto const& param : parameters) {
|
|
if (cmStrCat('_', cmSystemTools::UpperCase(param)) == suffix) {
|
|
return "1";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return "0";
|
|
}
|
|
} configurationTestNode;
|
|
|
|
static const struct JoinNode : public cmGeneratorExpressionNode
|
|
{
|
|
JoinNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 2; }
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return cmList{ parameters.front() }.join(parameters[1]);
|
|
}
|
|
} joinNode;
|
|
|
|
static const struct CompileLanguageNode : public cmGeneratorExpressionNode
|
|
{
|
|
CompileLanguageNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
if (context->Language.empty() &&
|
|
(!dagChecker || !dagChecker->EvaluatingCompileExpression())) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<COMPILE_LANGUAGE:...> may only be used to specify include "
|
|
"directories, compile definitions, compile options, and to evaluate "
|
|
"components of the file(GENERATE) command.");
|
|
return std::string();
|
|
}
|
|
|
|
cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
|
|
std::string genName = gg->GetName();
|
|
if (genName.find("Makefiles") == std::string::npos &&
|
|
genName.find("Ninja") == std::string::npos &&
|
|
genName.find("Visual Studio") == std::string::npos &&
|
|
genName.find("Xcode") == std::string::npos &&
|
|
genName.find("Watcom WMake") == std::string::npos &&
|
|
genName.find("Green Hills MULTI") == std::string::npos) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<COMPILE_LANGUAGE:...> not supported for this generator.");
|
|
return std::string();
|
|
}
|
|
if (parameters.empty()) {
|
|
return context->Language;
|
|
}
|
|
|
|
for (auto const& param : parameters) {
|
|
if (context->Language == param) {
|
|
return "1";
|
|
}
|
|
}
|
|
return "0";
|
|
}
|
|
} languageNode;
|
|
|
|
static const struct CompileLanguageAndIdNode : public cmGeneratorExpressionNode
|
|
{
|
|
CompileLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return TwoOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
if (!context->HeadTarget ||
|
|
(context->Language.empty() &&
|
|
(!dagChecker || !dagChecker->EvaluatingCompileExpression()))) {
|
|
// reportError(context, content->GetOriginalExpression(), "");
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<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* gg = context->LG->GetGlobalGenerator();
|
|
std::string genName = gg->GetName();
|
|
if (genName.find("Makefiles") == std::string::npos &&
|
|
genName.find("Ninja") == std::string::npos &&
|
|
genName.find("Visual Studio") == std::string::npos &&
|
|
genName.find("Xcode") == std::string::npos &&
|
|
genName.find("Watcom WMake") == std::string::npos &&
|
|
genName.find("Green Hills MULTI") == std::string::npos) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<COMPILE_LANG_AND_ID:lang,id> not supported for this generator.");
|
|
return std::string();
|
|
}
|
|
|
|
const std::string& lang = context->Language;
|
|
if (lang == parameters.front()) {
|
|
std::vector<std::string> idParameter((parameters.cbegin() + 1),
|
|
parameters.cend());
|
|
return CompilerIdNode{ lang.c_str() }.EvaluateWithLanguage(
|
|
idParameter, context, content, dagChecker, lang);
|
|
}
|
|
return "0";
|
|
}
|
|
} languageAndIdNode;
|
|
|
|
static const struct LinkLanguageNode : public cmGeneratorExpressionNode
|
|
{
|
|
LinkLanguageNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
if (!context->HeadTarget || !dagChecker ||
|
|
!(dagChecker->EvaluatingLinkExpression() ||
|
|
dagChecker->EvaluatingLinkLibraries() ||
|
|
dagChecker->EvaluatingLinkerLauncher())) {
|
|
reportError(context, 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(
|
|
context, content->GetOriginalExpression(),
|
|
"$<LINK_LANGUAGE> is not supported in link libraries expression.");
|
|
return std::string();
|
|
}
|
|
|
|
cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
|
|
std::string genName = gg->GetName();
|
|
if (genName.find("Makefiles") == std::string::npos &&
|
|
genName.find("Ninja") == std::string::npos &&
|
|
genName.find("Visual Studio") == std::string::npos &&
|
|
genName.find("Xcode") == std::string::npos &&
|
|
genName.find("Watcom WMake") == std::string::npos &&
|
|
genName.find("Green Hills MULTI") == std::string::npos) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<LINK_LANGUAGE:...> not supported for this generator.");
|
|
return std::string();
|
|
}
|
|
|
|
if (dagChecker->EvaluatingLinkLibraries()) {
|
|
context->HadHeadSensitiveCondition = true;
|
|
context->HadLinkLanguageSensitiveCondition = true;
|
|
}
|
|
|
|
if (parameters.empty()) {
|
|
return context->Language;
|
|
}
|
|
|
|
for (auto const& param : parameters) {
|
|
if (context->Language == param) {
|
|
return "1";
|
|
}
|
|
}
|
|
return "0";
|
|
}
|
|
} linkLanguageNode;
|
|
|
|
namespace {
|
|
struct LinkerId
|
|
{
|
|
static std::string Evaluate(const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
const std::string& lang)
|
|
{
|
|
std::string const& linkerId =
|
|
context->LG->GetMakefile()->GetSafeDefinition("CMAKE_" + lang +
|
|
"_COMPILER_ID");
|
|
if (parameters.empty()) {
|
|
return linkerId;
|
|
}
|
|
if (linkerId.empty()) {
|
|
return parameters.front().empty() ? "1" : "0";
|
|
}
|
|
static cmsys::RegularExpression linkerIdValidator("^[A-Za-z0-9_]*$");
|
|
|
|
for (auto const& param : parameters) {
|
|
if (!linkerIdValidator.find(param)) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"Expression syntax not recognized.");
|
|
return std::string();
|
|
}
|
|
|
|
if (param == linkerId) {
|
|
return "1";
|
|
}
|
|
}
|
|
return "0";
|
|
}
|
|
};
|
|
}
|
|
|
|
static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
|
|
{
|
|
LinkLanguageAndIdNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return TwoOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
if (!context->HeadTarget || !dagChecker ||
|
|
!(dagChecker->EvaluatingLinkExpression() ||
|
|
dagChecker->EvaluatingLinkLibraries() ||
|
|
dagChecker->EvaluatingLinkerLauncher())) {
|
|
reportError(
|
|
context, 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* gg = context->LG->GetGlobalGenerator();
|
|
std::string genName = gg->GetName();
|
|
if (genName.find("Makefiles") == std::string::npos &&
|
|
genName.find("Ninja") == std::string::npos &&
|
|
genName.find("Visual Studio") == std::string::npos &&
|
|
genName.find("Xcode") == std::string::npos &&
|
|
genName.find("Watcom WMake") == std::string::npos &&
|
|
genName.find("Green Hills MULTI") == std::string::npos) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<LINK_LANG_AND_ID:lang,id> not supported for this generator.");
|
|
return std::string();
|
|
}
|
|
|
|
if (dagChecker->EvaluatingLinkLibraries()) {
|
|
context->HadHeadSensitiveCondition = true;
|
|
context->HadLinkLanguageSensitiveCondition = true;
|
|
}
|
|
|
|
const std::string& lang = context->Language;
|
|
if (lang == parameters.front()) {
|
|
std::vector<std::string> idParameter((parameters.cbegin() + 1),
|
|
parameters.cend());
|
|
return LinkerId::Evaluate(idParameter, context, content, lang);
|
|
}
|
|
return "0";
|
|
}
|
|
} linkLanguageAndIdNode;
|
|
|
|
static const struct LinkLibraryNode : public cmGeneratorExpressionNode
|
|
{
|
|
LinkLibraryNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return OneOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
using ForGenex = cmGeneratorExpressionDAGChecker::ForGenex;
|
|
|
|
if (!context->HeadTarget || !dagChecker ||
|
|
!dagChecker->EvaluatingLinkLibraries(nullptr,
|
|
ForGenex::LINK_LIBRARY)) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<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(
|
|
context, 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(context, content->GetOriginalExpression(),
|
|
cmStrCat("The feature name '", feature,
|
|
"' contains invalid characters."));
|
|
return std::string();
|
|
}
|
|
|
|
const auto LL_BEGIN = cmStrCat("<LINK_LIBRARY:", feature, '>');
|
|
const auto 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, [&](const std::string& item) -> bool {
|
|
return item == LL_BEGIN || item == LL_END;
|
|
});
|
|
auto it =
|
|
std::find_if(list.cbegin() + 1, list.cend(),
|
|
[&feature](const std::string& item) -> bool {
|
|
return cmHasPrefix(item, "<LINK_LIBRARY:"_s) &&
|
|
item.substr(14, item.find('>', 14) - 14) != feature;
|
|
});
|
|
if (it != list.cend()) {
|
|
reportError(
|
|
context, 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(),
|
|
[](const std::string& item) -> bool {
|
|
return cmHasPrefix(item, "<LINK_GROUP:"_s);
|
|
});
|
|
if (it != list.cend()) {
|
|
reportError(context, 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 cmJoin(list, ";"_s);
|
|
}
|
|
} linkLibraryNode;
|
|
|
|
static const struct LinkGroupNode : public cmGeneratorExpressionNode
|
|
{
|
|
LinkGroupNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return OneOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
using ForGenex = cmGeneratorExpressionDAGChecker::ForGenex;
|
|
|
|
if (!context->HeadTarget || !dagChecker ||
|
|
!dagChecker->EvaluatingLinkLibraries(nullptr, ForGenex::LINK_GROUP)) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<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(
|
|
context, 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(),
|
|
[](const std::string& item) -> bool {
|
|
return cmHasPrefix(item, "<LINK_GROUP"_s);
|
|
}) != list.cend()) {
|
|
reportError(context, 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(context, content->GetOriginalExpression(),
|
|
cmStrCat("The feature name '", feature,
|
|
"' contains invalid characters."));
|
|
return std::string();
|
|
}
|
|
|
|
const auto LG_BEGIN = cmStrCat(
|
|
"<LINK_GROUP:", feature, ':',
|
|
cmJoin(cmRange<decltype(list.cbegin())>(list.cbegin() + 1, list.cend()),
|
|
"|"_s),
|
|
'>');
|
|
const auto LG_END = cmStrCat("</LINK_GROUP:", feature, '>');
|
|
|
|
list.front() = LG_BEGIN;
|
|
list.push_back(LG_END);
|
|
|
|
return cmJoin(list, ";"_s);
|
|
}
|
|
} linkGroupNode;
|
|
|
|
static const struct HostLinkNode : public cmGeneratorExpressionNode
|
|
{
|
|
HostLinkNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
if (!context->HeadTarget || !dagChecker ||
|
|
!dagChecker->EvaluatingLinkOptionsExpression()) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<HOST_LINK:...> may only be used with binary targets "
|
|
"to specify link options.");
|
|
return std::string();
|
|
}
|
|
|
|
return context->HeadTarget->IsDeviceLink() ? std::string()
|
|
: cmJoin(parameters, ";");
|
|
}
|
|
} hostLinkNode;
|
|
|
|
static const struct DeviceLinkNode : public cmGeneratorExpressionNode
|
|
{
|
|
DeviceLinkNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return ZeroOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
if (!context->HeadTarget || !dagChecker ||
|
|
!dagChecker->EvaluatingLinkOptionsExpression()) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"$<DEVICE_LINK:...> may only be used with binary targets "
|
|
"to specify link options.");
|
|
return std::string();
|
|
}
|
|
|
|
if (context->HeadTarget->IsDeviceLink()) {
|
|
cmList list{ parameters.begin(), parameters.end() };
|
|
const auto DL_BEGIN = "<DEVICE_LINK>"_s;
|
|
const auto DL_END = "</DEVICE_LINK>"_s;
|
|
cm::erase_if(list, [&](const std::string& 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 cmJoin(list, ";");
|
|
}
|
|
|
|
return std::string();
|
|
}
|
|
} deviceLinkNode;
|
|
|
|
static std::string getLinkedTargetsContent(
|
|
cmGeneratorTarget const* target, std::string const& prop,
|
|
cmGeneratorExpressionContext* context,
|
|
cmGeneratorExpressionDAGChecker* dagChecker)
|
|
{
|
|
std::string result;
|
|
if (cmLinkImplementationLibraries const* impl =
|
|
target->GetLinkImplementationLibraries(
|
|
context->Config, cmGeneratorTarget::LinkInterfaceFor::Usage)) {
|
|
for (cmLinkImplItem 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.
|
|
cmGeneratorExpressionContext libContext(
|
|
target->GetLocalGenerator(), context->Config, context->Quiet, target,
|
|
target, context->EvaluateForBuildsystem, lib.Backtrace,
|
|
context->Language);
|
|
std::string libResult =
|
|
lib.Target->EvaluateInterfaceProperty(prop, &libContext, dagChecker);
|
|
if (!libResult.empty()) {
|
|
if (result.empty()) {
|
|
result = std::move(libResult);
|
|
} else {
|
|
result.reserve(result.size() + 1 + libResult.size());
|
|
result += ";";
|
|
result += libResult;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static const struct TargetPropertyNode : public cmGeneratorExpressionNode
|
|
{
|
|
TargetPropertyNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
// This node handles errors on parameter count itself.
|
|
int NumExpectedParameters() const override { return OneOrMoreParameters; }
|
|
|
|
static const char* GetErrorText(std::string const& targetName,
|
|
std::string const& propertyName)
|
|
{
|
|
static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
|
|
if (targetName.empty() && propertyName.empty()) {
|
|
return "$<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(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagCheckerParent) const override
|
|
{
|
|
static cmsys::RegularExpression propertyNameValidator("^[A-Za-z0-9_]+$");
|
|
|
|
cmGeneratorTarget const* target = nullptr;
|
|
std::string targetName;
|
|
std::string propertyName;
|
|
|
|
if (parameters.size() == 2) {
|
|
targetName = parameters[0];
|
|
propertyName = parameters[1];
|
|
|
|
if (const char* e = GetErrorText(targetName, propertyName)) {
|
|
reportError(context, content->GetOriginalExpression(), e);
|
|
return std::string();
|
|
}
|
|
if (propertyName == "ALIASED_TARGET"_s) {
|
|
if (context->LG->GetMakefile()->IsAlias(targetName)) {
|
|
if (cmGeneratorTarget* tgt =
|
|
context->LG->FindGeneratorTargetToUse(targetName)) {
|
|
return tgt->GetName();
|
|
}
|
|
}
|
|
return std::string();
|
|
}
|
|
if (propertyName == "ALIAS_GLOBAL"_s) {
|
|
if (context->LG->GetMakefile()->IsAlias(targetName)) {
|
|
return context->LG->GetGlobalGenerator()->IsAlias(targetName)
|
|
? "TRUE"
|
|
: "FALSE";
|
|
}
|
|
return std::string();
|
|
}
|
|
cmLocalGenerator const* lg = context->CurrentTarget
|
|
? context->CurrentTarget->GetLocalGenerator()
|
|
: context->LG;
|
|
target = lg->FindGeneratorTargetToUse(targetName);
|
|
|
|
if (!target) {
|
|
std::ostringstream e;
|
|
e << "Target \"" << targetName << "\" not found.";
|
|
reportError(context, content->GetOriginalExpression(), e.str());
|
|
return std::string();
|
|
}
|
|
context->AllTargets.insert(target);
|
|
|
|
} else if (parameters.size() == 1) {
|
|
target = context->HeadTarget;
|
|
propertyName = parameters[0];
|
|
|
|
// Keep track of the properties seen while processing.
|
|
// The evaluation of the LINK_LIBRARIES generator expressions
|
|
// will check this to ensure that properties have one consistent
|
|
// value for all evaluations.
|
|
context->SeenTargetProperties.insert(propertyName);
|
|
|
|
context->HadHeadSensitiveCondition = true;
|
|
if (!target) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<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(
|
|
context, content->GetOriginalExpression(),
|
|
"$<TARGET_PROPERTY:...> expression requires one or two parameters");
|
|
return std::string();
|
|
}
|
|
|
|
if (propertyName == "SOURCES") {
|
|
context->SourceSensitiveTargets.insert(target);
|
|
}
|
|
|
|
if (propertyName.empty()) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<TARGET_PROPERTY:...> expression requires a non-empty property "
|
|
"name.");
|
|
return std::string();
|
|
}
|
|
|
|
if (!propertyNameValidator.find(propertyName)) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"Property name not supported.");
|
|
return std::string();
|
|
}
|
|
|
|
assert(target);
|
|
|
|
if (propertyName == "LINKER_LANGUAGE") {
|
|
if (target->LinkLanguagePropagatesToDependents() && dagCheckerParent &&
|
|
(dagCheckerParent->EvaluatingLinkLibraries() ||
|
|
dagCheckerParent->EvaluatingSources())) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"LINKER_LANGUAGE target property can not be used while evaluating "
|
|
"link libraries for a static library");
|
|
return std::string();
|
|
}
|
|
return target->GetLinkerLanguage(context->Config);
|
|
}
|
|
|
|
std::string interfacePropertyName;
|
|
bool isInterfaceProperty = false;
|
|
|
|
#define POPULATE_INTERFACE_PROPERTY_NAME(prop) \
|
|
if (propertyName == #prop) { \
|
|
interfacePropertyName = "INTERFACE_" #prop; \
|
|
} else if (propertyName == "INTERFACE_" #prop) { \
|
|
interfacePropertyName = "INTERFACE_" #prop; \
|
|
isInterfaceProperty = true; \
|
|
} else
|
|
|
|
CM_FOR_EACH_TRANSITIVE_PROPERTY_NAME(POPULATE_INTERFACE_PROPERTY_NAME)
|
|
// Note that the above macro terminates with an else
|
|
/* else */ if (cmHasLiteralPrefix(propertyName, "COMPILE_DEFINITIONS_")) {
|
|
cmPolicies::PolicyStatus polSt =
|
|
context->LG->GetPolicyStatus(cmPolicies::CMP0043);
|
|
if (polSt == cmPolicies::WARN || polSt == cmPolicies::OLD) {
|
|
interfacePropertyName = "INTERFACE_COMPILE_DEFINITIONS";
|
|
}
|
|
}
|
|
#undef POPULATE_INTERFACE_PROPERTY_NAME
|
|
|
|
bool evaluatingLinkLibraries = false;
|
|
|
|
if (dagCheckerParent) {
|
|
if (dagCheckerParent->EvaluatingGenexExpression() ||
|
|
dagCheckerParent->EvaluatingPICExpression() ||
|
|
dagCheckerParent->EvaluatingLinkerLauncher()) {
|
|
// No check required.
|
|
} else if (dagCheckerParent->EvaluatingLinkLibraries()) {
|
|
evaluatingLinkLibraries = true;
|
|
if (!interfacePropertyName.empty()) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<TARGET_PROPERTY:...> expression in link libraries "
|
|
"evaluation depends on target property which is transitive "
|
|
"over the link libraries, creating a recursion.");
|
|
return std::string();
|
|
}
|
|
} else {
|
|
#define ASSERT_TRANSITIVE_PROPERTY_METHOD(METHOD) dagCheckerParent->METHOD() ||
|
|
assert(CM_FOR_EACH_TRANSITIVE_PROPERTY_METHOD(
|
|
ASSERT_TRANSITIVE_PROPERTY_METHOD) false); // NOLINT(clang-tidy)
|
|
#undef ASSERT_TRANSITIVE_PROPERTY_METHOD
|
|
}
|
|
}
|
|
|
|
if (isInterfaceProperty) {
|
|
return cmGeneratorExpression::StripEmptyListElements(
|
|
target->EvaluateInterfaceProperty(propertyName, context,
|
|
dagCheckerParent));
|
|
}
|
|
|
|
cmGeneratorExpressionDAGChecker dagChecker(
|
|
context->Backtrace, target, propertyName, content, dagCheckerParent);
|
|
|
|
switch (dagChecker.Check()) {
|
|
case cmGeneratorExpressionDAGChecker::SELF_REFERENCE:
|
|
dagChecker.ReportError(context, content->GetOriginalExpression());
|
|
return std::string();
|
|
case cmGeneratorExpressionDAGChecker::CYCLIC_REFERENCE:
|
|
// No error. We just skip cyclic references.
|
|
return std::string();
|
|
case cmGeneratorExpressionDAGChecker::ALREADY_SEEN:
|
|
// 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();
|
|
}
|
|
|
|
if (!haveProp && !target->IsImported() &&
|
|
target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
|
|
if (target->IsLinkInterfaceDependentBoolProperty(propertyName,
|
|
context->Config)) {
|
|
context->HadContextSensitiveCondition = true;
|
|
return target->GetLinkInterfaceDependentBoolProperty(propertyName,
|
|
context->Config)
|
|
? "1"
|
|
: "0";
|
|
}
|
|
if (target->IsLinkInterfaceDependentStringProperty(propertyName,
|
|
context->Config)) {
|
|
context->HadContextSensitiveCondition = true;
|
|
const char* propContent =
|
|
target->GetLinkInterfaceDependentStringProperty(propertyName,
|
|
context->Config);
|
|
return propContent ? propContent : "";
|
|
}
|
|
if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName,
|
|
context->Config)) {
|
|
context->HadContextSensitiveCondition = true;
|
|
const char* propContent =
|
|
target->GetLinkInterfaceDependentNumberMinProperty(propertyName,
|
|
context->Config);
|
|
return propContent ? propContent : "";
|
|
}
|
|
if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName,
|
|
context->Config)) {
|
|
context->HadContextSensitiveCondition = true;
|
|
const char* propContent =
|
|
target->GetLinkInterfaceDependentNumberMaxProperty(propertyName,
|
|
context->Config);
|
|
return propContent ? propContent : "";
|
|
}
|
|
}
|
|
|
|
if (!target->IsImported() && dagCheckerParent &&
|
|
!dagCheckerParent->EvaluatingLinkLibraries()) {
|
|
if (target->IsLinkInterfaceDependentNumberMinProperty(propertyName,
|
|
context->Config)) {
|
|
context->HadContextSensitiveCondition = true;
|
|
const char* propContent =
|
|
target->GetLinkInterfaceDependentNumberMinProperty(propertyName,
|
|
context->Config);
|
|
return propContent ? propContent : "";
|
|
}
|
|
if (target->IsLinkInterfaceDependentNumberMaxProperty(propertyName,
|
|
context->Config)) {
|
|
context->HadContextSensitiveCondition = true;
|
|
const char* propContent =
|
|
target->GetLinkInterfaceDependentNumberMaxProperty(propertyName,
|
|
context->Config);
|
|
return propContent ? propContent : "";
|
|
}
|
|
}
|
|
|
|
if (!interfacePropertyName.empty()) {
|
|
result = cmGeneratorExpression::StripEmptyListElements(
|
|
this->EvaluateDependentExpression(result, context->LG, context, target,
|
|
&dagChecker, target));
|
|
std::string linkedTargetsContent = getLinkedTargetsContent(
|
|
target, interfacePropertyName, context, &dagChecker);
|
|
if (!linkedTargetsContent.empty()) {
|
|
result += (result.empty() ? "" : ";") + linkedTargetsContent;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
} targetPropertyNode;
|
|
|
|
static const struct TargetNameNode : public cmGeneratorExpressionNode
|
|
{
|
|
TargetNameNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
bool GeneratesContent() const override { return true; }
|
|
|
|
bool AcceptsArbitraryContentParameter() const override { return true; }
|
|
bool RequiresLiteralInput() const override { return true; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* /*context*/,
|
|
const GeneratorExpressionContent* /*content*/,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
return parameters.front();
|
|
}
|
|
|
|
int NumExpectedParameters() const override { return 1; }
|
|
|
|
} targetNameNode;
|
|
|
|
static const struct TargetObjectsNode : public cmGeneratorExpressionNode
|
|
{
|
|
TargetObjectsNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
std::string const& tgtName = parameters.front();
|
|
cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
|
|
if (!gt) {
|
|
std::ostringstream e;
|
|
e << "Objects of target \"" << tgtName
|
|
<< "\" referenced but no such target exists.";
|
|
reportError(context, content->GetOriginalExpression(), e.str());
|
|
return std::string();
|
|
}
|
|
cmStateEnums::TargetType type = gt->GetType();
|
|
if (type != cmStateEnums::EXECUTABLE &&
|
|
type != cmStateEnums::STATIC_LIBRARY &&
|
|
type != cmStateEnums::SHARED_LIBRARY &&
|
|
type != cmStateEnums::MODULE_LIBRARY &&
|
|
type != cmStateEnums::OBJECT_LIBRARY) {
|
|
std::ostringstream e;
|
|
e << "Objects of target \"" << tgtName
|
|
<< "\" referenced but is not one of the allowed target types "
|
|
<< "(EXECUTABLE, STATIC, SHARED, MODULE, OBJECT).";
|
|
reportError(context, content->GetOriginalExpression(), e.str());
|
|
return std::string();
|
|
}
|
|
cmGlobalGenerator* gg = context->LG->GetGlobalGenerator();
|
|
{
|
|
std::string reason;
|
|
if (!context->EvaluateForBuildsystem &&
|
|
!gt->Target->HasKnownObjectFileLocation(&reason)) {
|
|
std::ostringstream e;
|
|
e << "The evaluation of the TARGET_OBJECTS generator expression "
|
|
"is only suitable for consumption by CMake (limited"
|
|
<< reason
|
|
<< "). "
|
|
"It is not suitable for writing out elsewhere.";
|
|
reportError(context, content->GetOriginalExpression(), e.str());
|
|
return std::string();
|
|
}
|
|
}
|
|
|
|
cmList objects;
|
|
|
|
if (gt->IsImported()) {
|
|
cmValue loc = nullptr;
|
|
cmValue imp = nullptr;
|
|
std::string suffix;
|
|
if (gt->Target->GetMappedConfig(context->Config, loc, imp, suffix)) {
|
|
objects.assign(*loc);
|
|
}
|
|
context->HadContextSensitiveCondition = true;
|
|
} else {
|
|
gt->GetTargetObjectNames(context->Config, objects);
|
|
|
|
std::string obj_dir;
|
|
if (context->EvaluateForBuildsystem && !gg->SupportsCrossConfigs()) {
|
|
// Use object file directory with buildsystem placeholder.
|
|
obj_dir = gt->ObjectDirectory;
|
|
context->HadContextSensitiveCondition =
|
|
gt->HasContextDependentSources();
|
|
} else {
|
|
// Use object file directory with per-config location.
|
|
obj_dir = gt->GetObjectDirectory(context->Config);
|
|
context->HadContextSensitiveCondition = true;
|
|
}
|
|
|
|
for (auto& o : objects) {
|
|
o = cmStrCat(obj_dir, o);
|
|
}
|
|
}
|
|
|
|
// Create the cmSourceFile instances in the referencing directory.
|
|
cmMakefile* mf = context->LG->GetMakefile();
|
|
for (std::string const& o : objects) {
|
|
mf->AddTargetObject(tgtName, o);
|
|
}
|
|
|
|
return cmJoin(objects, ";");
|
|
}
|
|
} targetObjectsNode;
|
|
|
|
struct TargetRuntimeDllsBaseNode : public cmGeneratorExpressionNode
|
|
{
|
|
std::vector<std::string> CollectDlls(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content) const
|
|
{
|
|
std::string const& tgtName = parameters.front();
|
|
cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
|
|
if (!gt) {
|
|
std::ostringstream e;
|
|
e << "Objects of target \"" << tgtName
|
|
<< "\" referenced but no such target exists.";
|
|
reportError(context, content->GetOriginalExpression(), e.str());
|
|
return std::vector<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(context, content->GetOriginalExpression(), e.str());
|
|
return std::vector<std::string>();
|
|
}
|
|
|
|
if (auto* cli = gt->GetLinkInformation(context->Config)) {
|
|
std::vector<std::string> dllPaths;
|
|
auto const& dlls = cli->GetRuntimeDLLs();
|
|
|
|
for (auto const& dll : dlls) {
|
|
if (auto loc = dll->MaybeGetLocation(context->Config)) {
|
|
dllPaths.emplace_back(*loc);
|
|
}
|
|
}
|
|
|
|
return dllPaths;
|
|
}
|
|
|
|
return std::vector<std::string>();
|
|
}
|
|
};
|
|
|
|
static const struct TargetRuntimeDllsNode : public TargetRuntimeDllsBaseNode
|
|
{
|
|
TargetRuntimeDllsNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
std::vector<std::string> dlls = CollectDlls(parameters, context, content);
|
|
return cmJoin(dlls, ";");
|
|
}
|
|
} targetRuntimeDllsNode;
|
|
|
|
static const struct TargetRuntimeDllDirsNode : public TargetRuntimeDllsBaseNode
|
|
{
|
|
TargetRuntimeDllDirsNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
std::vector<std::string> dlls = CollectDlls(parameters, context, content);
|
|
std::vector<std::string> dllDirs;
|
|
for (const std::string& dll : dlls) {
|
|
std::string directory = cmSystemTools::GetFilenamePath(dll);
|
|
if (std::find(dllDirs.begin(), dllDirs.end(), directory) ==
|
|
dllDirs.end()) {
|
|
dllDirs.push_back(directory);
|
|
}
|
|
}
|
|
return cmJoin(dllDirs, ";");
|
|
}
|
|
} targetRuntimeDllDirsNode;
|
|
|
|
static const struct CompileFeaturesNode : public cmGeneratorExpressionNode
|
|
{
|
|
CompileFeaturesNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return OneOrMoreParameters; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
cmGeneratorTarget const* target = context->HeadTarget;
|
|
if (!target) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<COMPILE_FEATURE> may only be used with binary targets. It may "
|
|
"not be used with add_custom_command or add_custom_target.");
|
|
return std::string();
|
|
}
|
|
context->HadHeadSensitiveCondition = true;
|
|
|
|
using LangMap = std::map<std::string, cmList>;
|
|
static LangMap availableFeatures;
|
|
|
|
LangMap testedFeatures;
|
|
cmStandardLevelResolver standardResolver(context->LG->GetMakefile());
|
|
for (std::string const& p : parameters) {
|
|
std::string error;
|
|
std::string lang;
|
|
if (!standardResolver.CompileFeatureKnown(
|
|
context->HeadTarget->Target->GetName(), p, lang, &error)) {
|
|
reportError(context, content->GetOriginalExpression(), error);
|
|
return std::string();
|
|
}
|
|
testedFeatures[lang].push_back(p);
|
|
|
|
if (availableFeatures.find(lang) == availableFeatures.end()) {
|
|
cmValue featuresKnown =
|
|
standardResolver.CompileFeaturesAvailable(lang, &error);
|
|
if (!featuresKnown) {
|
|
reportError(context, content->GetOriginalExpression(), error);
|
|
return std::string();
|
|
}
|
|
availableFeatures[lang].assign(featuresKnown);
|
|
}
|
|
}
|
|
|
|
bool evalLL = dagChecker && dagChecker->EvaluatingLinkLibraries();
|
|
|
|
for (auto const& lit : testedFeatures) {
|
|
std::vector<std::string> const& langAvailable =
|
|
availableFeatures[lit.first];
|
|
cmValue standardDefault = context->LG->GetMakefile()->GetDefinition(
|
|
"CMAKE_" + lit.first + "_STANDARD_DEFAULT");
|
|
for (std::string const& it : lit.second) {
|
|
if (!cm::contains(langAvailable, it)) {
|
|
return "0";
|
|
}
|
|
if (standardDefault && standardDefault->empty()) {
|
|
// This compiler has no notion of language standard levels.
|
|
// All features known for the language are always available.
|
|
continue;
|
|
}
|
|
if (!standardResolver.HaveStandardAvailable(target, lit.first,
|
|
context->Config, it)) {
|
|
if (evalLL) {
|
|
cmValue l =
|
|
target->GetLanguageStandard(lit.first, context->Config);
|
|
if (!l) {
|
|
l = standardDefault;
|
|
}
|
|
assert(l);
|
|
context->MaxLanguageStandard[target][lit.first] = *l;
|
|
} else {
|
|
return "0";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return "1";
|
|
}
|
|
} compileFeaturesNode;
|
|
|
|
static const char* targetPolicyWhitelist[] = {
|
|
nullptr
|
|
#define TARGET_POLICY_STRING(POLICY) , #POLICY
|
|
|
|
CM_FOR_EACH_TARGET_POLICY(TARGET_POLICY_STRING)
|
|
|
|
#undef TARGET_POLICY_STRING
|
|
};
|
|
|
|
static cmPolicies::PolicyStatus statusForTarget(cmGeneratorTarget const* tgt,
|
|
const char* policy)
|
|
{
|
|
#define RETURN_POLICY(POLICY) \
|
|
if (strcmp(policy, #POLICY) == 0) { \
|
|
return tgt->GetPolicyStatus##POLICY(); \
|
|
}
|
|
|
|
CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY)
|
|
|
|
#undef RETURN_POLICY
|
|
|
|
assert(false && "Unreachable code. Not a valid policy");
|
|
return cmPolicies::WARN;
|
|
}
|
|
|
|
static cmPolicies::PolicyID policyForString(const char* policy_id)
|
|
{
|
|
#define RETURN_POLICY_ID(POLICY_ID) \
|
|
if (strcmp(policy_id, #POLICY_ID) == 0) { \
|
|
return cmPolicies::POLICY_ID; \
|
|
}
|
|
|
|
CM_FOR_EACH_TARGET_POLICY(RETURN_POLICY_ID)
|
|
|
|
#undef RETURN_POLICY_ID
|
|
|
|
assert(false && "Unreachable code. Not a valid policy");
|
|
return cmPolicies::CMP0002;
|
|
}
|
|
|
|
static const struct TargetPolicyNode : public cmGeneratorExpressionNode
|
|
{
|
|
TargetPolicyNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 1; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
if (!context->HeadTarget) {
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<TARGET_POLICY:prop> may only be used with binary targets. It "
|
|
"may not be used with add_custom_command or add_custom_target.");
|
|
return std::string();
|
|
}
|
|
|
|
context->HadContextSensitiveCondition = true;
|
|
context->HadHeadSensitiveCondition = true;
|
|
|
|
for (size_t i = 1; i < cm::size(targetPolicyWhitelist); ++i) {
|
|
const char* policy = targetPolicyWhitelist[i];
|
|
if (parameters.front() == policy) {
|
|
cmLocalGenerator* lg = context->HeadTarget->GetLocalGenerator();
|
|
switch (statusForTarget(context->HeadTarget, policy)) {
|
|
case cmPolicies::WARN:
|
|
lg->IssueMessage(
|
|
MessageType::AUTHOR_WARNING,
|
|
cmPolicies::GetPolicyWarning(policyForString(policy)));
|
|
CM_FALLTHROUGH;
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
case cmPolicies::REQUIRED_ALWAYS:
|
|
case cmPolicies::OLD:
|
|
return "0";
|
|
case cmPolicies::NEW:
|
|
return "1";
|
|
}
|
|
}
|
|
}
|
|
reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"$<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(
|
|
const std::vector<std::string>& /*parameters*/,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"INSTALL_PREFIX is a marker for install(EXPORT) only. It "
|
|
"should never be evaluated.");
|
|
return std::string();
|
|
}
|
|
|
|
} installPrefixNode;
|
|
|
|
class ArtifactDirTag;
|
|
class ArtifactLinkerTag;
|
|
class 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,
|
|
cmGeneratorExpressionContext* context)
|
|
{
|
|
context->DependTargets.insert(target);
|
|
context->AllTargets.insert(target);
|
|
}
|
|
};
|
|
|
|
struct TargetFilesystemArtifactDependencyCMP0112
|
|
{
|
|
static void AddDependency(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context)
|
|
{
|
|
context->AllTargets.insert(target);
|
|
cmLocalGenerator* lg = context->LG;
|
|
switch (target->GetPolicyStatusCMP0112()) {
|
|
case cmPolicies::WARN:
|
|
if (lg->GetMakefile()->PolicyOptionalWarningEnabled(
|
|
"CMAKE_POLICY_WARNING_CMP0112")) {
|
|
std::string err =
|
|
cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0112),
|
|
"\nDependency being added to target:\n \"",
|
|
target->GetName(), "\"\n");
|
|
lg->GetCMakeInstance()->IssueMessage(MessageType ::AUTHOR_WARNING,
|
|
err, context->Backtrace);
|
|
}
|
|
CM_FALLTHROUGH;
|
|
case cmPolicies::OLD:
|
|
context->DependTargets.insert(target);
|
|
break;
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
case cmPolicies::REQUIRED_ALWAYS:
|
|
case cmPolicies::NEW:
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
template <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,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content);
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactSonameTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
// The target soname file (.so.1).
|
|
if (target->IsDLLPlatform()) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_SONAME_FILE is not allowed "
|
|
"for DLL target platforms.");
|
|
return std::string();
|
|
}
|
|
if (target->GetType() != cmStateEnums::SHARED_LIBRARY) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_SONAME_FILE is allowed only for "
|
|
"SHARED libraries.");
|
|
return std::string();
|
|
}
|
|
std::string result = cmStrCat(target->GetDirectory(context->Config), '/',
|
|
target->GetSOName(context->Config));
|
|
return result;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactSonameImportTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
// The target soname file (.so.1).
|
|
if (target->IsDLLPlatform()) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_SONAME_IMPORT_FILE is not allowed "
|
|
"for DLL target platforms.");
|
|
return std::string();
|
|
}
|
|
if (target->GetType() != cmStateEnums::SHARED_LIBRARY) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_SONAME_IMPORT_FILE is allowed only for "
|
|
"SHARED libraries.");
|
|
return std::string();
|
|
}
|
|
|
|
if (target->HasImportLibrary(context->Config)) {
|
|
return cmStrCat(target->GetDirectory(
|
|
context->Config, cmStateEnums::ImportLibraryArtifact),
|
|
'/',
|
|
target->GetSOName(context->Config,
|
|
cmStateEnums::ImportLibraryArtifact));
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactPdbTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (target->IsImported()) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_PDB_FILE not allowed for IMPORTED targets.");
|
|
return std::string();
|
|
}
|
|
|
|
std::string language = target->GetLinkerLanguage(context->Config);
|
|
|
|
std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB";
|
|
|
|
if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_PDB_FILE is not supported by the target linker.");
|
|
return std::string();
|
|
}
|
|
|
|
cmStateEnums::TargetType targetType = target->GetType();
|
|
|
|
if (targetType != cmStateEnums::SHARED_LIBRARY &&
|
|
targetType != cmStateEnums::MODULE_LIBRARY &&
|
|
targetType != cmStateEnums::EXECUTABLE) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_PDB_FILE is allowed only for "
|
|
"targets with linker created artifacts.");
|
|
return std::string();
|
|
}
|
|
|
|
std::string result = cmStrCat(target->GetPDBDirectory(context->Config),
|
|
'/', target->GetPDBName(context->Config));
|
|
return result;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactLinkerTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
// The file used to link to the target (.so, .lib, .a) or import file
|
|
// (.lib, .tbd).
|
|
if (!target->IsLinkable()) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_FILE is allowed only for libraries and "
|
|
"executables with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
cmStateEnums::ArtifactType artifact =
|
|
target->HasImportLibrary(context->Config)
|
|
? cmStateEnums::ImportLibraryArtifact
|
|
: cmStateEnums::RuntimeBinaryArtifact;
|
|
return target->GetFullPath(context->Config, artifact);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactLinkerLibraryTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
// The file used to link to the target (.dylib, .so, .a).
|
|
if (!target->IsLinkable() ||
|
|
target->GetType() == cmStateEnums::EXECUTABLE) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_LIBRARY_FILE is allowed only for libraries "
|
|
"with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
|
|
if (!target->IsDLLPlatform() ||
|
|
target->GetType() == cmStateEnums::STATIC_LIBRARY) {
|
|
return target->GetFullPath(context->Config,
|
|
cmStateEnums::RuntimeBinaryArtifact);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactLinkerImportTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
// The file used to link to the target (.lib, .tbd).
|
|
if (!target->IsLinkable()) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_IMPORT_FILE is allowed only for libraries and "
|
|
"executables with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
|
|
if (target->HasImportLibrary(context->Config)) {
|
|
return target->GetFullPath(context->Config,
|
|
cmStateEnums::ImportLibraryArtifact);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (target->IsImported()) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_BUNDLE_DIR not allowed for IMPORTED targets.");
|
|
return std::string();
|
|
}
|
|
if (!target->IsBundleOnApple()) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_BUNDLE_DIR is allowed only for Bundle targets.");
|
|
return std::string();
|
|
}
|
|
|
|
std::string outpath = target->GetDirectory(context->Config) + '/';
|
|
return target->BuildBundleDirectory(outpath, context->Config,
|
|
cmGeneratorTarget::BundleDirLevel);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirNameTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (target->IsImported()) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_BUNDLE_DIR_NAME not allowed for IMPORTED targets.");
|
|
return std::string();
|
|
}
|
|
if (!target->IsBundleOnApple()) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_BUNDLE_DIR_NAME is allowed only for Bundle targets.");
|
|
return std::string();
|
|
}
|
|
|
|
auto level = cmGeneratorTarget::BundleDirLevel;
|
|
auto config = context->Config;
|
|
if (target->IsAppBundleOnApple()) {
|
|
return target->GetAppBundleDirectory(config, level);
|
|
}
|
|
if (target->IsFrameworkOnApple()) {
|
|
return target->GetFrameworkDirectory(config, level);
|
|
}
|
|
if (target->IsCFBundleOnApple()) {
|
|
return target->GetCFBundleDirectory(config, level);
|
|
}
|
|
return std::string();
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactBundleContentDirTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (target->IsImported()) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_BUNDLE_CONTENT_DIR not allowed for IMPORTED targets.");
|
|
return std::string();
|
|
}
|
|
if (!target->IsBundleOnApple()) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_BUNDLE_CONTENT_DIR is allowed only for Bundle targets.");
|
|
return std::string();
|
|
}
|
|
|
|
std::string outpath = target->GetDirectory(context->Config) + '/';
|
|
return target->BuildBundleDirectory(outpath, context->Config,
|
|
cmGeneratorTarget::ContentLevel);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactNameTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* /*unused*/)
|
|
{
|
|
return target->GetFullPath(context->Config,
|
|
cmStateEnums::RuntimeBinaryArtifact, true);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultCreator<ArtifactImportTag>
|
|
{
|
|
static std::string Create(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* /*unused*/)
|
|
{
|
|
if (target->HasImportLibrary(context->Config)) {
|
|
return target->GetFullPath(context->Config,
|
|
cmStateEnums::ImportLibraryArtifact, true);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
|
|
template <typename ArtifactT>
|
|
struct TargetFilesystemArtifactResultGetter
|
|
{
|
|
static std::string Get(const std::string& result);
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultGetter<ArtifactNameTag>
|
|
{
|
|
static std::string Get(const std::string& result)
|
|
{
|
|
return cmSystemTools::GetFilenameName(result);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultGetter<ArtifactDirTag>
|
|
{
|
|
static std::string Get(const std::string& result)
|
|
{
|
|
return cmSystemTools::GetFilenamePath(result);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetFilesystemArtifactResultGetter<ArtifactPathTag>
|
|
{
|
|
static std::string Get(const std::string& result) { return result; }
|
|
};
|
|
|
|
struct TargetArtifactBase : public cmGeneratorExpressionNode
|
|
{
|
|
TargetArtifactBase() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
protected:
|
|
cmGeneratorTarget* GetTarget(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const
|
|
{
|
|
// Lookup the referenced target.
|
|
std::string const& name = parameters.front();
|
|
|
|
if (!cmGeneratorExpression::IsValidTargetName(name)) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"Expression syntax not recognized.");
|
|
return nullptr;
|
|
}
|
|
cmGeneratorTarget* target = context->LG->FindGeneratorTargetToUse(name);
|
|
if (!target) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"No target \"" + name + "\"");
|
|
return nullptr;
|
|
}
|
|
if (target->GetType() >= cmStateEnums::OBJECT_LIBRARY &&
|
|
target->GetType() != cmStateEnums::UNKNOWN_LIBRARY) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"Target \"" + name +
|
|
"\" is not an executable or library.");
|
|
return nullptr;
|
|
}
|
|
if (dagChecker &&
|
|
(dagChecker->EvaluatingLinkLibraries(target) ||
|
|
(dagChecker->EvaluatingSources() &&
|
|
target == dagChecker->TopTarget()))) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"Expressions which require the linker language may not "
|
|
"be used while evaluating link libraries");
|
|
return nullptr;
|
|
}
|
|
|
|
return target;
|
|
}
|
|
};
|
|
|
|
template <typename ArtifactT, typename ComponentT>
|
|
struct TargetFilesystemArtifact : public TargetArtifactBase
|
|
{
|
|
TargetFilesystemArtifact() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 1; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
cmGeneratorTarget* target =
|
|
this->GetTarget(parameters, context, content, dagChecker);
|
|
if (!target) {
|
|
return std::string();
|
|
}
|
|
// Not a dependent target if we are querying for ArtifactDirTag,
|
|
// ArtifactNameTag, ArtifactBundleDirTag, ArtifactBundleDirNameTag,
|
|
// and ArtifactBundleContentDirTag
|
|
TargetFilesystemArtifactDependency<ArtifactT, ComponentT>::AddDependency(
|
|
target, context);
|
|
|
|
std::string result =
|
|
TargetFilesystemArtifactResultCreator<ArtifactT>::Create(target, context,
|
|
content);
|
|
if (context->HadError) {
|
|
return std::string();
|
|
}
|
|
return TargetFilesystemArtifactResultGetter<ComponentT>::Get(result);
|
|
}
|
|
};
|
|
|
|
template <typename ArtifactT>
|
|
struct TargetFilesystemArtifactNodeGroup
|
|
{
|
|
TargetFilesystemArtifactNodeGroup() // NOLINT(modernize-use-equals-default)
|
|
{
|
|
}
|
|
|
|
TargetFilesystemArtifact<ArtifactT, ArtifactPathTag> File;
|
|
TargetFilesystemArtifact<ArtifactT, ArtifactNameTag> FileName;
|
|
TargetFilesystemArtifact<ArtifactT, ArtifactDirTag> FileDir;
|
|
};
|
|
|
|
static const TargetFilesystemArtifactNodeGroup<ArtifactNameTag>
|
|
targetNodeGroup;
|
|
|
|
static const TargetFilesystemArtifactNodeGroup<ArtifactImportTag>
|
|
targetImportNodeGroup;
|
|
|
|
static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerTag>
|
|
targetLinkerNodeGroup;
|
|
|
|
static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerLibraryTag>
|
|
targetLinkerLibraryNodeGroup;
|
|
|
|
static const TargetFilesystemArtifactNodeGroup<ArtifactLinkerImportTag>
|
|
targetLinkerImportNodeGroup;
|
|
|
|
static const TargetFilesystemArtifactNodeGroup<ArtifactSonameTag>
|
|
targetSoNameNodeGroup;
|
|
|
|
static const TargetFilesystemArtifactNodeGroup<ArtifactSonameImportTag>
|
|
targetSoNameImportNodeGroup;
|
|
|
|
static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag>
|
|
targetPdbNodeGroup;
|
|
|
|
static const TargetFilesystemArtifact<ArtifactBundleDirTag, ArtifactPathTag>
|
|
targetBundleDirNode;
|
|
|
|
static const TargetFilesystemArtifact<ArtifactBundleDirNameTag,
|
|
ArtifactNameTag>
|
|
targetBundleDirNameNode;
|
|
|
|
static const TargetFilesystemArtifact<ArtifactBundleContentDirTag,
|
|
ArtifactPathTag>
|
|
targetBundleContentDirNode;
|
|
|
|
//
|
|
// To retrieve base name for various artifacts
|
|
//
|
|
template <typename ArtifactT>
|
|
struct TargetOutputNameArtifactResultGetter
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content);
|
|
};
|
|
|
|
template <>
|
|
struct TargetOutputNameArtifactResultGetter<ArtifactNameTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* /*unused*/)
|
|
{
|
|
return target->GetOutputName(context->Config,
|
|
cmStateEnums::RuntimeBinaryArtifact) +
|
|
target->GetFilePostfix(context->Config);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetOutputNameArtifactResultGetter<ArtifactImportTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* /*unused*/)
|
|
{
|
|
if (target->HasImportLibrary(context->Config)) {
|
|
return target->GetOutputName(context->Config,
|
|
cmStateEnums::ImportLibraryArtifact) +
|
|
target->GetFilePostfix(context->Config);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
// The library file used to link to the target (.so, .lib, .a) or import
|
|
// file (.lin, .tbd).
|
|
if (!target->IsLinkable()) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_FILE_BASE_NAME is allowed only for "
|
|
"libraries and executables with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
cmStateEnums::ArtifactType artifact =
|
|
target->HasImportLibrary(context->Config)
|
|
? cmStateEnums::ImportLibraryArtifact
|
|
: cmStateEnums::RuntimeBinaryArtifact;
|
|
return target->GetOutputName(context->Config, artifact) +
|
|
target->GetFilePostfix(context->Config);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetOutputNameArtifactResultGetter<ArtifactLinkerLibraryTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
// The library file used to link to the target (.so, .lib, .a).
|
|
if (!target->IsLinkable() ||
|
|
target->GetType() == cmStateEnums::EXECUTABLE) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_LIBRARY_FILE_BASE_NAME is allowed only for "
|
|
"libraries with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
|
|
if (!target->IsDLLPlatform() ||
|
|
target->GetType() == cmStateEnums::STATIC_LIBRARY) {
|
|
return target->GetOutputName(context->Config,
|
|
cmStateEnums::ImportLibraryArtifact) +
|
|
target->GetFilePostfix(context->Config);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetOutputNameArtifactResultGetter<ArtifactLinkerImportTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
// The import file used to link to the target (.lib, .tbd).
|
|
if (!target->IsLinkable()) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_IMPORT_FILE_BASE_NAME is allowed only for "
|
|
"libraries and executables with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
|
|
if (target->HasImportLibrary(context->Config)) {
|
|
return target->GetOutputName(context->Config,
|
|
cmStateEnums::ImportLibraryArtifact) +
|
|
target->GetFilePostfix(context->Config);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (target->IsImported()) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets.");
|
|
return std::string();
|
|
}
|
|
|
|
std::string language = target->GetLinkerLanguage(context->Config);
|
|
|
|
std::string pdbSupportVar = "CMAKE_" + language + "_LINKER_SUPPORTS_PDB";
|
|
|
|
if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_PDB_FILE_BASE_NAME is not supported by the target linker.");
|
|
return std::string();
|
|
}
|
|
|
|
cmStateEnums::TargetType targetType = target->GetType();
|
|
|
|
if (targetType != cmStateEnums::SHARED_LIBRARY &&
|
|
targetType != cmStateEnums::MODULE_LIBRARY &&
|
|
targetType != cmStateEnums::EXECUTABLE) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_PDB_FILE_BASE_NAME is allowed only for "
|
|
"targets with linker created artifacts.");
|
|
return std::string();
|
|
}
|
|
|
|
return target->GetPDBOutputName(context->Config) +
|
|
target->GetFilePostfix(context->Config);
|
|
}
|
|
};
|
|
|
|
template <typename ArtifactT>
|
|
struct TargetFileBaseNameArtifact : public TargetArtifactBase
|
|
{
|
|
TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 1; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
cmGeneratorTarget* target =
|
|
this->GetTarget(parameters, context, content, dagChecker);
|
|
if (!target) {
|
|
return std::string();
|
|
}
|
|
|
|
std::string result = TargetOutputNameArtifactResultGetter<ArtifactT>::Get(
|
|
target, context, content);
|
|
if (context->HadError) {
|
|
return std::string();
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
|
|
static const TargetFileBaseNameArtifact<ArtifactNameTag>
|
|
targetFileBaseNameNode;
|
|
static const TargetFileBaseNameArtifact<ArtifactImportTag>
|
|
targetImportFileBaseNameNode;
|
|
static const TargetFileBaseNameArtifact<ArtifactLinkerTag>
|
|
targetLinkerFileBaseNameNode;
|
|
static const TargetFileBaseNameArtifact<ArtifactLinkerLibraryTag>
|
|
targetLinkerLibraryFileBaseNameNode;
|
|
static const TargetFileBaseNameArtifact<ArtifactLinkerImportTag>
|
|
targetLinkerImportFileBaseNameNode;
|
|
static const TargetFileBaseNameArtifact<ArtifactPdbTag>
|
|
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,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content);
|
|
};
|
|
|
|
template <>
|
|
struct TargetFileArtifactResultGetter<ArtifactFilePrefixTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent*)
|
|
{
|
|
return target->GetFilePrefix(context->Config);
|
|
}
|
|
};
|
|
template <>
|
|
struct TargetFileArtifactResultGetter<ArtifactImportFilePrefixTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent*)
|
|
{
|
|
if (target->HasImportLibrary(context->Config)) {
|
|
return target->GetFilePrefix(context->Config,
|
|
cmStateEnums::ImportLibraryArtifact);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
template <>
|
|
struct TargetFileArtifactResultGetter<ArtifactLinkerFilePrefixTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (!target->IsLinkable()) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_FILE_PREFIX is allowed only for libraries and "
|
|
"executables with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
|
|
cmStateEnums::ArtifactType artifact =
|
|
target->HasImportLibrary(context->Config)
|
|
? cmStateEnums::ImportLibraryArtifact
|
|
: cmStateEnums::RuntimeBinaryArtifact;
|
|
|
|
return target->GetFilePrefix(context->Config, artifact);
|
|
}
|
|
};
|
|
template <>
|
|
struct TargetFileArtifactResultGetter<ArtifactLinkerLibraryFilePrefixTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (!target->IsLinkable() ||
|
|
target->GetType() == cmStateEnums::EXECUTABLE) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_LIBRARY_FILE_PREFIX is allowed only for libraries "
|
|
"with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
|
|
if (!target->IsDLLPlatform() ||
|
|
target->GetType() == cmStateEnums::STATIC_LIBRARY) {
|
|
return target->GetFilePrefix(context->Config,
|
|
cmStateEnums::RuntimeBinaryArtifact);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
template <>
|
|
struct TargetFileArtifactResultGetter<ArtifactLinkerImportFilePrefixTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (!target->IsLinkable()) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_IMPORT_FILE_PREFIX is allowed only for libraries and "
|
|
"executables with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
|
|
if (target->HasImportLibrary(context->Config)) {
|
|
return target->GetFilePrefix(context->Config,
|
|
cmStateEnums::ImportLibraryArtifact);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
template <>
|
|
struct TargetFileArtifactResultGetter<ArtifactFileSuffixTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent*)
|
|
{
|
|
return target->GetFileSuffix(context->Config);
|
|
}
|
|
};
|
|
template <>
|
|
struct TargetFileArtifactResultGetter<ArtifactImportFileSuffixTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent*)
|
|
{
|
|
if (target->HasImportLibrary(context->Config)) {
|
|
return target->GetFileSuffix(context->Config,
|
|
cmStateEnums::ImportLibraryArtifact);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
template <>
|
|
struct TargetFileArtifactResultGetter<ArtifactLinkerFileSuffixTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (!target->IsLinkable()) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_FILE_SUFFIX is allowed only for libraries and "
|
|
"executables with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
|
|
cmStateEnums::ArtifactType artifact =
|
|
target->HasImportLibrary(context->Config)
|
|
? cmStateEnums::ImportLibraryArtifact
|
|
: cmStateEnums::RuntimeBinaryArtifact;
|
|
|
|
return target->GetFileSuffix(context->Config, artifact);
|
|
}
|
|
};
|
|
template <>
|
|
struct TargetFileArtifactResultGetter<ArtifactLinkerLibraryFileSuffixTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (!target->IsLinkable() ||
|
|
target->GetType() == cmStateEnums::STATIC_LIBRARY) {
|
|
::reportError(context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_LIBRARY_FILE_SUFFIX is allowed only for "
|
|
"libraries with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
|
|
if (!target->IsDLLPlatform() ||
|
|
target->GetType() == cmStateEnums::STATIC_LIBRARY) {
|
|
return target->GetFileSuffix(context->Config,
|
|
cmStateEnums::RuntimeBinaryArtifact);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
template <>
|
|
struct TargetFileArtifactResultGetter<ArtifactLinkerImportFileSuffixTag>
|
|
{
|
|
static std::string Get(cmGeneratorTarget* target,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content)
|
|
{
|
|
if (!target->IsLinkable()) {
|
|
::reportError(
|
|
context, content->GetOriginalExpression(),
|
|
"TARGET_LINKER_IMPORT_FILE_SUFFIX is allowed only for libraries and "
|
|
"executables with ENABLE_EXPORTS.");
|
|
return std::string();
|
|
}
|
|
|
|
if (target->HasImportLibrary(context->Config)) {
|
|
return target->GetFileSuffix(context->Config,
|
|
cmStateEnums::ImportLibraryArtifact);
|
|
}
|
|
return std::string{};
|
|
}
|
|
};
|
|
|
|
template <typename ArtifactT>
|
|
struct TargetFileArtifact : public TargetArtifactBase
|
|
{
|
|
TargetFileArtifact() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
int NumExpectedParameters() const override { return 1; }
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* dagChecker) const override
|
|
{
|
|
cmGeneratorTarget* target =
|
|
this->GetTarget(parameters, context, content, dagChecker);
|
|
if (!target) {
|
|
return std::string();
|
|
}
|
|
|
|
std::string result =
|
|
TargetFileArtifactResultGetter<ArtifactT>::Get(target, context, content);
|
|
if (context->HadError) {
|
|
return std::string();
|
|
}
|
|
return result;
|
|
}
|
|
};
|
|
|
|
static const TargetFileArtifact<ArtifactFilePrefixTag> targetFilePrefixNode;
|
|
static const TargetFileArtifact<ArtifactImportFilePrefixTag>
|
|
targetImportFilePrefixNode;
|
|
static const TargetFileArtifact<ArtifactLinkerFilePrefixTag>
|
|
targetLinkerFilePrefixNode;
|
|
static const TargetFileArtifact<ArtifactLinkerLibraryFilePrefixTag>
|
|
targetLinkerLibraryFilePrefixNode;
|
|
static const TargetFileArtifact<ArtifactLinkerImportFilePrefixTag>
|
|
targetLinkerImportFilePrefixNode;
|
|
static const TargetFileArtifact<ArtifactFileSuffixTag> targetFileSuffixNode;
|
|
static const TargetFileArtifact<ArtifactImportFileSuffixTag>
|
|
targetImportFileSuffixNode;
|
|
static const TargetFileArtifact<ArtifactLinkerFileSuffixTag>
|
|
targetLinkerFileSuffixNode;
|
|
static const TargetFileArtifact<ArtifactLinkerLibraryFileSuffixTag>
|
|
targetLinkerLibraryFileSuffixNode;
|
|
static const TargetFileArtifact<ArtifactLinkerImportFileSuffixTag>
|
|
targetLinkerImportFileSuffixNode;
|
|
|
|
static const struct ShellPathNode : public cmGeneratorExpressionNode
|
|
{
|
|
ShellPathNode() {} // NOLINT(modernize-use-equals-default)
|
|
|
|
std::string Evaluate(
|
|
const std::vector<std::string>& parameters,
|
|
cmGeneratorExpressionContext* context,
|
|
const GeneratorExpressionContent* content,
|
|
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
|
|
{
|
|
cmList listIn{ parameters.front() };
|
|
if (listIn.empty()) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"\"\" is not an absolute path.");
|
|
return std::string();
|
|
}
|
|
cmStateSnapshot snapshot = context->LG->GetStateSnapshot();
|
|
cmOutputConverter converter(snapshot);
|
|
const char* separator = snapshot.GetState()->UseWindowsShell() ? ";" : ":";
|
|
std::vector<std::string> listOut;
|
|
listOut.reserve(listIn.size());
|
|
for (auto const& in : listIn) {
|
|
if (!cmSystemTools::FileIsFullPath(in)) {
|
|
reportError(context, content->GetOriginalExpression(),
|
|
"\"" + in + "\" is not an absolute path.");
|
|
return std::string();
|
|
}
|
|
listOut.emplace_back(converter.ConvertDirectorySeparatorsForShell(in));
|
|
}
|
|
return cmJoin(listOut, separator);
|
|
}
|
|
} shellPathNode;
|
|
|
|
const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
|
|
const std::string& identifier)
|
|
{
|
|
static std::map<std::string, cmGeneratorExpressionNode const*> const nodeMap{
|
|
{ "0", &zeroNode },
|
|
{ "1", &oneNode },
|
|
{ "AND", &andNode },
|
|
{ "OR", &orNode },
|
|
{ "NOT", ¬Node },
|
|
{ "C_COMPILER_ID", &cCompilerIdNode },
|
|
{ "CXX_COMPILER_ID", &cxxCompilerIdNode },
|
|
{ "OBJC_COMPILER_ID", &objcCompilerIdNode },
|
|
{ "OBJCXX_COMPILER_ID", &objcxxCompilerIdNode },
|
|
{ "CUDA_COMPILER_ID", &cudaCompilerIdNode },
|
|
{ "Fortran_COMPILER_ID", &fortranCompilerIdNode },
|
|
{ "HIP_COMPILER_ID", &hipCompilerIdNode },
|
|
{ "VERSION_GREATER", &versionGreaterNode },
|
|
{ "VERSION_GREATER_EQUAL", &versionGreaterEqNode },
|
|
{ "VERSION_LESS", &versionLessNode },
|
|
{ "VERSION_LESS_EQUAL", &versionLessEqNode },
|
|
{ "VERSION_EQUAL", &versionEqualNode },
|
|
{ "C_COMPILER_VERSION", &cCompilerVersionNode },
|
|
{ "CXX_COMPILER_VERSION", &cxxCompilerVersionNode },
|
|
{ "CUDA_COMPILER_VERSION", &cudaCompilerVersionNode },
|
|
{ "OBJC_COMPILER_VERSION", &objcCompilerVersionNode },
|
|
{ "OBJCXX_COMPILER_VERSION", &objcxxCompilerVersionNode },
|
|
{ "Fortran_COMPILER_VERSION", &fortranCompilerVersionNode },
|
|
{ "HIP_COMPILER_VERSION", &hipCompilerVersionNode },
|
|
{ "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 },
|
|
{ "TARGET_PROPERTY", &targetPropertyNode },
|
|
{ "TARGET_NAME", &targetNameNode },
|
|
{ "TARGET_OBJECTS", &targetObjectsNode },
|
|
{ "TARGET_POLICY", &targetPolicyNode },
|
|
{ "TARGET_EXISTS", &targetExistsNode },
|
|
{ "TARGET_NAME_IF_EXISTS", &targetNameIfExistsNode },
|
|
{ "TARGET_GENEX_EVAL", &targetGenexEvalNode },
|
|
{ "TARGET_RUNTIME_DLLS", &targetRuntimeDllsNode },
|
|
{ "TARGET_RUNTIME_DLL_DIRS", &targetRuntimeDllDirsNode },
|
|
{ "GENEX_EVAL", &genexEvalNode },
|
|
{ "BUILD_INTERFACE", &buildInterfaceNode },
|
|
{ "INSTALL_INTERFACE", &installInterfaceNode },
|
|
{ "BUILD_LOCAL_INTERFACE", &buildLocalInterfaceNode },
|
|
{ "INSTALL_PREFIX", &installPrefixNode },
|
|
{ "JOIN", &joinNode },
|
|
{ "COMPILE_ONLY", &compileOnlyNode },
|
|
{ "LINK_ONLY", &linkOnlyNode },
|
|
{ "COMPILE_LANG_AND_ID", &languageAndIdNode },
|
|
{ "COMPILE_LANGUAGE", &languageNode },
|
|
{ "LINK_LANG_AND_ID", &linkLanguageAndIdNode },
|
|
{ "LINK_LANGUAGE", &linkLanguageNode },
|
|
{ "LINK_LIBRARY", &linkLibraryNode },
|
|
{ "LINK_GROUP", &linkGroupNode },
|
|
{ "HOST_LINK", &hostLinkNode },
|
|
{ "DEVICE_LINK", &deviceLinkNode },
|
|
{ "SHELL_PATH", &shellPathNode }
|
|
};
|
|
|
|
{
|
|
auto itr = nodeMap.find(identifier);
|
|
if (itr != nodeMap.end()) {
|
|
return itr->second;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void reportError(cmGeneratorExpressionContext* context,
|
|
const std::string& expr, const std::string& result)
|
|
{
|
|
context->HadError = true;
|
|
if (context->Quiet) {
|
|
return;
|
|
}
|
|
|
|
std::ostringstream e;
|
|
/* clang-format off */
|
|
e << "Error evaluating generator expression:\n"
|
|
<< " " << expr << "\n"
|
|
<< result;
|
|
/* clang-format on */
|
|
context->LG->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
|
|
e.str(), context->Backtrace);
|
|
}
|