Genex-PATH: path handling

Fixes: #23498
This commit is contained in:
Marc Chevrier
2022-05-18 11:15:10 +02:00
parent 9f4aec6d61
commit f11e66670b
31 changed files with 1818 additions and 1 deletions

View File

@@ -96,6 +96,8 @@ The following conventions are used in this command's documentation:
The name of a variable into which the result of a command will be written.
.. _Path Structure And Terminology:
Path Structure And Terminology
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -216,6 +218,8 @@ normalize a path is as follows:
is ``.``).
.. _Path Decomposition:
Decomposition
^^^^^^^^^^^^^
@@ -385,6 +389,8 @@ Path traversal examples
Parent path is "c:/"
.. _Path Query:
Query
^^^^^
@@ -495,6 +501,8 @@ according to the following pseudo-code logic:
takes literal strings as input, not the names of variables.
.. _Path Modification:
Modification
^^^^^^^^^^^^
@@ -509,7 +517,7 @@ path, it is converted into a cmake-style path with forward-slashes
(``/``). On Windows, the long filename marker is taken into account.
When the ``NORMALIZE`` option is specified, the path is :ref:`normalized
<Normalization>` before the conversion.
<Normalization>` after the conversion.
For example:
@@ -644,6 +652,8 @@ is equivalent to the following:
cmake_path(APPEND_STRING path "input")
.. _Path Generation:
Generation
^^^^^^^^^^

View File

@@ -123,6 +123,71 @@ String Comparisons
``1`` if ``v1`` is a version greater than or equal to ``v2``, else ``0``.
.. _GenEx Path Queries:
Path Queries
------------
The ``$<PATH>`` generator expression offers the same capabilities as the
:command:`cmake_path` command, for the :ref:`Query <Path Query>` options.
For all ``$<PATH>`` generator expressions, paths are expected in cmake-style
format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>` generator
expression can be used to convert a native path to a cmake-style one.
The ``$<PATH>`` generator expression can also be used for path
:ref:`Decomposition <GenEx Path Decomposition>` and
:ref:`Transformations <GenEx Path Transformations>`.
.. genex:: $<PATH:HAS_*,path>
.. versionadded:: 3.24
The following operations return ``1`` if the particular path component is
present, ``0`` otherwise. See :ref:`Path Structure And Terminology` for the
meaning of each path component.
::
$<PATH:HAS_ROOT_NAME,path>
$<PATH:HAS_ROOT_DIRECTORY,path>
$<PATH:HAS_ROOT_PATH,path>
$<PATH:HAS_FILENAME,path>
$<PATH:HAS_EXTENSION,path>
$<PATH:HAS_STEM,path>
$<PATH:HAS_RELATIVE_PART,path>
$<PATH:HAS_PARENT_PATH,path>
Note the following special cases:
* For ``HAS_ROOT_PATH``, a true result will only be returned if at least one
of ``root-name`` or ``root-directory`` is non-empty.
* For ``HAS_PARENT_PATH``, the root directory is also considered to have a
parent, which will be itself. The result is true except if the path
consists of just a :ref:`filename <FILENAME_DEF>`.
.. genex:: $<PATH:IS_ABSOLUTE,path>
.. versionadded:: 3.24
Returns ``1`` if the path is :ref:`absolute <IS_ABSOLUTE>`, ``0`` otherwise.
.. genex:: $<PATH:IS_RELATIVE,path>
.. versionadded:: 3.24
This will return the opposite of ``IS_ABSOLUTE``.
.. genex:: $<PATH:IS_PREFIX[,NORMALIZE],path,input>
.. versionadded:: 3.24
Returns ``1`` if ``path`` is the prefix of ``input``,``0`` otherwise.
When the ``NORMALIZE`` option is specified, ``path`` and ``input`` are
:ref:`normalized <Normalization>` before the check.
Variable Queries
----------------
@@ -668,6 +733,153 @@ String Transformations
echo $<TARGET_GENEX_EVAL:foo,$<TARGET_PROPERTY:foo,CUSTOM_KEYS>>
)
.. _GenEx Path Decomposition:
Path Decomposition
------------------
The ``$<PATH>`` generator expression offers the same capabilities as the
:command:`cmake_path` command, for the
:ref:`Decomposition <Path Decomposition>` options.
For all ``$<PATH>`` generator expressions, paths are expected in cmake-style
format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>` generator
expression can be used to convert a native path to a cmake-style one.
The ``$<PATH>`` generator expression can also be used for path
:ref:`Queries <GenEx Path Queries>` and
:ref:`Transformations <GenEx Path Transformations>`.
.. genex:: $<PATH:GET_*,...>
.. versionadded:: 3.24
The following operations retrieve a different component or group of
components from a path. See :ref:`Path Structure And Terminology` for the
meaning of each path component.
::
$<PATH:GET_ROOT_NAME,path>
$<PATH:GET_ROOT_DIRECTORY,path>
$<PATH:GET_ROOT_PATH,path>
$<PATH:GET_FILENAME,path>
$<PATH:GET_EXTENSION[,LAST_ONLY],path>
$<PATH:GET_STEM[,LAST_ONLY],path>
$<PATH:GET_RELATIVE_PART,path>
$<PATH:GET_PARENT_PATH,path>
If a requested component is not present in the path, an empty string is
returned.
.. _GenEx Path Transformations:
Path Transformations
--------------------
The ``$<PATH>`` generator expression offers the same capabilities as the
:command:`cmake_path` command, for the
:ref:`Modification <Path Modification>` and
:ref:`Generation <Path Generation>` options.
For all ``$<PATH>`` generator expressions, paths are expected in cmake-style
format. The :ref:`$\<PATH:CMAKE_PATH\> <GenEx PATH-CMAKE_PATH>` generator
expression can be used to convert a native path to a cmake-style one.
The ``$<PATH>`` generator expression can also be used for path
:ref:`Queries <GenEx Path Queries>` and
:ref:`Decomposition <GenEx Path Decomposition>`.
.. _GenEx PATH-CMAKE_PATH:
.. genex:: $<PATH:CMAKE_PATH[,NORMALIZE],path>
.. versionadded:: 3.24
Returns ``path``. If ``path`` is a native path, it is converted into a
cmake-style path with forward-slashes (``/``). On Windows, the long filename
marker is taken into account.
When the ``NORMALIZE`` option is specified, the path is :ref:`normalized
<Normalization>` after the conversion.
.. genex:: $<PATH:APPEND,path,input,...>
.. versionadded:: 3.24
Returns all the ``input`` arguments appended to ``path`` using ``/`` as the
``directory-separator``. Depending on the ``input``, the value of ``path``
may be discarded.
See :ref:`cmake_path(APPEND) <APPEND>` for more details.
.. genex:: $<PATH:REMOVE_FILENAME,path>
.. versionadded:: 3.24
Returns ``path`` with filename component (as returned by
``$<PATH:GET_FILENAME>``) removed. After removal, any trailing
``directory-separator`` is left alone, if present.
See :ref:`cmake_path(REMOVE_FILENAME) <REMOVE_FILENAME>` for more details.
.. genex:: $<PATH:REPLACE_FILENAME,path,input>
.. versionadded:: 3.24
Returns ``path`` with the filename component replaced by ``input``. If
``path`` has no filename component (i.e. ``$<PATH:HAS_FILENAME>`` returns
``0``), ``path`` is unchanged.
See :ref:`cmake_path(REPLACE_FILENAME) <REPLACE_FILENAME>` for more details.
.. genex:: $<PATH:REMOVE_EXTENSION[,LAST_ONLY],path>
.. versionadded:: 3.24
Returns ``path`` with the :ref:`extension <EXTENSION_DEF>` removed, if any.
See :ref:`cmake_path(REMOVE_EXTENSION) <REMOVE_EXTENSION>` for more details.
.. genex:: $<PATH:REPLACE_EXTENSION[,LAST_ONLY],path>
.. versionadded:: 3.24
Returns ``path`` with the :ref:`extension <EXTENSION_DEF>` replaced by
``input``, if any.
See :ref:`cmake_path(REPLACE_EXTENSION) <REPLACE_EXTENSION>` for more details.
.. genex:: $<PATH:NORMAL_PATH,path>
.. versionadded:: 3.24
Returns ``path`` normalized according to the steps described in
:ref:`Normalization`.
.. genex:: $<PATH:RELATIVE_PATH,path,base_directory>
.. versionadded:: 3.24
Returns ``path``, modified to make it relative to the ``base_directory``
argument.
See :ref:`cmake_path(RELATIVE_PATH) <cmake_path-RELATIVE_PATH>` for more
details.
.. genex:: $<PATH:ABSOLUTE_PATH[,NORMALIZE],path,base_directory>
.. versionadded:: 3.24
Returns ``path`` as absolute. If ``path`` is a relative path
(``$<PATH:IS_RELATIVE>`` returns ``1``), it is evaluated relative to the
given base directory specified by ``base_directory`` argument.
When the ``NORMALIZE`` option is specified, the path is
:ref:`normalized <Normalization>` after the path computation.
See :ref:`cmake_path(ABSOLUTE_PATH) <ABSOLUTE_PATH>` for more details.
Variable Queries
----------------

View File

@@ -0,0 +1,4 @@
Genex-PATH
----------
* The :genex:`PATH` generator expression was added to manage paths.

View File

@@ -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 },

View File

@@ -330,6 +330,7 @@ add_RunCMake_test(GenEx-LINK_GROUP)
add_RunCMake_test(GenEx-TARGET_FILE -DLINKER_SUPPORTS_PDB=${LINKER_SUPPORTS_PDB})
add_RunCMake_test(GenEx-GENEX_EVAL)
add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
add_RunCMake_test(GenEx-PATH)
add_RunCMake_test(GeneratorExpression)
add_RunCMake_test(GeneratorInstance)
add_RunCMake_test(GeneratorPlatform)

View File

@@ -0,0 +1,34 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set (reference "../../a/d")
cmake_path(ABSOLUTE_PATH reference BASE_DIRECTORY "/x/y/a/f")
set(output "$<PATH:ABSOLUTE_PATH,../../a/d,/x/y/a/f>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "../../a/d")
cmake_path(ABSOLUTE_PATH reference BASE_DIRECTORY "/x/y/a/f" NORMALIZE)
set(output "$<PATH:ABSOLUTE_PATH,NORMALIZE,../../a/d,/x/y/a/f>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "/a/d/../e")
cmake_path(ABSOLUTE_PATH reference BASE_DIRECTORY "/x/y/a/f")
set(output "$<PATH:ABSOLUTE_PATH,/a/d/../e,/x/y/a/f>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "/a/d/../e")
cmake_path(ABSOLUTE_PATH reference BASE_DIRECTORY "/x/y/a/f" NORMALIZE)
set(output "$<PATH:ABSOLUTE_PATH,NORMALIZE,/a/d/../e,/x/y/a/f>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
check_errors("PATH:ABSOLUTE_PATH" ${errors})

View File

@@ -0,0 +1,68 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
cmake_path (APPEND path "/a/b" "c")
set(output "$<PATH:APPEND,/a/b,c>")
if (NOT output STREQUAL path)
list (APPEND errors "'${output}' instead of '${path}'")
endif()
set (path "a")
cmake_path (APPEND path "")
set(output "$<PATH:APPEND,a,>")
if (NOT output STREQUAL path)
list (APPEND errors "'${output}' instead of '${path}'")
endif()
cmake_path (APPEND path "/b")
set(output "$<PATH:APPEND,a/,/b>")
if (NOT output STREQUAL path)
list (APPEND errors "'${output}' instead of '${path}'")
endif()
if (WIN32)
set (path "a")
cmake_path (APPEND path "c:/b")
set(output "$<PATH:APPEND,a,c:/b>")
if (NOT output STREQUAL path)
list (APPEND errors "'${output}' instead of '${path}'")
endif()
set (path "a")
cmake_path (APPEND path "c:")
set(output "$<PATH:APPEND,a,c:>")
if (NOT output STREQUAL path)
list (APPEND errors "'${output}' instead of '${path}'")
endif()
set (path "c:a")
cmake_path (APPEND path "/b")
set(output "$<PATH:APPEND,c:a,/b>")
if (NOT output STREQUAL path)
list (APPEND errors "'${output}' instead of '${path}'")
endif()
set (path "c:a")
cmake_path (APPEND path "c:b")
set(output "$<PATH:APPEND,c:a,c:b>")
if (NOT output STREQUAL path)
list (APPEND errors "'${output}' instead of '${path}'")
endif()
set (path "//host")
cmake_path (APPEND path "b")
set(output "$<PATH:APPEND,//host,b>")
if (NOT output STREQUAL path)
list (APPEND errors "'${output}' instead of '${path}'")
endif()
set (path "//host/")
cmake_path (APPEND path "b")
set(output "$<PATH:APPEND,//host/,b>")
if (NOT output STREQUAL path)
list (APPEND errors "'${output}' instead of '${path}'")
endif()
endif()
check_errors ("PATH:APPEND" ${errors})

View File

@@ -0,0 +1,53 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
cmake_path(SET reference "/x/y/z/../../a/d")
set(output "$<PATH:CMAKE_PATH,/x/y/z/../../a/d>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
cmake_path(SET reference NORMALIZE "/x/y/z/../../a/d")
set(output "$<PATH:CMAKE_PATH,NORMALIZE,/x/y/z/../../a/d>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
if (WIN32)
cmake_path(SET reference "/x\\y/z\\..\\../a/d")
set(output "$<PATH:CMAKE_PATH,/x\y/z\..\../a/d>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
cmake_path(SET reference NORMALIZE "/x\\y/z\\..\\../a/d")
set(output "$<PATH:CMAKE_PATH,NORMALIZE,/x\y/z\..\../a/d>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
cmake_path(SET reference "//?/c:/x\\y/z\\..\\../a/d")
set(output "$<PATH:CMAKE_PATH,//?/c:/x\y/z\..\../a/d>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
cmake_path(SET reference NORMALIZE "//?/c:/x\\y/z\\..\\../a/d")
set(output "$<PATH:CMAKE_PATH,NORMALIZE,//?/c:/x\y/z\..\../a/d>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
cmake_path(SET reference "\\\\?\\UNC/host/x\\y/z\\..\\../a/d")
set(output "$<PATH:CMAKE_PATH,\\?\UNC/host/x\y/z\..\../a/d>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
cmake_path(SET reference NORMALIZE "\\\\?\\UNC\\host/x\\y/z\\..\\../a/d")
set(output "$<PATH:CMAKE_PATH,NORMALIZE,\\?\UNC/host/x\y/z\..\../a/d>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
endif()
check_errors("PATH:CMAKE_PATH" ${errors})

View File

@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.18...3.24)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)

View File

@@ -0,0 +1,311 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
###############################################
## First test with a path defining all elements
###############################################
if (WIN32)
set (path "C:/aa/bb/cc.ext1.ext2")
else()
set (path "/aa/bb/cc.ext1.ext2")
endif()
cmake_path(GET path ROOT_NAME reference)
if (WIN32)
set(output "$<PATH:GET_ROOT_NAME,C:/aa/bb/cc.ext1.ext2>")
else()
set (output "$<PATH:GET_ROOT_NAME,/aa/bb/cc.ext1.ext2>")
endif()
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_NAME returns bad data: ${output}")
endif()
cmake_path(GET path ROOT_DIRECTORY reference)
if (WIN32)
set(output "$<PATH:GET_ROOT_DIRECTORY,C:/aa/bb/cc.ext1.ext2>")
else()
set (output "$<PATH:GET_ROOT_DIRECTORY,/aa/bb/cc.ext1.ext2>")
endif()
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_DIRECTORY returns bad data: ${output}")
endif()
cmake_path(GET path ROOT_PATH reference)
if (WIN32)
set(output "$<PATH:GET_ROOT_PATH,C:/aa/bb/cc.ext1.ext2>")
else()
set (output "$<PATH:GET_ROOT_PATH,/aa/bb/cc.ext1.ext2>")
endif()
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_PATH returns bad data: ${output}")
endif()
cmake_path(GET path FILENAME reference)
if (WIN32)
set(output "$<PATH:GET_FILENAME,C:/aa/bb/cc.ext1.ext2>")
else()
set (output "$<PATH:GET_FILENAME,/aa/bb/cc.ext1.ext2>")
endif()
if (NOT output STREQUAL reference)
list (APPEND errors "FILENAME returns bad data: ${output}")
endif()
cmake_path(GET path EXTENSION reference)
if (WIN32)
set(output "$<PATH:GET_EXTENSION,C:/aa/bb/cc.ext1.ext2>")
else()
set (output "$<PATH:GET_EXTENSION,/aa/bb/cc.ext1.ext2>")
endif()
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION returns bad data: ${output}")
endif()
cmake_path(GET path EXTENSION LAST_ONLY reference)
if (WIN32)
set(output "$<PATH:GET_EXTENSION,LAST_ONLY,C:/aa/bb/cc.ext1.ext2>")
else()
set (output "$<PATH:GET_EXTENSION,LAST_ONLY,/aa/bb/cc.ext1.ext2>")
endif()
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION LAST_ONLY returns bad data: ${output}")
endif()
cmake_path(GET path STEM reference)
if (WIN32)
set(output "$<PATH:GET_STEM,C:/aa/bb/cc.ext1.ext2>")
else()
set (output "$<PATH:GET_STEM,/aa/bb/cc.ext1.ext2>")
endif()
if (NOT output STREQUAL reference)
list (APPEND errors "STEM returns bad data: ${output}")
endif()
cmake_path(GET path STEM LAST_ONLY reference)
if (WIN32)
set(output "$<PATH:GET_STEM,LAST_ONLY,C:/aa/bb/cc.ext1.ext2>")
else()
set (output "$<PATH:GET_STEM,LAST_ONLY,/aa/bb/cc.ext1.ext2>")
endif()
if (NOT output STREQUAL reference)
list (APPEND errors "STEM LAST_ONLY returns bad data: ${reference}")
endif()
cmake_path(GET path RELATIVE_PART reference)
if (WIN32)
set(output "$<PATH:GET_RELATIVE_PART,C:/aa/bb/cc.ext1.ext2>")
else()
set (output "$<PATH:GET_RELATIVE_PART,/aa/bb/cc.ext1.ext2>")
endif()
if (NOT output STREQUAL reference)
list (APPEND errors "RELATIVE_PART returns bad data: ${output}")
endif()
cmake_path(GET path PARENT_PATH reference)
if (WIN32)
set(output "$<PATH:GET_PARENT_PATH,C:/aa/bb/cc.ext1.ext2>")
else()
set (output "$<PATH:GET_PARENT_PATH,/aa/bb/cc.ext1.ext2>")
endif()
if (NOT output STREQUAL reference)
list (APPEND errors "PARENT_PATH returns bad data: ${output}")
endif()
######################################
## second, tests with missing elements
######################################
set (path "aa/bb/")
cmake_path(GET path ROOT_NAME reference)
set(output "$<PATH:GET_ROOT_NAME,aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_NAME returns bad data: ${output}")
endif()
cmake_path(GET path ROOT_DIRECTORY reference)
set(output "$<PATH:GET_ROOT_DIRECTORY,aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_DIRECTORY returns bad data: >${output}<, >${reference}<")
endif()
cmake_path(GET path ROOT_PATH reference)
set(output "$<PATH:GET_ROOT_PATH,aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_PATH returns bad data: ${output}")
endif()
cmake_path(GET path FILENAME reference)
set(output "$<PATH:GET_FILENAME,aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "FILENAME returns bad data: ${output}")
endif()
cmake_path(GET path EXTENSION reference)
set(output "$<PATH:GET_EXTENSION,aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION returns bad data: ${output}")
endif()
cmake_path(GET path STEM reference)
set(output "$<PATH:GET_STEM,aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "STEM returns bad data: ${output}")
endif()
cmake_path(GET path RELATIVE_PART reference)
set(output "$<PATH:GET_RELATIVE_PART,aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "RELATIVE_PART returns bad data: ${output}")
endif()
cmake_path(GET path PARENT_PATH reference)
set(output "$<PATH:GET_PARENT_PATH,aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "PARENT_PATH returns bad data: ${output}")
endif()
##################################
set (path "/aa/bb/")
cmake_path(GET path ROOT_NAME reference)
set(output "$<PATH:GET_ROOT_NAME,/aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_NAME returns bad data: ${output}")
endif()
cmake_path(GET path ROOT_DIRECTORY reference)
set(output "$<PATH:GET_ROOT_DIRECTORY,/aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_DIRECTORY returns bad data: ${output}")
endif()
cmake_path(GET path ROOT_PATH reference)
set(output "$<PATH:GET_ROOT_PATH,/aa/bb/>")
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_PATH returns bad data: ${output}")
endif()
###################################
set (path "/")
cmake_path(GET path ROOT_NAME reference)
set(output "$<PATH:GET_ROOT_NAME,/>")
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_NAME returns bad data: ${output}")
endif()
cmake_path(GET path ROOT_DIRECTORY reference)
set(output "$<PATH:GET_ROOT_DIRECTORY,/>")
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_DIRECTORY returns bad data: ${output}")
endif()
cmake_path(GET path ROOT_PATH reference)
set(output "$<PATH:GET_ROOT_PATH,/>")
if (NOT output STREQUAL reference)
list (APPEND errors "ROOT_PATH returns bad data: ${output}")
endif()
cmake_path(GET path FILENAME reference)
set(output "$<PATH:GET_FILENAME,/>")
if (NOT output STREQUAL reference)
list (APPEND errors "FILENAME returns bad data: ${output}")
endif()
cmake_path(GET path EXTENSION reference)
set(output "$<PATH:GET_EXTENSION,/>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION returns bad data: ${output}")
endif()
cmake_path(GET path STEM reference)
set(output "$<PATH:GET_STEM,/>")
if (NOT output STREQUAL reference)
list (APPEND errors "STEM returns bad data: ${output}")
endif()
cmake_path(GET path RELATIVE_PART reference)
set(output "$<PATH:GET_RELATIVE_PART,/>")
if (NOT output STREQUAL reference)
list (APPEND errors "RELATIVE_PART returns bad data: ${output}")
endif()
cmake_path(GET path PARENT_PATH reference)
set(output "$<PATH:GET_PARENT_PATH,/>")
if (NOT output STREQUAL reference)
list (APPEND errors "PARENT_PATH returns bad data: ${output}")
endif()
###################################
set (path ".file")
cmake_path(GET path FILENAME reference)
set(output "$<PATH:GET_FILENAME,.file>")
if (NOT output STREQUAL reference)
list (APPEND errors "FILENAME returns bad data: ${output}")
endif()
cmake_path(GET path EXTENSION reference)
set(output "$<PATH:GET_EXTENSION,.file>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION returns bad data: ${output}")
endif()
cmake_path(GET path STEM reference)
set(output "$<PATH:GET_STEM,.file>")
if (NOT output STREQUAL reference)
list (APPEND errors "STEM returns bad data: ${output}")
endif()
###################################
set (path ".file.ext")
cmake_path(GET path FILENAME reference)
set(output "$<PATH:GET_FILENAME,.file.ext>")
if (NOT output STREQUAL reference)
list (APPEND errors "FILENAME returns bad data: ${output}")
endif()
cmake_path(GET path EXTENSION reference)
set(output "$<PATH:GET_EXTENSION,.file.ext>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION returns bad data: ${output}")
endif()
cmake_path(GET path EXTENSION LAST_ONLY reference)
set(output "$<PATH:GET_EXTENSION,LAST_ONLY,.file.ext>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION returns bad data: ${output}")
endif()
cmake_path(GET path STEM reference)
set(output "$<PATH:GET_STEM,.file.ext>")
if (NOT output STREQUAL reference)
list (APPEND errors "STEM returns bad data: ${output}")
endif()
###################################
set (path ".file.ext1.ext2")
cmake_path(GET path FILENAME reference)
set(output "$<PATH:GET_FILENAME,.file.ext1.ext2>")
if (NOT output STREQUAL reference)
list (APPEND errors "FILENAME returns bad data: ${output}")
endif()
cmake_path(GET path EXTENSION reference)
set(output "$<PATH:GET_EXTENSION,.file.ext1.ext2>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION returns bad data: ${output}")
endif()
cmake_path(GET path EXTENSION LAST_ONLY reference)
set(output "$<PATH:GET_EXTENSION,LAST_ONLY,.file.ext1.ext2>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION returns bad data: ${output}")
endif()
cmake_path(GET path STEM reference)
set(output "$<PATH:GET_STEM,.file.ext1.ext2>")
if (NOT output STREQUAL reference)
list (APPEND errors "STEM returns bad data: ${output}")
endif()
check_errors("PATH:GET..." ${errors})

View File

@@ -0,0 +1,199 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set(output "$<PATH:HAS_ROOT_NAME,/a/b>")
if (output)
list (APPEND errors "ROOT_NAME: '/a/b' has root name")
endif()
set(output "$<PATH:HAS_ROOT_DIRECTORY,/a/b>")
if (NOT output)
list (APPEND errors "ROOT_DIRECTORY: '/a/b' does not have root directory")
endif()
set(output "$<PATH:HAS_ROOT_PATH,/a/b>")
if (NOT output)
list (APPEND errors "ROOT_PATH: '/a/b' does not have root path")
endif()
set(output "$<PATH:HAS_ROOT_PATH,a/b>")
if (output)
list (APPEND errors "ROOT_PATH: 'a/b' has root path")
endif()
set(output "$<PATH:HAS_FILENAME,/a/b>")
if (NOT output)
list (APPEND errors "FILENAME: '/a/b' does not have filename")
endif()
set(output "$<PATH:HAS_FILENAME,a.b>")
if (NOT output)
list (APPEND errors "FILENAME: 'a.b' does not have filename")
endif()
set(output "$<PATH:HAS_FILENAME,/a/b/>")
if (output)
list (APPEND errors "FILENAME: '/a/b/' has filename")
endif()
set(output "$<PATH:HAS_FILENAME,/>")
if (output)
list (APPEND errors "FILENAME: '/' has filename")
endif()
set(output "$<PATH:HAS_STEM,/a/b>")
if (NOT output)
list (APPEND errors "STEM: '/a/b' does not have stem")
endif()
set(output "$<PATH:HAS_STEM,a.b>")
if (NOT output)
list (APPEND errors "STEM: 'a.b' does not have stem")
endif()
set(output "$<PATH:HAS_STEM,.a>")
if (NOT output)
list (APPEND errors "STEM: '.a'} does not have stem")
endif()
set(output "$<PATH:HAS_STEM,/a/>")
if (output)
list (APPEND errors "STEM: '/a/' has stem")
endif()
set(output "$<PATH:HAS_STEM,/>")
if (output)
list (APPEND errors "STEM: '/' has stem")
endif()
set(output "$<PATH:HAS_EXTENSION,/a/b.c>")
if (NOT output)
list (APPEND errors "EXTENSION: '/a/b.c' does not have extension")
endif()
set(output "$<PATH:HAS_EXTENSION,b.c>")
if (NOT output)
list (APPEND errors "EXTENSION: 'b.c' does not have extension")
endif()
set(output "$<PATH:HAS_EXTENSION,/.a>")
if (output)
list (APPEND errors "EXTENSION: '/.a' has extension")
endif()
set(output "$<PATH:HAS_EXTENSION,/a/>")
if (output)
list (APPEND errors "EXTENSION: '/a/' has extension")
endif()
set(output "$<PATH:HAS_EXTENSION,/>")
if (output)
list (APPEND errors "EXTENSION: '/' has extension")
endif()
set(output "$<PATH:HAS_RELATIVE_PART,/a/b>")
if (NOT output)
list (APPEND errors "RELATIVE_PART: '/a/b' does not have relative part")
endif()
set(output "$<PATH:HAS_RELATIVE_PART,/>")
if (output)
list (APPEND errors "RELATIVE_PART: '/' has relative part")
endif()
set(output "$<PATH:HAS_PARENT_PATH,/a/b>")
if (NOT output)
list (APPEND errors "PARENT_PATH: '/a/b' does not have parent path")
endif()
set(output "$<PATH:HAS_PARENT_PATH,/>")
if (NOT output)
list (APPEND errors "PARENT_PATH: '/' does not have parent path")
endif()
set(output "$<PATH:HAS_PARENT_PATH,a>")
if (output)
list (APPEND errors "PARENT_PATH: 'a' has parent path")
endif()
if (WIN32)
set(output "$<PATH:HAS_ROOT_NAME,c:/a/b>")
if (NOT output)
list (APPEND errors "ROOT_NAME: 'c:/a/b' does not have root name")
endif()
set(output "$<PATH:HAS_ROOT_DIRECTORY,c:/a/b>")
if (NOT output)
list (APPEND errors "ROOT_DIRECTORY: 'c:/a/b' does not have root directory")
endif()
set(output "$<PATH:HAS_ROOT_PATH,c:/a/b>")
if (NOT output)
list (APPEND errors "ROOT_PATH: 'c:/a/b' does not have root path")
endif()
set(output "$<PATH:HAS_ROOT_NAME,c:a/b>")
if (NOT output)
list (APPEND errors "ROOT_NAME: 'c:a/b' does not have root name")
endif()
set(output "$<PATH:HAS_ROOT_DIRECTORY,c:a/b>")
if (output)
list (APPEND errors "ROOT_DIRECTORY: 'c:a/b' has root directory")
endif()
set(output "$<PATH:HAS_ROOT_PATH,c:a/b>")
if (NOT output)
list (APPEND errors "ROOT_PATH: 'c:a/b' does not have root path")
endif()
set(output "$<PATH:HAS_ROOT_NAME,//host/b>")
if (NOT output)
list (APPEND errors "ROOT_NAME: '//host/b' does not have root name")
endif()
set(output "$<PATH:HAS_ROOT_DIRECTORY,//host/b>")
if (NOT output)
list (APPEND errors "ROOT_DIRECTORY: '//host/b' does not have root directory")
endif()
set(output "$<PATH:HAS_ROOT_PATH,//host/b>")
if (NOT output)
list (APPEND errors "ROOT_PATH: '//host/b' does not have root path")
endif()
set(output "$<PATH:HAS_ROOT_NAME,//host>")
if (NOT output)
list (APPEND errors "ROOT_NAME: '//host' does not have root name")
endif()
set(output "$<PATH:HAS_ROOT_DIRECTORY,//host>")
if (output)
list (APPEND errors "ROOT_DIRECTORY: '//host' has root directory")
endif()
set(output "$<PATH:HAS_ROOT_PATH,//host>")
if (NOT output)
list (APPEND errors "ROOT_PATH: '//host' does not have root path")
endif()
set(output "$<PATH:HAS_RELATIVE_PART,c:/a/b>")
if (NOT output)
list (APPEND errors "RELATIVE_PART: 'c:/a/b' does not have relative part")
endif()
set(output "$<PATH:HAS_RELATIVE_PART,c:a/b>")
if (NOT output)
list (APPEND errors "RELATIVE_PART: 'c:a/b' does not have relative part")
endif()
set(output "$<PATH:HAS_RELATIVE_PART,//host/b>")
if (NOT output)
list (APPEND errors "RELATIVE_PART: '//host/b' does not have relative part")
endif()
set(output "$<PATH:HAS_PARENT_PATH,c:/a/b>")
if (NOT output)
list (APPEND errors "PARENT_PATH: 'c:/a/b' does not have parent path")
endif()
set(output "$<PATH:HAS_PARENT_PATH,c:/>")
if (NOT output)
list (APPEND errors "PARENT_PATH: 'c:/' does not have parent path")
endif()
set(output "$<PATH:HAS_PARENT_PATH,c:>")
if (NOT output)
list (APPEND errors "PARENT_PATH: 'c:' does not have parent path")
endif()
set(output "$<PATH:HAS_PARENT_PATH,//host/>")
if (NOT output)
list (APPEND errors "PARENT_PATH: '//host/' does not have parent path")
endif()
set(output "$<PATH:HAS_PARENT_PATH,//host>")
if (NOT output)
list (APPEND errors "PARENT_PATH: '//host' does not have parent path")
endif()
endif()
check_errors ("PATH:HAS..." ${errors})

View File

@@ -0,0 +1,44 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
if (WIN32)
set(path "c:/a")
set(output "$<PATH:IS_ABSOLUTE,c:/a>")
else()
set(path "/a")
set(output "$<PATH:IS_ABSOLUTE,/a>")
endif()
if (NOT output)
list (APPEND errors "'${path}' is not absolute")
endif()
set(output "$<PATH:IS_ABSOLUTE,a/b>")
if (output)
list (APPEND errors "'a/b' is absolute")
endif()
if (WIN32)
set(output "$<PATH:IS_ABSOLUTE,c:/a/b>")
if (NOT output)
list (APPEND errors "'c:/a/b' is not absolute")
endif()
set(output "$<PATH:IS_ABSOLUTE,//host/b>")
if (NOT output)
list (APPEND errors "'//host/b' is not absolute")
endif()
set(output "$<PATH:IS_ABSOLUTE,/a>")
if (output)
list (APPEND errors "'/a' is absolute")
endif()
set(output "$<PATH:IS_ABSOLUTE,c:a>")
if (output)
list (APPEND errors "'c:a' is absolute")
endif()
endif()
check_errors("PATH:IS_ABSOLUTE" ${errors})

View File

@@ -0,0 +1,25 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set(output "$<PATH:IS_PREFIX,a///b/c,a/b/c/d>")
if (NOT output)
list (APPEND errors "'a///b/c' is not prefix of 'a/b/c/d'")
endif()
set(output "$<PATH:IS_PREFIX,a///b/c/../d,a/b/d/e>")
if (output)
list (APPEND errors "'a///b/c/../d' is prefix of 'a/b/d/e'")
endif()
set(output "$<PATH:IS_PREFIX,NORMALIZE,a///b/c/../d,a/b/d/e>")
if (NOT output)
list (APPEND errors "'a///b/c/../d' is not prefix of 'a/b/d/e'")
endif()
set(output "$<PATH:IS_PREFIX,NORMALIZE,/a/b/..,/a/c/../b")
if (NOT output)
list (APPEND errors "'/a/b/..' is not prefix of '/a/c/../b'")
endif()
check_errors("PATH:IS_PREFIX" ${errors})

View File

@@ -0,0 +1,45 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
if (WIN32)
set(path "c:/a")
set(output "$<PATH:IS_RELATIVE,c:/a>")
else()
set(path "/a")
set(output "$<PATH:IS_RELATIVE,/a>")
endif()
if (output)
list (APPEND errors "'${path} is relative")
endif()
set(output "$<PATH:IS_RELATIVE,a/b>")
if (NOT output)
list (APPEND errors "'a/b' is not relative")
endif()
if (WIN32)
set(output "$<PATH:IS_RELATIVE,c:/a/b>")
if (output)
list (APPEND errors "'c:/a/b' is relative")
endif()
set(output "$<PATH:IS_RELATIVE,//host/b>")
if (output)
list (APPEND errors "'//host/b' is relative")
endif()
set(output "$<PATH:IS_RELATIVE,/a>")
if (NOT output)
list (APPEND errors "'/a' is not relative")
endif()
set(output "$<PATH:IS_RELATIVE,c:a>")
if (NOT output)
list (APPEND errors "'c:a' is not relative")
endif()
endif()
check_errors("PATH:IS_RELATIVE" ${errors})

View File

@@ -0,0 +1,43 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set (reference "a/./b/..")
cmake_path(NORMAL_PATH reference)
set(output "$<PATH:NORMAL_PATH,a/./b/..>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "a/.///b/../")
cmake_path(NORMAL_PATH reference)
set(output "$<PATH:NORMAL_PATH,a/.///b/../>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
if (WIN32)
set (reference "//host/./b/..")
cmake_path(NORMAL_PATH reference)
set(output "$<PATH:NORMAL_PATH,//host/./b/..>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "//host/./b/../")
cmake_path(NORMAL_PATH reference)
set(output "$<PATH:NORMAL_PATH,//host/./b/../>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "c://a/.///b/../")
cmake_path(NORMAL_PATH reference)
set(output "$<PATH:NORMAL_PATH,c://a/.///b/../>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
endif()
check_errors("PATH:NORMAL_PATH" ${errors})

View File

@@ -0,0 +1,64 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set (reference "/a//d")
cmake_path(RELATIVE_PATH reference BASE_DIRECTORY "/a/b/c")
set(output "$<PATH:RELATIVE_PATH,/a//d,/a/b/c>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "/a//b///c")
cmake_path(RELATIVE_PATH reference BASE_DIRECTORY "/a/d")
set(output "$<PATH:RELATIVE_PATH,/a/b///c,/a/d>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "a/b/c")
cmake_path(RELATIVE_PATH reference BASE_DIRECTORY "a")
set(output "$<PATH:RELATIVE_PATH,a/b/c,a>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "a/b/c")
cmake_path(RELATIVE_PATH reference BASE_DIRECTORY "a/b/c/x/y")
set(output "$<PATH:RELATIVE_PATH,a/b/c,a/b/c/x/y>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "a/b/c")
cmake_path(RELATIVE_PATH reference BASE_DIRECTORY "a/b/c")
set(output "$<PATH:RELATIVE_PATH,a/b/c,a/b/c>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "a/b")
cmake_path(RELATIVE_PATH reference BASE_DIRECTORY "c/d")
set(output "$<PATH:RELATIVE_PATH,a/b,c/d>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
set (reference "/a/d")
cmake_path(RELATIVE_PATH reference BASE_DIRECTORY "e/d/c")
set(output "$<PATH:RELATIVE_PATH,/a/d,e/d/c>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
if (WIN32)
set (reference "c:/a/d")
cmake_path(RELATIVE_PATH reference BASE_DIRECTORY "e/d/c")
set(output "$<PATH:RELATIVE_PATH,c:/a/d,e/d/c>")
if (NOT output STREQUAL reference)
list (APPEND errors "'${output}' instead of '${reference}'")
endif()
endif()
check_errors("PATH:RELATIVE_PATH" ${errors})

View File

@@ -0,0 +1,65 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set (reference "a/b/c.e.f")
cmake_path (REMOVE_FILENAME reference)
set(output "$<PATH:REMOVE_FILENAME,a/b/c.e.f>")
if (NOT output STREQUAL reference)
list (APPEND errors "FILENAME: '${output}' instead of '${reference}'")
endif()
cmake_path (REMOVE_FILENAME reference)
set(output "$<PATH:REMOVE_FILENAME,a/b/>")
if (NOT output STREQUAL reference)
list (APPEND errors "FILENAME: '${output}' instead of '${reference}'")
endif()
set (reference "a/b/c.e.f")
cmake_path (REMOVE_EXTENSION reference)
set(output "$<PATH:REMOVE_EXTENSION,a/b/c.e.f>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
set (reference "a/b/c.e.f")
cmake_path (REMOVE_EXTENSION reference LAST_ONLY)
set(output "$<PATH:REMOVE_EXTENSION,LAST_ONLY,a/b/c.e.f>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
cmake_path (REMOVE_EXTENSION reference)
set(output "$<PATH:REMOVE_EXTENSION,a/b/c.e>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
set (reference "a/b/c")
cmake_path (REMOVE_EXTENSION reference)
set(output "$<PATH:REMOVE_EXTENSION,a/b/c>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
set (reference "a/b/.c")
cmake_path (REMOVE_EXTENSION reference)
set(output "$<PATH:REMOVE_EXTENSION,a/b/.c>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
cmake_path (REMOVE_EXTENSION reference LAST_ONLY)
set(output "$<PATH:REMOVE_EXTENSION,LAST_ONLY,a/b/.c>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
set (reference "a/b/.")
cmake_path (REMOVE_EXTENSION reference LAST_ONLY)
set(output "$<PATH:REMOVE_EXTENSION,LAST_ONLY,a/b/.>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
check_errors("PATH:REMOVE..." ${errors})

View File

@@ -0,0 +1,73 @@
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set (reference "a/b/c.e.f")
cmake_path (REPLACE_FILENAME reference "x.y")
set(output "$<PATH:REPLACE_FILENAME,a/b/c.e.f,x.y>")
if (NOT output STREQUAL reference)
list (APPEND errors "FILENAME: '${output}' instead of '${reference}'")
endif()
set (reference "a/b/")
cmake_path (REPLACE_FILENAME reference "x.y")
set(output "$<PATH:REPLACE_FILENAME,a/b/,x.y>")
if (NOT output STREQUAL reference)
list (APPEND errors "FILENAME: '${output}' instead of '${reference}'")
endif()
set (reference "a/b/c.e.f")
cmake_path (REPLACE_EXTENSION reference ".x")
set(output "$<PATH:REPLACE_EXTENSION,a/b/c.e.f,.x>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
cmake_path (REPLACE_EXTENSION reference ".y")
set(output "$<PATH:REPLACE_EXTENSION,a/b/c.x,.y>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
cmake_path (REPLACE_EXTENSION reference "")
set(output "$<PATH:REPLACE_EXTENSION,a/b/c.y,>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
set (reference "a/b/c.e.f")
cmake_path (REPLACE_EXTENSION reference ".x" LAST_ONLY)
set(output "$<PATH:REPLACE_EXTENSION,LAST_ONLY,a/b/c.e.f,.x>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
cmake_path (REPLACE_EXTENSION reference ".y" LAST_ONLY)
set(output "$<PATH:REPLACE_EXTENSION,LAST_ONLY,a/b/c.e.x,.y>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
cmake_path (REPLACE_EXTENSION reference "" LAST_ONLY)
set(output "$<PATH:REPLACE_EXTENSION,LAST_ONLY,a/b/c.e.y,>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
set (reference "/a/.b")
cmake_path (REPLACE_EXTENSION reference ".x")
set(output "$<PATH:REPLACE_EXTENSION,/a/.b,.x>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '/${reference}'")
endif()
cmake_path (REPLACE_EXTENSION reference ".x" LAST_ONLY)
set(output "$<PATH:REPLACE_EXTENSION,LAST_ONLY,/a/.b.x,.x>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
set (reference "/a/b")
cmake_path (REPLACE_EXTENSION reference ".x")
set(output "$<PATH:REPLACE_EXTENSION,/a/b,.x>")
if (NOT output STREQUAL reference)
list (APPEND errors "EXTENSION: '${output}' instead of '${reference}'")
endif()
check_errors("PATH:REPLACE..." ${errors})

View File

@@ -0,0 +1,68 @@
include(RunCMake)
run_cmake(no-arguments)
run_cmake(bad-option)
function(check_path_syntax name test)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-${test}-build)
set(RunCMake_TEST_VARIANT_DESCRIPTION " - ${name}")
run_cmake_with_options(${test} ${ARGN})
endfunction()
## Unexpected arguments
### sub-commands with one argument
foreach (subcommand IN ITEMS GET_ROOT_NAME GET_ROOT_DIRECTORY GET_ROOT_PATH GET_FILENAME
GET_EXTENSION GET_STEM GET_RELATIVE_PART GET_PARENT_PATH
HAS_ROOT_NAME HAS_ROOT_DIRECTORY HAS_ROOT_PATH HAS_FILENAME
HAS_EXTENSION HAS_STEM HAS_RELATIVE_PART HAS_PARENT_PATH
IS_ABSOLUTE IS_RELATIVE CMAKE_PATH REMOVE_FILENAME REMOVE_EXTENSION
NORMAL_PATH)
check_path_syntax (${subcommand} unexpected-arg "-DPATH_ARGUMENTS=${subcommand},ARG1,ARG2")
endforeach()
foreach (subcommand IN ITEMS GET_EXTENSION GET_STEM REMOVE_EXTENSION)
if (subcommand STREQUAL "REMOVE_EXTENSION")
set(RunCMake-stderr-file "unexpected-arg2-stderr.txt")
endif()
check_path_syntax ("${subcommand}[LAST_ONLY]" unexpected-arg "-DPATH_ARGUMENTS=${subcommand},LAST_ONLY,ARG1,ARG2")
unset(RunCMake-stderr-file)
endforeach()
foreach (subcommand IN ITEMS CMAKE_PATH)
check_path_syntax ("${subcommand}[NORMALIZE]" unexpected-arg "-DPATH_ARGUMENTS=${subcommand},NORMALIZE,ARG1,ARG2")
endforeach()
### sub-commands with two arguments
foreach (subcommand IN ITEMS IS_PREFIX REPLACE_FILENAME REPLACE_EXTENSION RELATIVE_PATH ABSOLUTE_PATH)
check_path_syntax (${subcommand} unexpected-arg "-DPATH_ARGUMENTS=${subcommand},ARG1,ARG2,ARG3")
endforeach()
foreach (subcommand IN ITEMS IS_PREFIX ABSOLUTE_PATH)
check_path_syntax ("${subcommand}[NORMALIZE]" unexpected-arg "-DPATH_ARGUMENTS=${subcommand},NORMALIZE,ARG1,ARG2,ARG3")
endforeach()
foreach (subcommand IN ITEMS REPLACE_EXTENSION)
set(RunCMake-stderr-file "unexpected-arg2-stderr.txt")
check_path_syntax ("${subcommand}[LAST_ONLY]" unexpected-arg "-DPATH_ARGUMENTS=${subcommand},LAST_ONLY,ARG1,ARG2,ARG3")
unset(RunCMake-stderr-file)
endforeach()
unset (RunCMake-stderr-file)
function(check_path_execution name)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake_TEST_VARIANT_DESCRIPTION " - ${name}")
run_cmake_with_options(generate -DPATH_TEST=${name})
run_cmake_command(check "${CMAKE_COMMAND}" "-DRunCMake_SOURCE_DIR=${RunCMake_SOURCE_DIR}" -P "${RunCMake_TEST_BINARY_DIR}/${name}.cmake")
endfunction()
check_path_execution (GET_ITEM)
check_path_execution (HAS_ITEM)
check_path_execution (CMAKE_PATH)
check_path_execution (APPEND)
check_path_execution (REMOVE_ITEM)
check_path_execution (REPLACE_ITEM)
check_path_execution (NORMAL_PATH)
check_path_execution (RELATIVE_PATH)
check_path_execution (ABSOLUTE_PATH)
check_path_execution (IS_RELATIVE)
check_path_execution (IS_ABSOLUTE)
check_path_execution (IS_PREFIX)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at bad-option.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<PATH:BAD_OPTION,ARG>
BAD_OPTION: invalid option.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<PATH:BAD_OPTION,ARG>")

View File

@@ -0,0 +1,13 @@
function (CHECK_ERRORS command)
set (errors ${ARGN})
set (command "$<${command}>")
if (errors)
string (LENGTH "${command}" length)
math (EXPR count "${length} + 2")
string (REPEAT " " ${count} shift)
list (TRANSFORM errors PREPEND "${shift}")
list (JOIN errors "\n" msg)
message (FATAL_ERROR "${command}: ${msg}")
endif()
endfunction()

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT "${PATH_TEST}.cmake" INPUT "${PATH_TEST}.cmake.in")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at no-arguments.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<PATH:>
\$<PATH> expression requires at least two parameters.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<PATH:>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at unexpected-arg.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<PATH:[A-Z_]+,.+>
\$<PATH:[A-Z_]+(,[A-Z_]+)?> expression requires exactly (one|two) parameters?.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
file(GENERATE OUTPUT result.txt CONTENT "$<PATH:${PATH_ARGUMENTS}>")

View File

@@ -0,0 +1,9 @@
CMake Error at unexpected-arg.cmake:[0-9]+ \(file\):
Error evaluating generator expression:
\$<PATH:[A-Z_]+,.+>
\$<PATH:[A-Z_]+(,[A-Z_]+)?> expression requires exactly (one|two)
parameters?.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)