summaryrefslogtreecommitdiffstats
path: root/Source/cmGeneratorExpressionNode.cxx
diff options
context:
space:
mode:
authorMarc Chevrier <marc.chevrier@gmail.com>2022-05-18 09:15:10 (GMT)
committerMarc Chevrier <marc.chevrier@gmail.com>2022-05-31 13:39:51 (GMT)
commitf11e66670ba9716f45cf757285a77b937dce420b (patch)
treec18b78f99b1bdcc7b36ebbab43727816a07803ed /Source/cmGeneratorExpressionNode.cxx
parent9f4aec6d61f0c4f91cae8f35c5b514d456fba8ff (diff)
downloadCMake-f11e66670ba9716f45cf757285a77b937dce420b.zip
CMake-f11e66670ba9716f45cf757285a77b937dce420b.tar.gz
CMake-f11e66670ba9716f45cf757285a77b937dce420b.tar.bz2
Genex-PATH: path handling
Fixes: #23498
Diffstat (limited to 'Source/cmGeneratorExpressionNode.cxx')
-rw-r--r--Source/cmGeneratorExpressionNode.cxx436
1 files changed, 436 insertions, 0 deletions
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index beb623f..52b771a 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"
@@ -599,6 +602,438 @@ 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 MakeCIdentifierNode : public cmGeneratorExpressionNode
{
MakeCIdentifierNode() {} // NOLINT(modernize-use-equals-default)
@@ -2829,6 +3264,7 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode(
{ "REMOVE_DUPLICATES", &removeDuplicatesNode },
{ "LOWER_CASE", &lowerCaseNode },
{ "UPPER_CASE", &upperCaseNode },
+ { "PATH", &pathNode },
{ "MAKE_C_IDENTIFIER", &makeCIdentifierNode },
{ "BOOL", &boolNode },
{ "IF", &ifNode },