summaryrefslogtreecommitdiffstats
path: root/Source/cmGeneratorExpressionNode.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmGeneratorExpressionNode.cxx')
-rw-r--r--Source/cmGeneratorExpressionNode.cxx677
1 files changed, 669 insertions, 8 deletions
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 396e9c9..45d5a83 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -7,10 +7,12 @@
#include <cerrno>
#include <cstdlib>
#include <cstring>
+#include <functional>
#include <map>
#include <memory>
#include <set>
#include <sstream>
+#include <unordered_map>
#include <utility>
#include <cm/iterator>
@@ -24,6 +26,7 @@
#include "cmsys/String.h"
#include "cmAlgorithms.h"
+#include "cmCMakePath.h"
#include "cmComputeLinkInformation.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionContext.h"
@@ -413,7 +416,7 @@ static const struct TargetExistsNode : public cmGeneratorExpressionNode
return std::string();
}
- std::string targetName = parameters.front();
+ std::string const& targetName = parameters.front();
if (targetName.empty() ||
!cmGeneratorExpression::IsValidTargetName(targetName)) {
reportError(context, content->GetOriginalExpression(),
@@ -445,7 +448,7 @@ static const struct TargetNameIfExistsNode : public cmGeneratorExpressionNode
return std::string();
}
- std::string targetName = parameters.front();
+ std::string const& targetName = parameters.front();
if (targetName.empty() ||
!cmGeneratorExpression::IsValidTargetName(targetName)) {
reportError(context, content->GetOriginalExpression(),
@@ -599,6 +602,455 @@ static const struct UpperCaseNode : public cmGeneratorExpressionNode
}
} 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 CheckPathParametersEx(cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ 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)) {
+ reportError(ctx, cnt->GetOriginalExpression(),
+ cmStrCat("$<PATH:", option, "> expression requires ",
+ (exactly ? "exactly" : "at least"), ' ',
+ (required == 1 ? "one parameter" : "two parameters"),
+ '.'));
+ return false;
+ }
+ return true;
+};
+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 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 {
+ return CheckPathParameters(ctx, cnt, "GET_ROOT_NAME"_s, args) &&
+ !args.front().empty()
+ ? cmCMakePath{ args.front() }.GetRootName().String()
+ : std::string{};
+ } },
+ { "GET_ROOT_DIRECTORY"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ return CheckPathParameters(ctx, cnt, "GET_ROOT_DIRECTORY"_s,
+ args) &&
+ !args.front().empty()
+ ? cmCMakePath{ args.front() }.GetRootDirectory().String()
+ : std::string{};
+ } },
+ { "GET_ROOT_PATH"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ return CheckPathParameters(ctx, cnt, "GET_ROOT_PATH"_s, args) &&
+ !args.front().empty()
+ ? cmCMakePath{ args.front() }.GetRootPath().String()
+ : std::string{};
+ } },
+ { "GET_FILENAME"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ return CheckPathParameters(ctx, cnt, "GET_FILENAME"_s, args) &&
+ !args.front().empty()
+ ? cmCMakePath{ args.front() }.GetFileName().String()
+ : 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{};
+ }
+ return lastOnly
+ ? cmCMakePath{ args.front() }.GetExtension().String()
+ : cmCMakePath{ args.front() }.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{};
+ }
+ return lastOnly
+ ? cmCMakePath{ args.front() }.GetStem().String()
+ : cmCMakePath{ args.front() }.GetNarrowStem().String();
+ }
+ return std::string{};
+ } },
+ { "GET_RELATIVE_PART"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ return CheckPathParameters(ctx, cnt, "GET_RELATIVE_PART"_s,
+ args) &&
+ !args.front().empty()
+ ? cmCMakePath{ args.front() }.GetRelativePath().String()
+ : std::string{};
+ } },
+ { "GET_PARENT_PATH"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ return CheckPathParameters(ctx, cnt, "GET_PARENT_PATH"_s, args)
+ ? cmCMakePath{ args.front() }.GetParentPath().String()
+ : 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)) {
+ auto path =
+ cmCMakePath{ args.front(), cmCMakePath::auto_format };
+ return 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)) {
+ cmCMakePath path;
+ for (const auto& p : args) {
+ path /= p;
+ }
+ return path.String();
+ }
+ return std::string{};
+ } },
+ { "REMOVE_FILENAME"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ return CheckPathParameters(ctx, cnt, "REMOVE_FILENAME"_s, args) &&
+ !args.front().empty()
+ ? cmCMakePath{ args.front() }.RemoveFileName().String()
+ : std::string{};
+ } },
+ { "REPLACE_FILENAME"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ return CheckPathParameters(ctx, cnt, "REPLACE_FILENAME"_s, args, 2)
+ ? cmCMakePath{ args[0] }
+ .ReplaceFileName(cmCMakePath{ args[1] })
+ .String()
+ : 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{};
+ }
+ return lastOnly
+ ? cmCMakePath{ args.front() }.RemoveExtension().String()
+ : cmCMakePath{ args.front() }.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 cmCMakePath{ args[0] }
+ .ReplaceExtension(cmCMakePath{ args[1] })
+ .String();
+ }
+ return cmCMakePath{ args[0] }
+ .ReplaceWideExtension(cmCMakePath{ args[1] })
+ .String();
+ }
+ return std::string{};
+ } },
+ { "NORMAL_PATH"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ return CheckPathParameters(ctx, cnt, "NORMAL_PATH"_s, args) &&
+ !args.front().empty()
+ ? cmCMakePath{ args.front() }.Normal().String()
+ : std::string{};
+ } },
+ { "RELATIVE_PATH"_s,
+ [](cmGeneratorExpressionContext* ctx,
+ const GeneratorExpressionContent* cnt,
+ Arguments& args) -> std::string {
+ return CheckPathParameters(ctx, cnt, "RELATIVE_PATH"_s, args, 2)
+ ? cmCMakePath{ args[0] }.Relative(args[1]).String()
+ : 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)) {
+ auto path = cmCMakePath{ args[0] }.Absolute(args[1]);
+ return 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;
+
static const struct MakeCIdentifierNode : public cmGeneratorExpressionNode
{
MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default)
@@ -1198,6 +1650,162 @@ static const struct LinkLanguageAndIdNode : public cmGeneratorExpressionNode
}
} 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();
+ }
+
+ std::vector<std::string> list;
+ cmExpandLists(parameters.begin(), parameters.end(), list);
+ 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();
+ }
+
+ std::vector<std::string> list;
+ cmExpandLists(parameters.begin(), parameters.end(), list);
+ 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)
@@ -1269,7 +1877,8 @@ static std::string getLinkedTargetsContent(
{
std::string result;
if (cmLinkImplementationLibraries const* impl =
- target->GetLinkImplementationLibraries(context->Config)) {
+ 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
@@ -1612,7 +2221,7 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode
const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
- std::string tgtName = parameters.front();
+ std::string const& tgtName = parameters.front();
cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
if (!gt) {
std::ostringstream e;
@@ -1638,7 +2247,7 @@ static const struct TargetObjectsNode : public cmGeneratorExpressionNode
{
std::string reason;
if (!context->EvaluateForBuildsystem &&
- !gg->HasKnownObjectFileLocation(&reason)) {
+ !gt->Target->HasKnownObjectFileLocation(&reason)) {
std::ostringstream e;
e << "The evaluation of the TARGET_OBJECTS generator expression "
"is only suitable for consumption by CMake (limited"
@@ -1700,7 +2309,7 @@ static const struct TargetRuntimeDllsNode : public cmGeneratorExpressionNode
const GeneratorExpressionContent* content,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
- std::string tgtName = parameters.front();
+ std::string const& tgtName = parameters.front();
cmGeneratorTarget* gt = context->LG->FindGeneratorTargetToUse(tgtName);
if (!gt) {
std::ostringstream e;
@@ -1951,6 +2560,7 @@ class ArtifactPathTag;
class ArtifactPdbTag;
class ArtifactSonameTag;
class ArtifactBundleDirTag;
+class ArtifactBundleDirNameTag;
class ArtifactBundleContentDirTag;
template <typename ArtifactT, typename ComponentT>
@@ -2011,6 +2621,12 @@ struct TargetFilesystemArtifactDependency<ArtifactBundleDirTag,
{
};
template <>
+struct TargetFilesystemArtifactDependency<ArtifactBundleDirNameTag,
+ ArtifactPathTag>
+ : TargetFilesystemArtifactDependencyCMP0112
+{
+};
+template <>
struct TargetFilesystemArtifactDependency<ArtifactBundleContentDirTag,
ArtifactPathTag>
: TargetFilesystemArtifactDependencyCMP0112
@@ -2138,6 +2754,41 @@ struct TargetFilesystemArtifactResultCreator<ArtifactBundleDirTag>
};
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,
@@ -2217,7 +2868,7 @@ protected:
cmGeneratorExpressionDAGChecker* dagChecker) const
{
// Lookup the referenced target.
- std::string name = parameters.front();
+ std::string const& name = parameters.front();
if (!cmGeneratorExpression::IsValidTargetName(name)) {
::reportError(context, content->GetOriginalExpression(),
@@ -2270,7 +2921,8 @@ struct TargetFilesystemArtifact : public TargetArtifactBase
return std::string();
}
// Not a dependent target if we are querying for ArtifactDirTag,
- // ArtifactNameTag, ArtifactBundleDirTag, and ArtifactBundleContentDirTag
+ // ArtifactNameTag, ArtifactBundleDirTag, ArtifactBundleDirNameTag,
+ // and ArtifactBundleContentDirTag
TargetFilesystemArtifactDependency<ArtifactT, ComponentT>::AddDependency(
target, context);
@@ -2311,6 +2963,10 @@ static const TargetFilesystemArtifactNodeGroup<ArtifactPdbTag>
static const TargetFilesystemArtifact<ArtifactBundleDirTag, ArtifactPathTag>
targetBundleDirNode;
+static const TargetFilesystemArtifact<ArtifactBundleDirNameTag,
+ ArtifactNameTag>
+ targetBundleDirNameNode;
+
static const TargetFilesystemArtifact<ArtifactBundleContentDirTag,
ArtifactPathTag>
targetBundleContentDirNode;
@@ -2636,6 +3292,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "TARGET_SONAME_FILE_DIR", &targetSoNameNodeGroup.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 },
@@ -2644,6 +3301,8 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "REMOVE_DUPLICATES", &removeDuplicatesNode },
{ "LOWER_CASE", &lowerCaseNode },
{ "UPPER_CASE", &upperCaseNode },
+ { "PATH", &pathNode },
+ { "PATH_EQUAL", &pathEqualNode },
{ "MAKE_C_IDENTIFIER", &makeCIdentifierNode },
{ "BOOL", &boolNode },
{ "IF", &ifNode },
@@ -2668,6 +3327,8 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "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 }